React Navigation源代码阅读 :routers/TabRouter.js

import invariant from '../utils/invariant';
import getScreenForRouteName from './getScreenForRouteName';
import createConfigGetter from './createConfigGetter';

import NavigationActions from '../NavigationActions';
import validateRouteConfigMap from './validateRouteConfigMap';
import getScreenConfigDeprecated from './getScreenConfigDeprecated';

function childrenUpdateWithoutSwitchingIndex(actionType) {
    return [
        NavigationActions.SET_PARAMS,
        NavigationActions.COMPLETE_TRANSITION,
    ].includes(actionType);
}

/**
 * 定义一个选项卡路由器
 * @param routeConfigs 路由配置信息
 * @param config  路由器/导航器设置
 * @returns {*}
 */
export default (routeConfigs, config = {}) => {
    // Fail fast on invalid route definitions
    // 路由配置信息格式检查,如果格式不合格,直接抛出错误不再继续
    validateRouteConfigMap(routeConfigs);

    // 确定路由配置中各个路由的顺序, order 是一个以路由的路由名称为元素的集合,
    // 要么通过 config.order 来指定,要么使用路由配置中缺省的定义顺序
    const order = config.order || Object.keys(routeConfigs);

    //确定各个路由的 path,所有路由的 path 信息由 config.paths 指定
    const paths = config.paths || {};

    // 获取缺省路由参数
    const initialRouteParams = config.initialRouteParams;

    // 获取缺省路由名称 : 或者是 config.initialRouteName 明确指定,或者使用
    // 路由顺序定义集合中排在首位的那个路由作为缺省路由名称
    const initialRouteName = config.initialRouteName || order[0];

    // 获取缺省路由的索引
    const initialRouteIndex = order.indexOf(initialRouteName);

    // 确定返回行为,要么使用 config.backBehavior 指定的返回目标,要么使用
    // 缺省的返回目标 `initialRoute` (表示返回 initialRouteName 定义的缺省路由)
    const backBehavior = config.backBehavior || 'initialRoute';
    const shouldBackNavigateToInitialRoute = backBehavior === 'initialRoute';

    // 获取每个子路由的路由器信息:
    // 1.如果某个子路由是一个嵌套路由器,则对应的 tabRouters 元素设置为相应的 路由器信息
    // 2.如果某个子路由是一个普通组件,则对应的 tabRouters 元素设置为 null
    const tabRouters = {};
    order.forEach(routeName => {
        const routeConfig = routeConfigs[routeName];
        paths[routeName] =
            typeof routeConfig.path === 'string' ? routeConfig.path : routeName;
        tabRouters[routeName] = null;
        const screen = getScreenForRouteName(routeConfigs, routeName);
        if (screen.router) {
            tabRouters[routeName] = screen.router;
        }
    });
    if (initialRouteIndex === -1) {
        // 遇到了无效的初始路由设置,抛出错误
        throw new Error(
            `Invalid initialRouteName '${initialRouteName}' for TabRouter. ` +
            `Should be one of ${order.map(n => `"${n}"`).join(', ')}`
        );
    }

    // 定义和返回 TabRouter 对象
    return {
        /**
         * 计算在某个导航状态 inputState 上应用指令 action 得到的目标导航状态
         * @param action 将要执行的 action
         * @param inputState action 执行前的状态,action 将要应用到的导航状态
         * @returns {*}
         */
        getStateForAction(action, inputState) {
            // Establish a default state
            let state = inputState;
            if (!state) {
                // 如果没有通过 inputState 指定导航状态,则这里构造一个初始导航状态
                // 对每个选项卡(Tab)对应的子路由初初始化其路由状态,每个选项卡的状态
                // 放在一起构成了整个选项卡路由器的初始导航状态,类似这样子:
                // [{选项卡0子路由的导航状态},{选项卡1子路由的导航状态},...,选项卡n子路由的导航状态]
                const routes = order.map(routeName => {
                    // 获取当前子路由的参数
                    const params =
                        routeName === initialRouteName ? initialRouteParams : undefined;
                    // 获取当前子路由的路由器信息
                    const tabRouter = tabRouters[routeName];
                    if (tabRouter) {
                        // 当前子路由是一个嵌套路由器,行初始化它的状态并返回
                        const childAction = NavigationActions.init();
                        return {
                            ...tabRouter.getStateForAction(childAction),
                            key: routeName, // 注意:这里 key 使用了路由名称
                            routeName, // 注意:这里 routeName 还是使用了路由名称
                            params,
                        };
                    }

                    // 当前子路由是一个普通屏幕组件,也对它的导航状态做初始化并返回
                    return {
                        key: routeName, // 注意:这里 key 使用了路由名称
                        routeName, // 注意:这里 routeName 还是使用了路由名称
                        params,
                    };
                });

                // 基于各个子路由的导航状态构建整个选项卡路由器的导航状态
                state = {
                    routes,
                    index: initialRouteIndex,
                    isTransitioning: false,
                };
                // console.log(`${order.join('-')}: Initial state`, {state});
            }

            // 上面根据参数 inputState 决定应用 action 的起始状态对象 state,以上逻辑上可以总结为 :
            // 1. 如果指定了 inputState,使用它作为将要应用 action 的导航状态对象 state
            // 2. 如果没有指定 inputState,使用初始导航状态对象作为将要应用 action 的导航状态对象 state

            if (action.type === NavigationActions.INIT) {
                // 这是一个对选项卡路由器进行初始化的 action, 此时 inputState 应该未指定,
                // 而 state 现在是一个初始状态, 现在如果 action.params 有信息的话, 把它
                // 合并到每个子路由的路由参数 params 中去
                // Merge any params from the action into all the child routes
                const {params} = action;
                if (params) {
                    state.routes = state.routes.map(route => ({
                        ...route,
                        params: {
                            ...route.params,
                            ...params,
                            ...(route.routeName === initialRouteName
                                ? initialRouteParams
                                : null),
                        },
                    }));
                }
            }

            // Let the current tab handle it
            // 先尝试看看当前选项卡子路由能否处理此 action , 如果当前选项卡子路由能否处理此 action (当前选项卡子路由
            // 是一个嵌套子路由器并且负责该 action 的处理),此函数的任务在下面的if语句块中就完成返回了。
            const activeTabLastState = state.routes[state.index];
            const activeTabRouter = tabRouters[order[state.index]];
            if (activeTabRouter) {
                const activeTabState = activeTabRouter.getStateForAction(
                    action,
                    activeTabLastState
                );
                if (!activeTabState && inputState) {
                    // 当前选项卡子路由能处理此 action 并且处理前后状态无变化
                    return null;
                }
                if (activeTabState && activeTabState !== activeTabLastState) {
                    // 当前选项卡子路由能处理此 action 并且处理前后状态有变化
                    const routes = [...state.routes];
                    routes[state.index] = activeTabState;
                    return {
                        ...state,
                        routes,
                    };
                }
            }

            // 下面是要做Tab切换了。Tab切换逻辑放在上面的当前子路由处理action尝试之后,目的是为了让内部
            // Tab子路由先有机会发生变化。
            // Handle tab changing. Do this after letting the current tab try to
            // handle the action, to allow inner tabs to change first
            let activeTabIndex = state.index;

            // 先对是否要执行 BACK action 进行处理
            // 是否符合执行BACK action的条件
            const isBackEligible =
                action.key == null || action.key === activeTabLastState.key;
            if (action.type === NavigationActions.BACK) {
                // action 是一个 BACK action
                if (isBackEligible && shouldBackNavigateToInitialRoute) {
                    // action 是一个 BACK action 并且需要返回到初始路由
                    activeTabIndex = initialRouteIndex;
                } else {
                    // 如果 action 是一个 BACK action 但是不符合返回执行条件或者
                    // 不需要返回初始路由,那就什么都不做,返回原导航状态对象
                    return state;
                }
            }

            // 对 NAVIGATE action 的处理
            let didNavigate = false;// 用于记录导航动作是否真的会发生
            if (action.type === NavigationActions.NAVIGATE) {
                const navigateAction = action;
                didNavigate = !!order.find((tabId, i) => {
                    if (tabId === navigateAction.routeName) {
                        activeTabIndex = i;
                        return true;
                    }
                    return false;
                });
                if (didNavigate) {
                    const childState = state.routes[activeTabIndex];
                    let newChildState;

                    const tabRouter = tabRouters[action.routeName];

                    if (action.action) {
                        newChildState = tabRouter
                            ? tabRouter.getStateForAction(action.action, childState)
                            : null;
                    } else if (!tabRouter && action.params) {
                        newChildState = {
                            ...childState,
                            params: {
                                ...(childState.params || {}),
                                ...action.params,
                            },
                        };
                    }

                    if (newChildState && newChildState !== childState) {
                        const routes = [...state.routes];
                        routes[activeTabIndex] = newChildState;
                        return {
                            ...state,
                            routes,
                            index: activeTabIndex,
                        };
                    }
                }
            }

            // 对 SET_PARAMS action 的处理
            if (action.type === NavigationActions.SET_PARAMS) {
                const key = action.key;
                const lastRoute = state.routes.find(route => route.key === key);
                if (lastRoute) {
                    const params = {
                        ...lastRoute.params,
                        ...action.params,
                    };
                    const routes = [...state.routes];
                    routes[state.routes.indexOf(lastRoute)] = {
                        ...lastRoute,
                        params,
                    };
                    return {
                        ...state,
                        routes,
                    };
                }
            }
            if (activeTabIndex !== state.index) {
                return {
                    ...state,
                    index: activeTabIndex,
                };
            } else if (didNavigate && !inputState) {
                return state;
            } else if (didNavigate) {
                return null;
            }

            // Let other tabs handle it and switch to the first tab that returns a new state
            let index = state.index;
            let routes = state.routes;
            order.find((tabId, i) => {
                const tabRouter = tabRouters[tabId];
                if (i === index) {
                    return false;
                }
                let tabState = routes[i];
                if (tabRouter) {
                    // console.log(`${order.join('-')}: Processing child router:`, {action, tabState});
                    tabState = tabRouter.getStateForAction(action, tabState);
                }
                if (!tabState) {
                    index = i;
                    return true;
                }
                if (tabState !== routes[i]) {
                    routes = [...routes];
                    routes[i] = tabState;
                    index = i;
                    return true;
                }
                return false;
            });
            // console.log(`${order.join('-')}: Processed other tabs:`, {lastIndex: state.index, index});

            // Nested routers can be updated after switching tabs with actions such as SET_PARAMS
            // and COMPLETE_TRANSITION.
            // NOTE: This may be problematic with custom routers because we whitelist the actions
            // that can be handled by child routers without automatically changing index.
            if (childrenUpdateWithoutSwitchingIndex(action.type)) {
                index = state.index;
            }

            if (index !== state.index || routes !== state.routes) {
                return {
                    ...state,
                    index,
                    routes,
                };
            }
            return state;
        },

        // 获取当前活跃选项卡子路由屏幕组件,或者如果当前活跃选项卡子路由是一个嵌套路由器,递归查找
        // 其当前活跃屏幕组件
        getComponentForState(state) {
            const routeName = state.routes[state.index].routeName;
            invariant(
                routeName,
                `There is no route defined for index ${state.index}. Check that
        that you passed in a navigation state with a valid tab/screen index.`
            );
            const childRouter = tabRouters[routeName];
            if (childRouter) {
                return childRouter.getComponentForState(state.routes[state.index]);
            }
            return getScreenForRouteName(routeConfigs, routeName);
        },

        /**
         * 获取选项卡子路由的 screen 属性的值(如果子路由是嵌套路由器,不递归查找)
         * @param routeName
         * @returns {*}
         */
        getComponentForRouteName(routeName) {
            return getScreenForRouteName(routeConfigs, routeName);
        },

        /**
         * 获取当前导航状态对象的路径和参数信息
         * @param state
         * @returns {{path: *, params: *}}
         */
        getPathAndParamsForState(state) {
            const route = state.routes[state.index];
            const routeName = order[state.index];
            const subPath = paths[routeName];
            const screen = getScreenForRouteName(routeConfigs, routeName);
            let path = subPath;
            let params = route.params;
            if (screen && screen.router) {
                const stateRoute = route;
                // If it has a router it's a navigator.
                // If it doesn't have router it's an ordinary React component.
                const child = screen.router.getPathAndParamsForState(stateRoute);
                path = subPath ? `${subPath}/${child.path}` : child.path;
                params = child.params ? {...params, ...child.params} : params;
            }
            return {
                path,
                params,
            };
        },

        /**
         * Gets an optional action, based on a relative path and query params.
         *
         * This will return null if there is no action matched
         */
        getActionForPathAndParams(path, params) {
            return (
                order
                    .map(tabId => {
                        const parts = path.split('/');
                        const pathToTest = paths[tabId];
                        if (parts[0] === pathToTest) {
                            const tabRouter = tabRouters[tabId];
                            const action = NavigationActions.navigate({
                                routeName: tabId,
                            });
                            if (tabRouter && tabRouter.getActionForPathAndParams) {
                                action.action = tabRouter.getActionForPathAndParams(
                                    parts.slice(1).join('/'),
                                    params
                                );
                            } else if (params) {
                                action.params = params;
                            }
                            return action;
                        }
                        return null;
                    })
                    .find(action => !!action) ||
                order
                    .map(tabId => {
                        const tabRouter = tabRouters[tabId];
                        return (
                            tabRouter && tabRouter.getActionForPathAndParams(path, params)
                        );
                    })
                    .find(action => !!action) ||
                null
            );
        },

        getScreenOptions: createConfigGetter(
            routeConfigs,
            config.navigationOptions
        ),

        getScreenConfig: getScreenConfigDeprecated,
    };
};

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/80316916