内容简介:用react-router v4可以实现单页面应用,可以将组件映射到路由上,将对应的组件渲染到想要渲染的位置。 react路由有两种方式:一种是HashRouter,即利用hash实现路由切换。另一种是BrowserRouter,即利用html5 API实现路由的切换。本文是在阅读react-router v4源码之后简单的实现。本文将从以下几部分进行总结:以下是参照
用react-router v4可以实现单页面应用,可以将组件映射到路由上,将对应的组件渲染到想要渲染的位置。 react路由有两种方式:一种是HashRouter,即利用hash实现路由切换。另一种是BrowserRouter,即利用html5 API实现路由的切换。本文是在阅读react-router v4源码之后简单的实现。
本文将从以下几部分进行总结:
- 使用react-router的一个简单例子
- 代码结构
- Provider和Consumer
- 实现HashRouter
- 实现Route
- 实现Link
- 实现Redirect
- 实现Switch
使用react-router的一个简单例子
以下是参照 react-router 官方文档 实现的一个简单例子:
import React, { Component } from 'react'; import { render } from 'react-dom'; import { HashRouter as Router, Route, Link, Redirect, Switch } from 'react-router-dom'; import Home from './Home'; import Profile from './Profile'; import User from './User'; export default class App extends Component { constructor() { super(); } render() { return ( <Router> <div> <div> <Link to="/home">首页</Link> <Link to="/profile">个人中心</Link> <Link to="/user">用户</Link> </div> <div> <Switch> <Route path="/home" exact={true} component={Home}></Route> <Route path="/profile" component={Profile}></Route> <Route path="/user" component={User}></Route> <Redirect to="/home"></Redirect> </Switch> </div> </div> </Router> ) } } render(<App></App>, document.querySelector('#root')); 复制代码
这样就能实现一个超级简单的单页面应用,根据路径的变化渲染相应的组件。点击首页会跳转到Home组件,点击个人中心会跳转到Profile组件,点击用户会跳转到User组件。在这个例子当中。看下这行代码
import { HashRouter as Router, Route, Link, Redirect, Switch } from 'react-router-dom'; 复制代码
如果不引入'react-router-dom'这个包,而是自己实现一个my-react-router-dom,暴露出HashRouter,Route,Link,Redirect,Switch这几个组件,并且这几个组件和react-router-dom提供的功能基本一样,那究竟怎么实现呢?
代码结构
如上图所示,在上面的那个例子路由引入'react-router-dom'改为'./my-react-router-dom', 并在同级新建一个my-react-router-dom文件夹,并在index.js中暴露出 HashRouter, Route, Link, Redirect, Switch这几个方法。
// 这是index.js文件 import HashRouter from './HashRouter'; import Route from './Route'; import Link from './Link'; import Redirect from './Redirect'; import Switch from './Switch'; export { HashRouter, Route, Link, Redirect, Switch } 复制代码
Provider和Consumer
再看一下context.js文件,context可以跨组件传递数据:
// 这是context.js文件 import React, { Component } from 'react'; // 这个方法是16.3新增的 let { Provider, Consumer} = React.createContext(); export { Provider, Consumer}; 复制代码
旧版context的致命缺陷
‘现有的原生 Context API 存在着一个致命的问题,那就是在 Context 值更新后,顶层组件向目标组件 props 透传的过程中,如果中间某个组件的 shouldComponentUpdate 函数返回了 false,因为无法再继续触发底层组件的 rerender,新的 Context 值将无法到达目标组件。这样的不确定性对于目标组件来说是完全不可控的,也就是说目标组件无法保证自己每一次都可以接收到更新后的 Context 值。’ 如何解读 react 16.3 引入的新 context api ---诚身的回答
新版 Context API 提供Provider和Consumer两个组件,顾名思义,provider(提供者)和Consumer(消费者):
- Provider 组件:用在组件树中更外层的位置。它接受一个名为 value 的 prop,其值可以是任何 JavaScript 中的数据类型。
- Consumer 组件:可以在 Provider 组件内部的任何一层使用。它接收一个名为 children 值为一个函数的 prop。这个函数的参数是 Provider 组件接收的那个 value prop 的值,返回值是一个 React 元素(一段 JSX 代码)。
实现HashRouter
分析上面的例子,HashRouter的别名设置为Router,传入的 Router 中的每一项即为一条路由配置,表示在匹配给定的地址时,应该使用什么组件渲染视图。HashRouter的简单实现如下:
// 这是hashRouter.js文件 import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { Provider } from './context'; export default class HashRouter extends Component { constructor() { super(); this.state = { location: { // slice(1)将#截掉 pathname: window.location.hash.slice(1) || '' } }; } componentDidMount() { // 首次进入页面url会显示#/ // 如localhost:3000会显示为localhost:3000/#/ window.location.hash = window.location.hash || '/'; // 监听hash值变化,重新设置location状态 window.addEventListener('hashchange', () => { this.setState({ location: { ...this.state.location, pathname: window.location.hash.slice(1) || '/' } }) }) } render() { // 每个子route对象都会包含location let value = { location: this.state.location, history: { push(to) { window.location.hash = to; } } } return ( <Provider value={value}> {this.props.children} </Provider> ) } } 复制代码
这样一来,使用hashRouter的方式第一次进入页面时,将显示#/,如localhost:3000首次进入页面将会显示localhost:3000/#/,通过Provider组件来将location和history对象传递给子route,通过hashchange方法来监听hash值的变化,进而重新设置location的状态。
实现Route
通过分析上面的例子,看这一行代码:
<Route path="/home" exact={true} component={Home}></Route> 复制代码
发现Route组件包含有path,exact,component这些属性,那如何实现Route组件呢?看Route.js文件:
// 这是route.js文件 import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { Consumer } from './context'; import pathToReg from 'path-to-regexp'; export default class Router extends Component { constructor() { super(); } render() { return ( <Consumer> {state => { // <route path="xx" component="xx" exact={true}></route> // path是route传递的 let { path, component: Component, exact = false } = this.props; // pathname是location中的 let pathname = state.location.pathname; // 根据path实现一个正则,通过正则匹配 // location中的/home/123是能匹配到Home组件的 let keys = []; let reg = pathToReg(path, keys, { end: exact}); keys = keys.map(item => item.name); let result = pathname.match(reg); let [url, ...values] = result || []; // 实现路由跳转 let props = { location: state.location, history: state.history, match: { params: keys.reduce((obj, current, index) => { obj[current] = values[index]; return obj; }, {}) } } if (result) { return <Component {...props}></Component> } return null }} </Consumer> ) } } 复制代码
利用path-to-regexp这个库来进行是否严格匹配路径,利用新版context的Consumer组件和props来取出path,component,axact这几个参数。如果匹配到path,则通过<Component {...props}>返回相应匹配到的组件。
实现Link
分析上面的例子,Link的组件实现稍微简单一些,点击内容跳转到to属性对应的路径即可:
// 这是Link组件 import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { Consumer } from './context'; export default class Link extends Component { constructor() { super(); } render() { return ( <Consumer> {state => { return <a onClick={() => { state.history.push(this.props.to); }}>{this.props.children}</a> }} </Consumer> ) } } 复制代码
通过history.push即可实现点击this.props.children的内容跳转到Link组件的to属性对应的路径。
实现Redirect
重定向就是匹配不到后直接跳转到Redirect中的to路径:
// 这是Redirect.js文件 import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { Consumer } from './context'; export default class Redirect extends Component { constructor() { super(); } render() { return ( <Consumer> {state => { // 重定向就是匹配不到后直接跳转到redirect中的to路径 state.history.push(this.props.to); return null; }} </Consumer> ) } } 复制代码
实现Switch
Switch组件的作用就是只匹配第一个匹配到的组件:
// 这是Switch.js的文件 import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { Consumer } from './context'; import pathToRegExp from 'path-to-regexp'; // Switch的作用就是匹配一个组件 export default class Switch extends Component { constructor() { super(); } render() { return ( <Consumer> {state => { { let pathname = state.location.pathname; // 取出Switch包含的组件 let children = this.props.children; for ( var i = 0; i < children.length; i++) { let child = children[i]; // Redirect组件可能没有path属性 let path = child.props.path || ''; pathToRegExp(path, [], {end: false}); // switch匹配成功了 if (reg.test(pathname)) { // 将匹配到的组件返回即可 return child; } } return null; } }} </Consumer> ) } } 复制代码
通过遍历this.props.children来进行逐一匹配,如果匹配到相应的路径,立即返回对应的组件,如果匹配不到,则返回空。
以上所述就是小编给大家介绍的《简易实现react-router v4(上)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
RGB转16进制工具
RGB HEX 互转工具
HEX HSV 转换工具
HEX HSV 互换工具