Route 匹配路径,渲染对应的组件
使用
<Router>
<Route
path='/'
exact,
component={ OneComponent },
render,
children,
/>
</Router>
规则
path要匹配的路径exact是否精确匹配component要渲染的组件render函数调用返回组件children函数调用返回组件
实现
1. 引入相应的文件, 导出组件
import React, { useContext } from 'react';
import RouterContext from './RouterContext';
import { pathToRegexp } from 'path-to-regexp';
export default function (props) {
}
2. 通过路由上下文,获取当前要跳转的 pathname
cont routerContext = useContext(RouterContext);
const pathname = routerContext.location.pathname;
3. 拿到要向下传递的 路由属性
let routeProps = {
history: routerContext.history,
location: routerContext.location,
}
4. 拿到传递过来的属性,进行解构取值
- 路径没有传递时,默认为
'/' exact精确匹配没有传递时,默认为false
let {
path = '/', // 预设路径
component: RouteComponent,
exact: end = false,
render,
children,
} = props;
5. 正则库 判断要跳转的路径与预设路径是否匹配 决定是否渲染
传入三个参数
let pathname = window.location.pathname;
let keys = [];
let regexp = pathToRegexp(path, keys, { end });
let result = pathname.match(regexp);
6. 根据匹配结果 渲染
6.1 匹配上了
获取动态参数对象
if (result) {
let [ url, ...values ] = result;
let paramNames = keys.map(key => key.name);
let paramObj = {};
let params = paramNames.reduce((paramObj, paramName, index) => {
paramObj[paramName] = values
return paramObj;
}, paramObj);
}
为 routeProps 新增 match 属性
routeProps.match = {
url, // 实际跳转的路径
path, // 预设的路径
isExact: pathname === url, // 是否精确匹配
paramObj, // 匹配动态路由 参数对象
}
组件渲染方式分四种情况:
componentrender函数children函数- 三者都没有传入时
if (RouteComponent) {
return <RouteComponent {...routeProps} />
} else if (render) {
return render(routeProps);
} else if (children) {
return children(routeProps);
} else {
return null;
}
6.2. 匹配不上
children 有值时渲染组件
else if (!result) {
if (chilren) return children(routerProps);
return null;
}
注意: 无论路径是否匹配上,只要通过
children函数渲染组件的方式,都能渲染,component和render函数,必须匹配上之后才能渲染。
补充 pathToRegexp 运用
- 引入该方法
const { pathToRegexp } = require('path-to-regexp');
- 准备一个空数组,调用返回的
regexp
let params = [];
// 预设路径
let path = '/user/:age/name/:name';
// 跳转路径
let pathname = '/user/1/name/stella';
// end 是否精确匹配
let regexp = pathToRegexp(path, params, { end: false });
- 匹配 跳转路径 与 预设路径
let result = pathname.match(regexp);
- 打印相关值, 进行分析
console.log(params);
// [{
// name: 'age',
// prefix: '/',
// suffix: '',
// pattern: '[^\\/#\\?]+?',
// modifier: ''
// },{
// name: 'name',
// prefix: '/',
// suffix: '',
// pattern: '[^\\/#\\?]+?',
// modifier: ''
// }]
console.log(regexp);
// /^\/user(?:\/([^\/#\?]+?))\/name(?:\/([^\/#\?]+?))(?:[\/#\?](?=[]|$))?(?=[\/#\?]|[]|$)/i
console.log(result);
// [
// '/user/1/name/stella',
// '1',
// 'stella',
// index: 0,
// input: '/user/1/name/stella',
// groups: undefined
// ]
- 获取动态路由 参数 与 参数值
// 参数
params = params.map(param => param.name);
// [ 'age', 'name' ]
// 参数值
const [ url, ...values ] = result.slice(1);
// url: '/user/1/name/stella'
// values: [ '1', 'stella' ]
// 获取匹配到的参数对象
const paramsObj = params.map((param, index) => ({ [param]: result[index] }));
// [ { age: '1' }, { name: 'stella' } ]
完整
import React, { useContext } from 'react';
import RouterContext from './RouterContext';
import {pathToRegexp} from 'path-to-regexp';
xport default function (props) {
let context = useContext(RouterContext);
let { path = '/', component: Component, exact = false, render, children } = props;
path = typeof path === 'string' ? path : path.pathname;
let pathname = context.location.pathname;
let keys = [];
let regexp = pathToRegexp(path, keys, { end: exact });
let result = pathname.match(regexp);
let routeProps = {
location: context.location,
history: context.history,
}
if(result) {
let [ url, ...values ] = result;
let paramNames = keys.map(item => item.name);
let paramObj = {}
let params = paramNames.reduce((paramObj, paramName, index) => {
paramObj[paramName] = values[index];
return paramObj;
}, paramObj);
let matchResult = {
url,
path,
isExact: pathname === url,
params,
}
routeProps.match = matchResult;
if (Component) {
return <Component { ...routeProps } />
} else if (render) {
return render(routeProps);
} else if (children) {
return children(routeProps);
} else {
return null
}
} else {
if (children) return children(routeProps);
return null;
}
}