手写react路由之path-to-regexp


一、path-to-regexp简介

路由的匹配需要对路径进行正则匹配,而path-to-regexp这个库则提供了匹配的方法。
官网使用说明

1. 第一个参数(必填):匹配规则

先看看库的使用

import {
    
    pathToRegexp} from "path-to-regexp"

const result = pathToRegexp("api/blogId/:id")

console.log(result)    
//输出结果是/^api\/blogId(?:\/([^\/#\?]+?))[\/#\?]?$/i,这是一个对象

尝试匹配路径

import {
    
    pathToRegexp} from "path-to-regexp"

const result = pathToRegexp("api/blogId/:id")

console.log(result.exec("api/blogId/123"))
console.log(result.exec("api/blogId/123/"))
console.log(result.exec("/blogId/123"))
console.log(result.exec("api/blogId/123/456"))

控制台输出结果:
控制台输出结果

2. 第二个参数(可选),包含匹配路径关键字的数组

查看第二个参数的内容:

import {
    
    pathToRegexp} from "path-to-regexp"

const keys = [];

const result = pathToRegexp("api/blogId/:id",keys)

console.log(result,keys)

查看打印结果:
在这里插入图片描述

通过打印的结果,我们会发现数组选项表示匹配到的内容,其中数组对象里的name表示匹配到的对应的地址参数key。

3. 第三个参数(可选),配置对象

1.sensitive:是否区分大小写,默认false

2.strict:是否启用严格模式,默认为false,当为true时不能匹配最后的斜杆“/”(如blogID/123/)

扫描二维码关注公众号,回复: 14328391 查看本文章

3.end:是否匹配到字符串的末尾,默认为true,在router里是通过isExact改变

4.start:当为true时,regexp将从字符串的开头匹配,默认值为true

5.delimiter:段的默认分隔符,例如[^/#?],其命名模式为(默认值:’/#?’)

6.endsWith :可选字符或字符列表,将其视为“结束”字符

7.encode:在插入RegExp之前对字符串进行编码的函数

8.prefixes :分析时自动考虑前缀的字符列表(默认值:./)


二、实现router里的match对象

1. 回顾路由给组件注入的参数

先看看router给切换组件注入的props有哪些,通过下列代码,我们进入http://localhost:3000/a页面,并查看控制台打印结果:

import React from 'react'
import {
    
    BrowserRouter as Router,Route} from 'react-router-dom'

export default function App() {
    
                                                        
    return (
        <Router>
            <Route path="/a" exact component={
    
    A} />
            <Route path="/b" component={
    
    B} />
            <Route path="/c" component={
    
    C} />
        </Router>
    )
}
                                                                                                                                                                                                                                                                                                                                                                  
function A(props){
    
    
    console.log("props:",props)
    return <h1>A</h1>
}

function B(){
    
    
    return <h1>B</h1>
}

function C(){
    
    
    return <h1>C</h1>
}

控制台打印结果
其中我们发现match对象里包含四个属性,可以通过被包裹的组件进行读取,分别是isExact(代表是否精确匹配)、params(代表地址匹配参数)、path(代表路径地址)、url(代表匹配到的路径)

2. 实现路由里的match对象

import {
    
     pathToRegexp } from "path-to-regexp"

/**
 * @param {String} path 路径规则
 * @param {String} pathname 页面路径地址的pathname
 * @param {Object} options 相关配置,{sensitive,strict,isExct,}
 */
export default function pathMatch(path,pathname, options) {
    
    

    const keys = [];   //保存路径规则

    const defaultOptions = {
    
    
        sensitive: false,   //是否区分大小写
        strict: false,     //是否为严格模式
        exact: false,   //是否匹配到字符串末尾  
    }  //定义默认值

    options = {
    
    
        ...defaultOptions,
        ...options
    } //混合覆盖成新配置

    const newOptions = {
    
    
        sensitive: options.sensitive,
        strict: options.strict,
        end: options.exact,
    }   //配置

    const regExp = pathToRegexp(path, keys, newOptions)   //库的使用

    const result = regExp.exec(pathname)  //获取匹配结果

    if (!result) {
    
    
        return;
    }   //没有匹配到结果,直接返回

    let resultArray = Array.from(result)   //转换成真数组

    const paramsArray = resultArray.slice(1)  //去除数组第一项

    let params = {
    
    };  //定义params对象

    for (let i = 0; i < paramsArray.length; i++) {
    
    
        params[keys[i]['name']] = paramsArray[i];
    }   //循环获得params对象

    return {
    
    
        isExact: pathname === result[0], //判断是否是精确匹配
        params,   //传入的路径规则
        path,    //匹配规则
        url: result[0],      //url地址上与params匹配到的部分
    };
}

3. 测试手写的match函数

在http://localhost:3000/blogId/123/a下运行下列代码:

const res1 = pathMatch("/blogId/:id", "/blogId/123/a", {
    
     exact: true, strict: true })
console.log("res1", res1)

const res2 = pathMatch("/blogId/:id", "/blogId/123/a", {
    
     exact: false, strict: true })
console.log("res2", res2)

const res3 = pathMatch("/blogId/:id", "/blogId/123/a", {
    
     exact: true, strict: false })
console.log("res3", res3)

const res4 = pathMatch("/blogId/:id", "/blogId/123/a", {
    
     exact: false, strict: false })
console.log("res4", res4)

控制台打印结果如下:
控制台打印结果

在http://localhost:3000/blogId/123/下运行下列代码:

const res1 = pathMatch("/blogId/:id", "/blogId/123/", {
    
     exact: true, strict: true })
console.log("res1", res1)

const res2 = pathMatch("/blogId/:id", "/blogId/123/", {
    
     exact: false, strict: true })
console.log("res2", res2)

const res3 = pathMatch("/blogId/:id", "/blogId/123/", {
    
     exact: true, strict: false })
console.log("res3", res3)

const res4 = pathMatch("/blogId/:id", "/blogId/123/", {
    
     exact: false, strict: false })
console.log("res4", res4)

控制台打印结果如下:
控制台打印结果

在http://localhost:3000/blogId下运行下列代码:

const res1 = pathMatch("/blogId/:id", "/blogId", {
    
     exact: true, strict: true })
console.log("res1", res1)

const res2 = pathMatch("/blogId/:id", "/blogId", {
    
     exact: false, strict: true })
console.log("res2", res2)

const res3 = pathMatch("/blogId/:id", "/blogId", {
    
     exact: true, strict: false })
console.log("res3", res3)

const res4 = pathMatch("/blogId/:id", "/blogId", {
    
     exact: false, strict: false })
console.log("res4", res4)

控制台打印结果如下:
控制台打印结果

总结

match的实现主要依托于path-to-regexp这个库,通过该库导入匹配规则和路径以及相关配置,即可获取包含路由信息的对象。

猜你喜欢

转载自blog.csdn.net/Mr_RedStar/article/details/116862544