简易版 React-Router 实现

544 阅读2分钟

上一篇简单的介绍了react-router 的使用方法和基本的API,对于react-router几个重要的API做了源码解读。这篇就实现一个简易版的 react-router

设计思路

 image.png 由上图可知,核心内容就是如何监听到URL的改变?图中说到三种方式,其实也就两种**pushstate **和 浏览器的前进和回退。刷新页面还是处于当前的URL,不涉及URL的改变。上一篇文章中也讲到 前端路由的原理有两点

  1. URL改变 页面不刷新。
  2. 监听到URL的改变。

所以在设计 react-router 的时候需要考虑 **pushstate **和 浏览器的前进和回退这两种方式的URL改变。

Router

功能:负责监听页面对象发生了改变,并开始重新渲染页面 **

  1. 先定义一个上下文,方便把history数据传入所有的子组件
const RouteContext = React.createContext({})
  1. 定义 Router 组件,主要内容监听URL变化
const globalHistory = window.history // history 使用window 全局的history
class Router extends React.Component {
  constructor(props) {
    super(props)
    this.state = { // 把location 设置为state 每次URL的改变,能够更新页面
      location: window.location
    }
    // 第一种跳转方式:浏览器的前进后退,触发popstate 事件
    window.addEventListener("popstate", () => {
      this.setState({
        location: window.location
      })
    })
  }
  // 第二种跳转方式:pushstate
	// 向子组件提供push 方法更新路由,跳转页面
  push = (route) => {
    globalHistory.pushState({}, "", route)
    this.setState({
      location: window.location
    })
  }
	// 定义上下文,把通用内容传入子组件
  render() {
    const { children } = this.props
    const { location } = this.state
    return (
      <RouteContext.Provider value={{
        history: globalHistory,
        location,
        push: this.push,
      }}>
        {
          React.cloneElement(children, {
            history: globalHistory,
            location,
            push: this.push,
          })
        }
      </RouteContext.Provider>
    )
  }
}

export default Router

Route

功能:页面开始渲染后,根据具体的页面location信息展示具体路由地址对应的内容 **

import React, { useContext } from 'react'
const Route = (props) => {
  // 在上下文中获取到相关信息
  const context = useContext(RouteContext)
  // 计算 location 匹配到的 path
  const computedPath = (path, exact) => {
    ...TODO 
    // 这里内容和源码一样,其核心使用了path-to-regexp 库,能够计算出URL中的参数
  }
  // eslint-disable-next-line no-unused-vars
  const { render, children, component, path, exact = false, ...rest } = props
  const match = computedPath(path, exact)
  const params = { ...context, match, location: context.location }
  // 渲染 也就是源码中的三目运算。把相关的属性传入子组件
  if (match) {
    if (children) {
      if (typeof children === 'function') {
        return children(params)
      }
      return React.cloneElement(children, params)
    } else if (component) {
      return component(params)
    } else if (render) {
      return render(params)
    }
  }
  return null
}

export default Route

这样一个简单的React-Router 就实现了,能够实现页面的跳转。

完整代码:[github.com/LiuSandy/we…