这次的版本是 6.2.1

应用

相比拟 5.x 版本, <Switch>元素降级为了<Routes>

简略的 v6 例子:

function App(){    return  <BrowserRouter>        <Routes>            <Route path="/about" element={<About/>}/>            <Route path="/users" element={<Users/>}/>            <Route path="/" element={<Home/>}/>        </Routes>    </BrowserRouter>}

context

在 react-router 中, 他创立了两个 context 供后续的应用, 当然这两个 context 是在外部的, 并没有 API 裸露进去

NavigationContext

/** * 一个路由对象的根本形成 */export interface RouteObject {    caseSensitive?: boolean;    children?: RouteObject[];    element?: React.ReactNode;    index?: boolean;    path?: string;}// 罕用的参数类型export type Params<Key extends string = string> = {    readonly [key in Key]: string | undefined;};/** * 一个 路由匹配 接口 */export interface RouteMatch<ParamKey extends string = string> {    /**     * 动静参数的名称和值的URL     */    params: Params<ParamKey>;    /**     * 路径名     */    pathname: string;    /**     * 之前匹配的路径名     */    pathnameBase: string;    /**     * 匹配到的路由对象     */    route: RouteObject;}interface RouteContextObject {    outlet: React.ReactElement | null;    matches: RouteMatch[];}const RouteContext = React.createContext<RouteContextObject>({    outlet: null,    matches: []});

LocationContext

