React-router源码解析

665 阅读2分钟

react-router

react-router-dom概念

react-router-dom是一个管理前端路由的第三方库,为什么会出现前端路由这个概念,也是为了让用户体验升级,通过不刷新页面就可以完成页面之间的切换,甚至能达到原生app的效果,这算前端的最高级体验。

第二点,因为所有的页面都在同一个应用里面,页面相互之间的切换不需要再进行 url请求、js,css解析、dom渲染等,这样大大减少了这些步骤所消耗的性能。

列举react-router的几个组件

  • BrowserRouter
  • Router
  • Link
  • Switch
  • useHistory
  • useRouteMatch
  • useLocation
  • useParams
  • withRouter

依次来进行解析

BrowserRouter

import { createBrowserHistory } from 'history'
<Router history = { this.history } children={ this.props.children } />

Router 创建context,传递history

<RouterContext.Provider
	value = {{
    	history: this.props.history,
        location: this.state.location,
        match: 初始化一个match
    }}
>
	{ this.props.children }
</RouterContext.Provider>

constructor(){
	this.props.history.listen((location) => {
    	this.setState({state})
    })
}

Link 渲染成a 标签 ,需要禁止 默认行为

hanldeClick = event => {
	event.preventDefault()
    this.context.history.push( this.props.to )
}
render(){
	<a href = { to } {...resProps} onClick = { this.handleClick }>
    	{ children }
    </a>
}

Route 重头戏 可以接受三个方式 children component render 三者之间是互斥关系,加载顺序如下: children > component > render 大概使用方式如下:

<Route
    path = "/"
    children={children}
    component = { HomePage }
    render = { render }
/>
  //接下来就是代码实现
  <RouterContext.Consumer>
      {
          context => {
              const { history,location } = context
              const { children, component,render,path, computedMatch } = this.props;
              const match = 
                  computedMatch ? computedMatch 
                  : path ?  matchPath(location.pathname,this.props) : context.match
              const props = { ...context,match  }

              // match children, component, render,  null
              // 不match children(function), null
              return (
                  <RouterContext.Provider value={ props }>
                      match ?
                          children ? typeof children === "function" ? children(props): children
                          : component ? React.createElement(component,props)
                              : render ? render(props) : null
                      : typeof children === "function" ? children(props): null
                  </RouterContext.Provider>
              )
          }
      }
  </RouterContext.Consumer>

敲重点 为什么要在return里面包一层 RouterContext.Provider 因为context会采取就近原则 会从 最上面获取match location location因为setState还变了一下, match是没有变

所以需要在match变化之后需要重新包一层 RouterContext.Provider 因为context value变化 子组件重新渲染更新 所以当子组件变化之后 嵌套路由里面所有的值 取得就是最新的值

Switch 独占路由 : 从上往下匹配,如果匹配到了就默认终止匹配

<RouterContext.Consumer>
	{
    	context => {
        	let match,element;
            let { location } = context
            React.Children.forEach(this.props.children, child => {
            	//只要匹配上 match就不等于null 独占路由了
            	if(match == null && React.isValidElement(child)){
                	element = child;
                    match = child.props.path ? matchPath(location.pathname,child.props) : context.match
                }
            })
            return match ? React.cloneElement(element,{ computedMath:match }) : null
        }
    }
</RouterContext.Consumer>

Redirect 跳转

<RouterContext.Consumer>
	{
    	context => {
            let { location,history,match } = context
            let { push=false,to } = this.props
            return <Lifecycle onMount={ ()=> { push ? history.push(to) : history.replace(to) } } ></Lifecycle>
            
        }
    }
</RouterContext.Consumer>
class Lifecycle extends Component{
	componentDidMount() {
		this.props.onMount.call(this)
	}
   	render(){
    	return null
    }
}

高阶组件 withRouter

const withRouter = WrappedComponent => props => {
	return (
      <RouterContext.Consumer>
          {
              context => {
                  return <WrappedComponent { ...props } { ...context } />
              }
          }
      </RouterContext.Consumer>
    )
}

自定义hooks

useRouteMatch,useHistory,useParams,useLocation
const function useRouterMatch(){
	return useContext(RouterContext).match
}

const function useHistory(){
	return useContext(RouterContext).history
}

const function useParams(){
	return useRouterMatch().params ? useRouterMatch().params : {}
}

const function useLocation(){
	return useContext(RouterContext).location
}


好了以上就是react-router相关的组件