React Navigation源代码阅读 : views/ScenesReducer.js

import invariant from '../utils/invariant';
import shallowEqual from '../utils/shallowEqual';

const SCENE_KEY_PREFIX = 'scene_';

/**
 * Helper function to compare route keys (e.g. "9", "11").
 * 工具助手函数,用来比较两个路由的key
 * @param one
 * @param two
 * @returns {number} one 比 two 大时返回 1,其他情况返回 -1
 */
function compareKey(one, two) {
    const delta = one.length - two.length;
    if (delta > 0) {
        return 1;
    }
    if (delta < 0) {
        return -1;
    }
    return one > two ? 1 : -1;
}

/**
 *  * Helper function to sort scenes based on their index and view key.
 * 工具助手函数,比较两个场景,根据他们的索引 index 和 视图 key, 优先考虑 index,
 * 1. 如果 one 的 index 比 two 的大,返回 1, 如果 one 的 index 比 two 的小,返回 -1;
 * 2. 如果 one 和 two 的 index 相等, 使用它们的 key 和函数 compareKey 比较两个场景;
 * @param one
 * @param two
 * @returns {number}
 */
function compareScenes(one, two) {
    if (one.index > two.index) {
        return 1;
    }
    if (one.index < two.index) {
        return -1;
    }

    return compareKey(one.key, two.key);
}

/**
 * Whether two routes are the same.
 * 指出两个路由场景参数是否是指向同一个路由场景对象
 * 两个参数会被认为指向同一个路由场景对象的情况 :
 * 1. key 相等,
 * 2. 并且 index 相等,
 * 3. 并且属性 isStale 相等,
 * 4. 并且属性 isActive 相等,
 * 5. 并且通过路由浅比较函数 areRoutesShallowEqual 比较两个场景的路由的结论也是相等
 * @param one
 * @param two
 * @returns {boolean|*|boolean}
 */
function areScenesShallowEqual(one, two) {
    return (
        one.key === two.key &&
        one.index === two.index &&
        one.isStale === two.isStale &&
        one.isActive === two.isActive &&
        areRoutesShallowEqual(one.route, two.route)
    );
}

/**
 * Whether two routes are the same.
 * 指出两个路由参数是否指向同一个路由对象
 * 两个参数会被认为指向同一个路由对象的情况 :
 * 1. 两个参数对象某个是 undefined/null/0 时,直接 === 比较返回相等则被认为是相等;
 * 2. 否则如果两个参数对象的 key !== 比较返回不同则认为是不相等;
 * 3. 使用浅比较方法 shallowEqual 比较两个参数对象,如果返回相等则认为两个参数路由对象相等;

 * @param one
 * @param two
 * @returns {boolean}
 */
function areRoutesShallowEqual(one, two) {
    if (!one || !two) {
        return one === two;
    }

    if (one.key !== two.key) {
        return false;
    }

    return shallowEqual(one, two);
}

/**
 * scenes reducer 函数,根据之前的状态对象 prevState 和 下一个状态对象 nextState,
 * 对一组 scenes 进行归纳削减,
 * @param scenes
 * @param nextState
 * @param prevState
 * @returns {*}
 * @constructor
 */