import type {    Location,    Action as NavigationType} from "history";interface LocationContextObject {    location: Location; // 原生的 location 对象, window.location    /**     * enum Action 一个枚举, 他有三个参数, 代表路由三种动作     * Pop = "POP",     * Push = "PUSH",     * Replace = "REPLACE"     */    navigationType: NavigationType;  }const LocationContext = React.createContext<LocationContextObject>(null!);

MemoryRouter

react-router-dom 的源码解析中咱们说到了 BrowserRouterHashRouter, 那么这个 MemoryRouter又是什么呢

他是将 URL 的历史记录保留在内存中的 <Router>(不读取或写入地址栏)。在测试和非浏览器环境中很有用,例如 React Native。

他的源码和其余两个 Router 最大的区别就是一个 createMemoryHistory 办法, 此办法也来自于 history 库中

export function MemoryRouter({                                 basename,                                 children,                                 initialEntries,                                 initialIndex                             }: MemoryRouterProps): React.ReactElement {    let historyRef = React.useRef<MemoryHistory>();    if (historyRef.current == null) {        historyRef.current = createMemoryHistory({ initialEntries, initialIndex });    }    let history = historyRef.current;    let [state, setState] = React.useState({        action: history.action,        location: history.location    });    React.useLayoutEffect(() => history.listen(setState), [history]);    return (        <Router            basename={basename}            children={children}            location={state.location}            navigationType={state.action}            navigator={history}        />    );}

那咱们当初来看一看这个办法, 这里只讲他与 createHashHistory 不同的中央:

export function createMemoryHistory(  options: MemoryHistoryOptions = {}): MemoryHistory {  let { initialEntries = ['/'], initialIndex } = options; // 不同的初始值 initialEntries  let entries: Location[] = initialEntries.map((entry) => {    let location = readOnly<Location>({      pathname: '/',      search: '',      hash: '',      state: null,      key: createKey(), // 通过 random 生成惟一值      ...(typeof entry === 'string' ? parsePath(entry) : entry)    }); // 这里的 location 属于是间接创立, HashHistory 中是应用的 window.location      // readOnly办法 能够看做 (obj)=>obj, 并没有太大作用    return location;  });   function push(to: To, state?: any) {    let nextAction = Action.Push;    let nextLocation = getNextLocation(to, state);    function retry() {      push(to, state);    }    // 疏忽其余相似的代码        if (allowTx(nextAction, nextLocation, retry)) {      index += 1;      // 别处是调用原生 API, history.pushState      entries.splice(index, entries.length, nextLocation);      applyTx(nextAction, nextLocation);    }  }    // 与 push 相似, 疏忽 replace  function go(delta: number) {      // 与HashHistory不同, 也是走的相似 push    let nextIndex = clamp(index + delta, 0, entries.length - 1);    let nextAction = Action.Pop;    let nextLocation = entries[nextIndex];    function retry() {      go(delta);    }    if (allowTx(nextAction, nextLocation, retry)) {      index = nextIndex;      applyTx(nextAction, nextLocation);    }  }  let history: MemoryHistory = {    // 基本相同  };  return history;}

Navigate

用来扭转 当然 location 的办法, 是一个 react-router 抛出的 API

应用形式:

function App() {    // 一旦 user 是有值的, 就跳转至 `/dashboard` 页面了    // 算是跳转路由的一种计划    return <div>        {user && (            <Navigate to="/dashboard" replace={true} />        )}        <form onSubmit={event => this.handleSubmit(event)}>            <input type="text" name="username" />            <input type="password" name="password" />        </form>    </div>}

源码

export function Navigate({ to, replace, state }: NavigateProps): null {    // 间接调用 useNavigate 来获取 navigate 办法, 并且  useEffect 每次都会触发    // useNavigate 源码在下方会讲到    let navigate = useNavigate();    React.useEffect(() => {        navigate(to, { replace, state });    });    return null;}

Outlet

用来渲染子路由的元素, 简略来说就是一个路由的占位符

代码很简略, 应用的逻辑是这样

应用形式:

function App(props) {    return (        <HashRouter>            <Routes>                <Route path={'/'} element={<Dashboard></Dashboard>}>                    <Route path="qqwe" element={<About/>}/>                    <Route path="about" element={<About/>}/>                    <Route path="users" element={<Users/>}/>                </Route>            </Routes>        </HashRouter>    );}// 其中外层的Dashboard:function Dashboard() {    return (        <div>            <h1>Dashboard</h1>            <Outlet />            // 这里就会渲染他的子路由了            // 和以前 children 差不多        </div>    );}

源码

export function Outlet(props: OutletProps): React.ReactElement | null {    return useOutlet(props.context);}export function useOutlet(context?: unknown): React.ReactElement | null {    let outlet = React.useContext(RouteContext).outlet;    if (outlet) {        return (            <OutletContext.Provider value={context}>{outlet}</OutletContext.Provider>        );    }    return outlet;}

useParams

从以后URL所匹配的门路中, 返回一个对象的键/值对的动静参数。

function useParams<    ParamsOrKey extends string | Record<string, string | undefined> = string    >(): Readonly<    [ParamsOrKey] extends [string] ? Params<ParamsOrKey> : Partial<ParamsOrKey>    > {    // 间接获取了 RouteContext 中 matches 数组的最初一个对象, 如果没有就是空对象    let { matches } = React.useContext(RouteContext);    let routeMatch = matches[matches.length - 1];    return routeMatch ? (routeMatch.params as any) : {};}

useResolvedPath

将给定的`to'值的路径名与以后地位进行比拟

<NavLink> 这个组件中应用到

function useResolvedPath(to: To): Path {    let { matches } = React.useContext(RouteContext);    let { pathname: locationPathname } = useLocation();        // 合并成一个 json 字符, 至于为什么又要解析, 是为了增加字符层的缓存, 如果是一个对象, 就不好浅比拟了    let routePathnamesJson = JSON.stringify(        matches.map(match => match.pathnameBase)    );        // TODO resolveTo    return React.useMemo(        () => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname),        [to, routePathnamesJson, locationPathname]    );}

useRoutes

useRoutes钩子的性能等同于<Routes>,但它应用JavaScript对象而不是<Route>元素来定义路由。
相当于是一种 schema 版本, 更好的配置性

应用形式:

如果应用过 umi, 是不是会感觉到截然不同

function App() {  let element = useRoutes([    { path: "/", element: <Home /> },    { path: "dashboard", element: <Dashboard /> },    {      path: "invoices",      element: <Invoices />,      children: [        { path: ":id", element: <Invoice /> },        { path: "sent", element: <SentInvoices /> }      ]    },    { path: "*", element: <NotFound /> }  ]);  return element;}

源码

// 具体的 routes 对象是如何生成的, 上面的 Routes-createRoutesFromChildren 会讲到export function useRoutes(    routes: RouteObject[],    locationArg?: Partial<Location> | string): React.ReactElement | null {        let { matches: parentMatches } = React.useContext(RouteContext);    let routeMatch = parentMatches[parentMatches.length - 1];    // 获取匹配的 route        let parentParams = routeMatch ? routeMatch.params : {};    let parentPathname = routeMatch ? routeMatch.pathname : "/";    let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";    let parentRoute = routeMatch && routeMatch.route;    // 这里下面都是一些参数, 没有就是默认值        //  等于 React.useContext(LocationContext).location, 约等于原生的 location    let locationFromContext = useLocation();    let location;    if (locationArg) { // 对于配置项参数的一些判断        let parsedLocationArg =            typeof locationArg === "string" ? parsePath(locationArg) : locationArg;        location = parsedLocationArg;    } else {        location = locationFromContext;    }    // 如果参数里有则应用参数里的, 如果没有应用 context 的        let pathname = location.pathname || "/";    let remainingPathname =        parentPathnameBase === "/"            ? pathname            : pathname.slice(parentPathnameBase.length) || "/";    // matchRoutes 大略的作用是通过pathname遍历寻找,匹配到的路由    具体源码放在上面讲    let matches = matchRoutes(routes, { pathname: remainingPathname });        // 最初调用渲染函数  首先对数据进行 map    // joinPaths  的作用约等于 paths.join("/") 并且去除多余的斜杠    return _renderMatches(        matches &&        matches.map(match =>            Object.assign({}, match, {                params: Object.assign({}, parentParams, match.params),                pathname: joinPaths([parentPathnameBase, match.pathname]),                pathnameBase:                    match.pathnameBase === "/"                        ? parentPathnameBase                        : joinPaths([parentPathnameBase, match.pathnameBase])            })        ),        parentMatches    );}

useRoutes-matchRoutes

function matchRoutes(    routes: RouteObject[],    locationArg: Partial<Location> | string,    basename = "/"): RouteMatch[] | null {    let location =        typeof locationArg === "string" ? parsePath(locationArg) : locationArg;    // 获取排除 basename 的 pathname    let pathname = stripBasename(location.pathname || "/", basename);    if (pathname == null) {        return null;    }    // flattenRoutes 函数的次要作用, 压平 routes, 不便遍历    // 源码见下方    let branches = flattenRoutes(routes);        // 对路由进行排序    // rankRouteBranches 源码见下方    rankRouteBranches(branches);        // 筛选出匹配到的路由 matchRouteBranch源码在上面讲    let matches = null;    for (let i = 0; matches == null && i < branches.length; ++i) {        matches = matchRouteBranch(branches[i], pathname);    }    return matches;}

useRoutes-matchRoutes-stripBasename

拆分 basename, 代码很简略, 这里就间接贴出来了

function stripBasename(pathname: string, basename: string): string | null {    if (basename === "/") return pathname;    if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {        return null;    }    let nextChar = pathname.charAt(basename.length);    if (nextChar && nextChar !== "/") {        return null;    }    return pathname.slice(basename.length) || "/";}

useRoutes-matchRoutes-flattenRoutes

递归解决 routes, 压平 routes

function flattenRoutes(    routes: RouteObject[],    branches: RouteBranch[] = [],    parentsMeta: RouteMeta[] = [],    parentPath = ""): RouteBranch[] {    routes.forEach((route, index) => {        let meta: RouteMeta = {            relativePath: route.path || "",            caseSensitive: route.caseSensitive === true,            childrenIndex: index,            route        };        if (meta.relativePath.startsWith("/")) {            meta.relativePath = meta.relativePath.slice(parentPath.length);        }                // joinPaths 源码: (paths)=>paths.join("/").replace(/\/\/+/g, "/")        // 把数组转成字符串, 并且革除反复斜杠        let path = joinPaths([parentPath, meta.relativePath]);        let routesMeta = parentsMeta.concat(meta);        // 如果有子路由则递归        if (route.children && route.children.length > 0) {            flattenRoutes(route.children, branches, routesMeta, path);        }        // 匹配不到就 return        if (route.path == null && !route.index) {            return;        }        // 压平后组件增加的对象, TODO computeScore        branches.push({ path, score: computeScore(path, route.index), routesMeta });    });    return branches;}

useRoutes-matchRoutes-rankRouteBranches

对路由进行排序, 这里能够略过,不论排序算法如何, 只须要晓得, 晓得输出的值是通过一系列排序的就行

function rankRouteBranches(branches: RouteBranch[]): void {    branches.sort((a, b) =>        a.score !== b.score            ? b.score - a.score // Higher score first            : compareIndexes(                a.routesMeta.map(meta => meta.childrenIndex),                b.routesMeta.map(meta => meta.childrenIndex)            )    );}

useRoutes-matchRoutes-matchRouteBranch

匹配函数, 承受参数 branch 就是某一个 rankRouteBranches

function matchRouteBranch<ParamKey extends string = string>(    branch: RouteBranch,    pathname: string): RouteMatch<ParamKey>[] | null {    let { routesMeta } = branch;    let matchedParams = {};    let matchedPathname = "/";    let matches: RouteMatch[] = [];        //  routesMeta 具体起源能够查看 下面的flattenRoutes    for (let i = 0; i < routesMeta.length; ++i) {        let meta = routesMeta[i];        let end = i === routesMeta.length - 1;        let remainingPathname =            matchedPathname === "/"                ? pathname                : pathname.slice(matchedPathname.length) || "/";                // 比拟, matchPath 源码在下方        let match = matchPath(            { path: meta.relativePath, caseSensitive: meta.caseSensitive, end },            remainingPathname        );        // 如果返回是空 则间接返回        if (!match) return null;        // 更换对象源        Object.assign(matchedParams, match.params);        let route = meta.route;                // push 到最终后果上, joinPaths 不再赘述        matches.push({            params: matchedParams,            pathname: joinPaths([matchedPathname, match.pathname]),            pathnameBase: joinPaths([matchedPathname, match.pathnameBase]),            route        });        if (match.pathnameBase !== "/") {            matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);        }    }    return matches;}

useRoutes-matchRoutes-matchRouteBranch-matchPath

对一个URL路径名进行模式匹配,并返回无关匹配的信息。
他也是一个保留在外的可用 API

export function matchPath<    ParamKey extends ParamParseKey<Path>,    Path extends string    >(    pattern: PathPattern<Path> | Path,    pathname: string): PathMatch<ParamKey> | null {    // pattern 的从新赋值    if (typeof pattern === "string") {        pattern = { path: pattern, caseSensitive: false, end: true };    }    // 通过正则匹配返回匹配到的正则表达式   matcher 为 RegExp    let [matcher, paramNames] = compilePath(        pattern.path,        pattern.caseSensitive,        pattern.end    );    // 正则对象的 match 办法    let match = pathname.match(matcher);    if (!match) return null;    // 取 match 到的值    let matchedPathname = match[0];    let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");    let captureGroups = match.slice(1);        // params 转成对象  { param:value, ... }    let params: Params = paramNames.reduce<Mutable<Params>>(        (memo, paramName, index) => {            // 如果是*号  转换            if (paramName === "*") {                let splatValue = captureGroups[index] || "";                pathnameBase = matchedPathname                    .slice(0, matchedPathname.length - splatValue.length)                    .replace(/(.)\/+$/, "$1");            }            // safelyDecodeURIComponent  等于 decodeURIComponent + try_catch            memo[paramName] = safelyDecodeURIComponent(                captureGroups[index] || "",                paramName            );            return memo;        },        {}    );    return {        params,        pathname: matchedPathname,        pathnameBase,        pattern    };}

useRoutes-matchRoutes-matchRouteBranch-matchPath-compilePath

function compilePath(    path: string,    caseSensitive = false,    end = true): [RegExp, string[]] {    let paramNames: string[] = [];    // 正则匹配替换    let regexpSource =        "^" +        path            // 疏忽尾随的 / 和 /*            .replace(/\/*\*?$/, "")            // 确保以 / 结尾            .replace(/^\/*/, "/")             // 本义特殊字符            .replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars            .replace(/:(\w+)/g, (_: string, paramName: string) => {                paramNames.push(paramName);                return "([^\\/]+)";            });    // 对于*号的特地判断    if (path.endsWith("*")) {        paramNames.push("*");        regexpSource +=            path === "*" || path === "/*"                ? "(.*)$" // Already matched the initial /, just match the rest                : "(?:\\/(.+)|\\/*)$"; // Don't include the / in params["*"]    } else {        regexpSource += end            ? "\\/*$" // 匹配到开端时,疏忽尾部斜杠            :             "(?:\\b|\\/|$)";    }    let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i");        // 返回匹配后果    return [matcher, paramNames];}

useRoutes-_renderMatches

渲染匹配到的路由

function _renderMatches(    matches: RouteMatch[] | null,    parentMatches: RouteMatch[] = []): React.ReactElement | null {        if (matches == null) return null;        // 通过 context 传递数据    return matches.reduceRight((outlet, match, index) => {        return (            <RouteContext.Provider                children={                    match.route.element !== undefined ? match.route.element : <Outlet />                }                value={{                    outlet,                    matches: parentMatches.concat(matches.slice(0, index + 1))                }}            />        );    }, null as React.ReactElement | null);}

Router

为应用程序的其余局部提供context信息

通常不会应用此组件, 他是 MemoryRouter 最终渲染的组件

在 react-router-dom 库中, 也是 BrowserRouter 和 HashRouter 的最终渲染组件

export function Router({                           basename: basenameProp = "/",                           children = null,                           location: locationProp,                           navigationType = NavigationType.Pop,                           navigator,                           static: staticProp = false                       }: RouterProps): React.ReactElement | null {    // 格式化 baseName     let basename = normalizePathname(basenameProp);        // memo context value    let navigationContext = React.useMemo(        () => ({ basename, navigator, static: staticProp }),        [basename, navigator, staticProp]    );    // 如果是字符串则解析  依据 #, ? 特殊符号解析 url    if (typeof locationProp === "string") {        locationProp = parsePath(locationProp);    }    let {        pathname = "/",        search = "",        hash = "",        state = null,        key = "default"    } = locationProp;    // 同样的缓存    let location = React.useMemo(() => {        // 这还办法在 useRoutes-matchRoutes-stripBasename 讲过这里就不多说        let trailingPathname = stripBasename(pathname, basename);        if (trailingPathname == null) {            return null;        }        return {            pathname: trailingPathname,            search,            hash,            state,            key        };    }, [basename, pathname, search, hash, state, key]);    // 空值判断    if (location == null) {        return null;    }    // 提供 context 的 provider, 传递 children    return (        <NavigationContext.Provider value={navigationContext}>            <LocationContext.Provider                children={children}                value={{ location, navigationType }}            />        </NavigationContext.Provider>    );}

parsePath

此源码来自于 history 仓库

function parsePath(path: string): Partial<Path> {  let parsedPath: Partial<Path> = {};  // 首先确定 path  if (path) {      // 是否有#号 , 如果有则截取    let hashIndex = path.indexOf('#');    if (hashIndex >= 0) {      parsedPath.hash = path.substr(hashIndex);      path = path.substr(0, hashIndex);    }    // 再判断 ? , 有也截取    let searchIndex = path.indexOf('?');    if (searchIndex >= 0) {      parsedPath.search = path.substr(searchIndex);      path = path.substr(0, searchIndex);    }    // 最初就是 path    if (path) {      parsedPath.pathname = path;    }  }// 返回后果  return parsedPath;}

Routes

用来包裹 route 的元素, 次要是通过 useRoutes 的逻辑

 function Routes({                           children,                           location                       }: RoutesProps): React.ReactElement | null {    return useRoutes(createRoutesFromChildren(children), location);}

Routes-createRoutesFromChildren

接管到的参数个别都是 Route children, 可能是多层嵌套的, 最初得的咱们定义的 route 组件构造,
它将被传递给 useRoutes 函数

function createRoutesFromChildren(    children: React.ReactNode): RouteObject[] {    let routes: RouteObject[] = [];    // 应用官网函数循环    React.Children.forEach(children, element => {        if (element.type === React.Fragment) {            // 如果是 React.Fragment 组件 则间接push 递归函数            routes.push.apply(                routes,                createRoutesFromChildren(element.props.children)            );            return;        }                let route: RouteObject = {            caseSensitive: element.props.caseSensitive,            element: element.props.element,            index: element.props.index,            path: element.props.path        }; // route 对象具备的属性                // 同样地递归        if (element.props.children) {            route.children = createRoutesFromChildren(element.props.children);        }        routes.push(route);    });    return routes;}

useHref

返回残缺的链接

export function useHref(to: To): string {    let { basename, navigator } = React.useContext(NavigationContext);    // useResolvedPath 在下面讲过    let { hash, pathname, search } = useResolvedPath(to);    let joinedPathname = pathname;    if (basename !== "/") {        let toPathname = getToPathname(to);        let endsWithSlash = toPathname != null && toPathname.endsWith("/");        joinedPathname =            pathname === "/"                ? basename + (endsWithSlash ? "/" : "")                : joinPaths([basename, pathname]);    }    // 能够看做, 路由的拼接, 包含 ? , #    return navigator.createHref({ pathname: joinedPathname, search, hash });}

resolveTo

解析toArg, 返回对象

function resolveTo(    toArg: To,    routePathnames: string[],    locationPathname: string): Path {    // parsePath下面曾经剖析过了    let to = typeof toArg === "string" ? parsePath(toArg) : toArg;    let toPathname = toArg === "" || to.pathname === "" ? "/" : to.pathname;    let from: string;    if (toPathname == null) {        from = locationPathname;    } else {        let routePathnameIndex = routePathnames.length - 1;        // 如果以 .. 开始的门路        if (toPathname.startsWith("..")) {            let toSegments = toPathname.split("/");            // 去除 ..            while (toSegments[0] === "..") {                toSegments.shift();                routePathnameIndex -= 1;            }            to.pathname = toSegments.join("/");        }        // from 复制        from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";    }    // 解析, 返回对象    let path = resolvePath(to, from);    if (        toPathname &&        toPathname !== "/" &&        toPathname.endsWith("/") &&        !path.pathname.endsWith("/")    ) {        path.pathname += "/";    }    // 确保加上开端 /    return path;}

resolveTo-resolvePath

返回一个绝对于给定路径名的解析门路对象, 这里的函数也根本都讲过

function resolvePath(to: To, fromPathname = "/"): Path {    let {        pathname: toPathname,        search = "",        hash = ""    } = typeof to === "string" ? parsePath(to) : to;    let pathname = toPathname        ? toPathname.startsWith("/")            ? toPathname            // resolvePathname            : resolvePathname(toPathname, fromPathname)        : fromPathname;    return {        pathname,        search: normalizeSearch(search),        hash: normalizeHash(hash)    };}

resolveTo-resolvePath-resolvePathname

function resolvePathname(relativePath: string, fromPathname: string): string {    // 去除开端斜杠, 再以斜杠宰割成数组    let segments = fromPathname.replace(/\/+$/, "").split("/");    let relativeSegments = relativePath.split("/");    relativeSegments.forEach(segment => {        if (segment === "..") {            // 移除 ..            if (segments.length > 1) segments.pop();        } else if (segment !== ".") {            segments.push(segment);        }    });    return segments.length > 1 ? segments.join("/") : "/";}

useLocation useNavigationType

function useLocation(): Location {    // 只是获取 context 中的数据    return React.useContext(LocationContext).location;}

同上

function useNavigationType(): NavigationType {    return React.useContext(LocationContext).navigationType;}

useMatch

function useMatch<    ParamKey extends ParamParseKey<Path>,    Path extends string    >(pattern: PathPattern<Path> | Path): PathMatch<ParamKey> | null {    // 获取 location.pathname    let { pathname } = useLocation();    // matchPath  在 useRoutes-matchRoutes-matchRouteBranch-matchPath 中讲到过    // 对一个URL路径名进行模式匹配,并返回无关匹配的信息。    return React.useMemo(        () => matchPath<ParamKey, Path>(pattern, pathname),        [pathname, pattern]    );}

useNavigate

此 hooks 是用来获取操作路由对象的

function useNavigate(): NavigateFunction {    // 从 context 获取数据    let { basename, navigator } = React.useContext(NavigationContext);    let { matches } = React.useContext(RouteContext);    let { pathname: locationPathname } = useLocation();    // 转成 json, 不便 memo 比照    let routePathnamesJson = JSON.stringify(        matches.map(match => match.pathnameBase)    );    let activeRef = React.useRef(false);    React.useEffect(() => {        activeRef.current = true;    }); // 管制渲染, 须要在渲染结束一次后操作        // 路由操作函数    let navigate: NavigateFunction = React.useCallback(        (to: To | number, options: NavigateOptions = {}) => {            if (!activeRef.current) return; // 管制渲染            // 如果 go 是数字, 则后果相似于 go 办法            if (typeof to === "number") {                navigator.go(to);                return;            }            // 解析go            let path = resolveTo(                to,                JSON.parse(routePathnamesJson),                locationPathname            );            if (basename !== "/") {                path.pathname = joinPaths([basename, path.pathname]);            }            // 这一块 就是 前一个括号产生函数, 后一个括号传递参数            // 小小地转换下:            // !!options.replace ?             //     navigator.replace(            //         path,            //         options.state            //     )            //     : navigator.push(            //         path,            //         options.state            //     )            //            (!!options.replace ? navigator.replace : navigator.push)(                path,                options.state            );        },        [basename, navigator, routePathnamesJson, locationPathname]    );    // 最初返回    return navigate;}

generatePath

返回一个有参数插值的门路。 原理还是通过正则替换

function generatePath(path: string, params: Params = {}): string {    return path        .replace(/:(\w+)/g, (_, key) => {            return params[key]!;        })        .replace(/\/*\*$/, _ =>            params["*"] == null ? "" : params["*"].replace(/^\/*/, "/")        );}

他的具体应用:

generatePath("/users/:id", { id: 42 }); // "/users/42"generatePath("/files/:type/*", {  type: "img",  "*": "cat.jpg"}); // "/files/img/cat.jpg"

这里的代码能够说是笼罩整个 react-router 80%以上, 有些简略的, 用途小的这里也不再过多赘述了

参考文档:

  • https://reactrouter.com/docs/...
  • https://github.com/remix-run/...
  • https://github.com/remix-run/...