持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
前言
在前面的篇章中,我们主要介绍了如何实现react路由里的Router组件,在《 基于history实现路由里的Router 》这个篇章里,我们了解到Router组件的主要功能是通过关联地址栏,给后代组件提供上下文数据。
本篇章我们将要实现一个Route组件,通过Route组件实现上下文数据的消费使用。
封装Route
通过Router组件提供的上下文,我们可以利用该上下文,实现子组件的动态渲染。
import React, { Component } from 'react'
import ctx from "./RouterContext";
// 之前篇章中通过path-to-regexp封装的地址匹配规则
import matchPath from "./matchPath";
export default class Route extends Component {
// 封装:通过上下文动态匹配符合的子组件然后渲染
renderChildren(ctx) {
if (this.props.children !== undefined && this.props.children !== null) {
if (typeof this.props.children === "function") {
return this.props.children(ctx);
}
else {
return this.props.children;
}
}
if (!ctx.match) {
return null;
}
if (typeof this.props.render === "function") {
return this.props.render(ctx);
}
if (this.props.component) {
const Component = this.props.component;
return <Component {...ctx} />
}
return null;
}
matchRoute(location) {
const { exact = false, strict = false, sensitive = false } = this.props;
return matchPath(this.props.path || "/", location.pathname, {
exact,
strict,
sensitive
})
}
consumerFunc = (value) => {
const ctxValue = {
history: value.history,
location: value.location,
match: this.matchRoute(value.location)
}
return <ctx.Provider value={ctxValue}>
{this.renderChildren(ctxValue)}
</ctx.Provider>
}
render() {
return <ctx.Consumer>
{this.consumerFunc}
</ctx.Consumer>
}
}
在之前的篇章《 来,手写一个简单的路由(一) 》中,我们主要map去遍历符合的组件然后去render子组件,在上面的代码主要if判断去处理符合规则的子组件。
在Route组件里,我们新建上下文转发Router提供的上下文数据,然后继续传递给后代,而在Route组件里,同时也利用Router提供的上下文数据进行子组件的动态渲染。
在这里我们主要判断下列情况:
- 组件的children是否有值:
- 有值:
- 是否是函数:
- 是:调用函数并传递上下文数据
- 不是:直接渲染
- 是否是函数:
- 没有值:
- 返回null
- 有值:
- 组件的render是否有值:
- 有值并且是函数:
- 调用render并传递上下文数据
- 有值并且是函数:
- 组件的component是否有值:
- 有值:
- 直接调用
- 没有值:
- 返回null
- 有值:
其中我们主要调用的matchPath就是在上一篇基于path-to-regexp库封装的方法。
至此,我们通过生产-消费模式实现了简单的路由模型。
测试
我们的Route组件封装在react-router中,react-router-dom主要是引入并导出,所以我们引用封装的react-router-dom:
import React from "react";
import { BrowserRouter, Route } from "./react-router-dom"
export default function App() {
return (
<BrowserRouter>
<Route path="/a" component={A}>
</Route>
<Route path="/b" component={B} />
<Route component={Change} />
</BrowserRouter>
)
}
function A() {
return <h1>A</h1>
}
function B() {
return <h1>B</h1>
}
function Change({ history }) {
return <div>
<button onClick={() => {
history.push("/a")
}}>to A</button>
<button onClick={() => {
history.push("/b")
}}>to B</button>
</div>
}
点击跳转到/a页面:
点击跳转到/b页面:
小结
本篇章我们基于生产-消费模式,利用Router组件提供的上下文,封装Route组件,该组件主要是通过匹配规则动态渲染子组件,而匹配规则正是通过上下文数据提供的。
在react-router中封装Route组件,而Route组件利用path-to-regexp库封装的匹配方法实现目标数据。在react-router-dom中则对react-router提供的Route组件进行引入并导出。