export default function ScenesReducer(scenes, nextState, prevState) {
    if (prevState === nextState) {
        // 如果前后状态没有变化,说明对 scenes 来讲什么都不需要做,直接返回
        return scenes;
    }

    // 以下三个Map 的 key 都是 scene 的 key, value 是 scene 对象
    const prevScenes = new Map();
    const freshScenes = new Map();// 用于保存 nextState 决定的 scenes
    const staleScenes = new Map();

    // 对 scenes 数组中所有的 scene 对象按照它们的 isStale 属性进行分类:
    // 1. isStale 为 true , 分类到 staleScenes
    // 2. isStale 为 false, 分类到 prevScenes
    // Populate stale scenes from previous scenes marked as stale.
    scenes.forEach(scene => {
        const {key} = scene;
        if (scene.isStale) {
            // 如果某个 scene.isStale 为真,说明这是一个过期不用的 scene,
            // 将它放入 map staleScenes,
            staleScenes.set(key, scene);
        }
        // 否则将它放入 prevScenes
        prevScenes.set(key, scene);
    });

    const nextKeys = new Set();
    nextState.routes.forEach((route, index) => {
        // 构建新的场景对象的 key : route.key 加上前缀 scene_
        // 注意 : 这一点说明 route 和 scene 二者的 key 是不同的,但有可派生计算关系
        const key = SCENE_KEY_PREFIX + route.key;
        // 构建一个新的 scene 对象
        const scene = {
            index,
            isActive: false,
            isStale: false,
            key,
            route,
        };
        invariant(
            !nextKeys.has(key),
            `navigation.state.routes[${index}].key "${key}" conflicts with ` +
            'another route!'
        );
        nextKeys.add(key);

        if (staleScenes.has(key)) {
            // 如果某个之前被标记为 stale 的 scene 现在被 nextState 包含,说明它
            // 需要被再次使用,所以需要将它从 staleScenes 中删除
            // A previously `stale` scene is now part of the nextState, so we
            // revive it by removing it from the stale scene map.
            staleScenes.delete(key);
        }

        // 将新构建的 scene 添加到 freshScenes
        freshScenes.set(key, scene);
    });

    if (prevState) {
        // Look at the previous routes and classify any removed scenes as `stale`.
        // 如果 prevState 中的某个 scene 不再被 nextState 决定的 freshScenes 包含,
        // 将它们放到 staleScenes 中去
        prevState.routes.forEach((route, index) => {
            const key = SCENE_KEY_PREFIX + route.key;
            if (freshScenes.has(key)) {
                return;
            }
            staleScenes.set(key, {
                index,
                isActive: false,
                isStale: true,
                key,
                route,
            });
        });
    }

    const nextScenes = [];

    const mergeScene = nextScene => {
        const {key} = nextScene;
        const prevScene = prevScenes.has(key) ? prevScenes.get(key) : null;
        if (prevScene && areScenesShallowEqual(prevScene, nextScene)) {
            // Reuse `prevScene` as `scene` so view can avoid unnecessary re-render.
            // This assumes that the scene's navigation state is immutable.
            // 如果待合并的 scene nextScene 跟之前某个 scene prevScene 是同一个,
            // 则重用该 scene 对象
            nextScenes.push(prevScene);
        } else {
            // 如果待合并的 scene nextScene 是一个新的 scene, 则直接使用该 scene 对象
            nextScenes.push(nextScene);
        }
    };

    // 合并每个 stale scene 到 nextScenes
    staleScenes.forEach(mergeScene);
    // 合并每个 fresh scene 到 nextScenes
    // (如果 prevScenes 已经包含了待合并的某个  fresh scene  ,则会重用)
    freshScenes.forEach(mergeScene);

    // 排序
    nextScenes.sort(compareScenes);

    // 统计活跃 scene 的数量,恒等式 : 应该总是为 1
    let activeScenesCount = 0;
    nextScenes.forEach((scene, ii) => {
        // 计算某个 scene 是否处于当前活跃状态(也就是栈顶scene,用户直接看到的那个scene)
        const isActive = !scene.isStale && scene.index === nextState.index;
        if (isActive !== scene.isActive) {
            nextScenes[ii] = {
                ...scene,
                isActive,
            };
        }
        if (isActive) {
            activeScenesCount++;
        }
    });

    invariant(
        activeScenesCount === 1,
        'there should always be only one scene active, not %s.',
        activeScenesCount
    );

    if (nextScenes.length !== scenes.length) {
        // 如果新的场景数组 nextScenes 和原来的场景数组 scenes 长度不同,
        // 说明根据前后状态变化导致了场景数组的变化,所以返回新的 场景数组
        // nextScenes
        return nextScenes;
    }

    if (
        nextScenes.some(
            (scene, index) => !areScenesShallowEqual(scenes[index], scene)
        )
    ) {
        // 如果新的场景数组 nextScenes 和原来的场景数组 scenes 长度相同,
        // 但是 scene 元素顺序发生了变化,也说明根据前后状态变化导致了场景
        // 数组的变化,所以返回新的 场景数组 nextScenes
        return nextScenes;
    }

    // 如果经过一系列计算逻辑能走到这里,说明 scenes 没有变化,直接返回它
    // scenes haven't changed.
    return scenes;
}

注意区分 scene 和 route 的概念,以及它们的关系,代码例子 :

  const scene = {
            index,
            isActive: false,
            isStale: false,
            key,
            route,
        };

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/80467484
今日推荐