React【 Route 】

820 阅读2分钟

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, // 匹配动态路由 参数对象
}

组件渲染方式分四种情况:

  • component
  • render 函数
  • 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 函数渲染组件的方式,都能渲染,componentrender 函数,必须匹配上之后才能渲染。

补充 pathToRegexp 运用

  1. 引入该方法
const { pathToRegexp } = require('path-to-regexp');
  1. 准备一个空数组,调用返回的 regexp
let params = [];
// 预设路径
let path = '/user/:age/name/:name'; 
// 跳转路径
let pathname = '/user/1/name/stella'; 
// end 是否精确匹配
let regexp = pathToRegexp(path, params, { end: false });
  1. 匹配 跳转路径 与 预设路径
let result = pathname.match(regexp);
  1. 打印相关值, 进行分析
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
// ]
  1. 获取动态路由 参数 与 参数值
// 参数
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;
  }
}