This time the version is 6.2.1
use
Compared to version 5.x, the <Switch> element has been upgraded to <Routes>
Simple v6 example:
function App(){
return <BrowserRouter>
<Routes>
<Route path="/about" element={<About/>}/>
<Route path="/users" element={<Users/>}/>
<Route path="/" element={<Home/>}/>
</Routes>
</BrowserRouter>
}
context
In react-router, he created two contexts for subsequent use. Of course, these two contexts are internal and no API is exposed.
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
In react-router-dom
source parsed we talk BrowserRouter
and HashRouter
, then this MemoryRouter
what is it
He is a <Router> that keeps the history of URLs in memory (does not read or write the address bar). Useful in testing and non-browser environments such as React Native.
The biggest difference between his source code and the other two createMemoryHistory
is a 061faa7986ecc3 method, which also comes from the history
library
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}
/>
);
}
So let's take a look at this method now, here only talk about the difference between it 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
The method used to change the location of course is an API thrown by react-router
How to use:
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>
}
source code
export function Navigate({ to, replace, state }: NavigateProps): null {
// 直接调用 useNavigate 来获取 navigate 方法, 并且 useEffect 每次都会触发
// useNavigate 源码在下方会讲到
let navigate = useNavigate();
React.useEffect(() => {
navigate(to, { replace, state });
});
return null;
}
Outlet
The element used to render the sub-route is simply a placeholder for a route
The code is very simple, the logic used is like this
How to use:
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>
);
}
source code
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
Returns an object's dynamic parameters of key/value pairs from the path matched by the current 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
Compares the pathname given the `to' value to the current location
<NavLink>
the component 061faa7986ef00
function useResolvedPath(to: To): Path {
let { matches } = React.useContext(RouteContext);
let { pathname: locationPathname } = useLocation();
// 合并成一个 json 字符, 至于为什么又要解析, 是为了添加字符层的缓存, 如果是一个对象, 就不好浅比较了
let routePathnamesJson = JSON.stringify(
matches.map(match => match.pathnameBase)
);
// resolveTo 的具体作用在下方讨论
return React.useMemo(
() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname),
[to, routePathnamesJson, locationPathname]
);
}
useRoutes
The useRoutes hook is functionally equivalent to <Routes>, but it uses JavaScript objects instead of <Route> elements to define routes.
Equivalent to a schema version, better configurability
How to use:
If you have used umi, will it feel exactly the same?
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;
}
source code
// 具体的 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
Splitting the basename, the code is very simple, it is directly posted here
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
Process routes recursively, flatten 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;
}
// 压平后组件添加的对象
branches.push({ path, score: computeScore(path, route.index), routesMeta });
});
return branches;
}
useRoutes-matchRoutes-rankRouteBranches
Sorting routes can be skipped here, no matter what the sorting algorithm is, you only need to know that the input value is a series of sorting.
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
Matching function, accepting parameter branch is a 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
Pattern-matches a URL pathname and returns information about the match.
It is also a reserved API available
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
Render the matched route
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
Provide context information for other parts of the application
This component is usually not used, it is the final rendered component of MemoryRouter
In the react-router-dom library, it is also the final rendering component of BrowserRouter and 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
This source code comes from the history repository
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
The element used to wrap the route, mainly through the logic of useRoutes
function Routes({
children,
location
}: RoutesProps): React.ReactElement | null {
return useRoutes(createRoutesFromChildren(children), location);
}
Routes-createRoutesFromChildren
The received parameters are generally Route children, which may be nested in multiple layers, and finally the route component structure we defined,
It will be passed to the useRoutes function
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
Back to full link
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
Parse toArg, return object
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
Returns a parsed path object relative to the given path name, and the functions here are basically described
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;
}
Ditto
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
This hook is used to get the operation route object
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
Returns a path with parameter interpolation. The principle is still through regular replacement
function generatePath(path: string, params: Params = {}): string {
return path
.replace(/:(\w+)/g, (_, key) => {
return params[key]!;
})
.replace(/\/*\*$/, _ =>
params["*"] == null ? "" : params["*"].replace(/^\/*/, "/")
);
}
His specific use:
generatePath("/users/:id", { id: 42 }); // "/users/42"
generatePath("/files/:type/*", {
type: "img",
"*": "cat.jpg"
}); // "/files/img/cat.jpg"
The code here can be said to cover more than 80% of the entire react-router, some of which are simple and less useful, and I won't go into details here.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。