# react_router **Repository Path**: aeipyuan/react_router ## Basic Information - **Project Name**: react_router - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-05-13 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # react-router理解 ### 1. Provider和Consumer `Provider`和`Comsume`r是`React`提供的两个原生组件,`Provider`的`value`属性传递数据,`Provider`包裹内的所有`Consumer`都可以直接获取到`Provider`的数据 获取方法 ```javascript let { Provider, Consumer } = React.createContext(); ``` 使用方法 ```javascript
{state => { console.log(state) return
{state.name}
}}
``` ### 2. HashRouter 和 Route 根据`hash`改变组件的步骤 - `HashRouter`绑定`hashchange`事件,每次发生都会触发`setState`,并记录新的`hash` - 数据改变触发`render`函数,`render`一个`Provider`,并且将新数据(`pathname`)放到`Provider`的`value`属性上 - Router作为`Router`的子组件,也会重新执行`render`,此时通过`Consumer`接受`Provider`提供的数据,并且比对`Route`自身传递的`path`属性和`state`里面的`pathname`,如果符合条件则返回`Route`属性传入的`Component`进行渲染,否则返回`null`不渲染 ```javascript /* index.jsx */ /* HashRouter.js */ export default class HashRouter extends React.Component { constructor() { super(); this.state = { location: {/* slice去除# */ pathname: window.location.hash.slice(1) || '/' } } } /* 挂载组件完成时绑定hashchange事件 */ componentDidMount() { window.location.hash = window.location.hash || '/' window.addEventListener('hashchange', () => { this.setState({ location: { ...this.state.location, pathname: window.location.hash.slice(1) || '/' } }) }) } render() { /* 要传给Route使用的值 */ let value = { location: this.state.location } return ( {this.props.children} ) } } /* Route.js */ export default class Route extends React.Component { render() { return ( {state => { /* 浏览器路径 */ let pathname = state.location.pathname;/* /home */ /* 获取Route的路径和组件,以及是否精确匹配 */ //{path: "/home", component: ƒ, exac:true} let { path, component: Component, exac = false } = this.props; /* 比对路径 end为true时为严格模式时*/ let reg = pathToRegexp(path, [], { end: exac }); /* 通过属性把Provider的数据继续传给子组件 */ return reg.test(pathname) ? : null; }} ) } } ``` ### 3. Link ```javascript /* index.jsx */
主页 个人中心 用户
``` 实现步骤 - 扩充`HashRender`通过`Provider`传递的方法,增加`push`方法强制改变`hash` ```javascript /* 要传给Consumer使用的数据 */ let value = { location: this.state.location, history: { push(to) { window.location.hash = to; } } } ``` - `Link`组件插入一个`Consumer`,返回值是一个`a`标签,点击触发`HashRender`提供的`push`方法 ```javascript return ( {state => { /* 调用state的push函数,强制跳转 */ return { state.history.push(this.props.to) }}>{this.props.children} }} ) ``` ### 4. Switch ```javascript ``` `Switch`实现每次只匹配一个组件的效果 - 利用`Consumer`获取浏览器`hash`值 - 遍历孩子,遇到符合条件的直接停止遍历,不处理后面的子元素 ```javascript return ( {state => { let { pathname } = state.location; /* 遍历子元素,找出符合条件的进行渲染然后返回 */ for (let child of this.props.children) { /* 比对 */ let path = child.props.path || ''; let exac = child.props.exac || false; let reg = pathToRegexp(path, [], { end: exac }); if (reg.test(pathname)) return child; } return null; }} ) ``` ### 5. Redirect ```javascript ``` 当所有页面都不匹配时强制跳转 ```javascript return ( {state => { // 直接强行改变hash state.history.push(this.props.to); return null; }} ) ``` ### 6. 根据参数匹配页面 `match`的作用是使组件可以通过类似`/home/:id`的方式传递数据,渲染出`id`相关数据 ```javascript /* user.jsx */ return (
用户添加 用户列表
) /* userList.jsx */ return (
Amy Mike Nancy
) /* userDetail.js */ return (
Detail
id: {this.props.match.params.id}
name:{this.props.match.params.name}
) ``` 实现步骤: - 利用`pathToRegexp`函数解析对比可以得到`key`和`values`的映射关系 ```javascript let { pathToRegexp } = require('path-to-regexp') let keys = []; let reg = pathToRegexp('/user/detail/:id/:name', keys, { end: false }) console.log(keys.map(v => v.name));//[ 'id', 'name' ] /* 测试 */ let pathname = '/user/detail/111/bbb'; let [url, ...values] = path.match(reg); console.log(values);//['111','bbb'];对应于['id','name'] ``` - 依据`pathToRegexp`规则修改`Route`类的`render`函数,将映射应用到`params`上,传递给子元素 ```javascript /* Route.js */ render() { return ( {state => { /* 浏览器路径 */ let { pathname } = state.location;/* /home */ /* 获取Route的路径和组件,以及是否精确匹配 */ //{path: "/home", component: ƒ, exac:true} let { path, component: Component, exac = false } = this.props; /* 比对路径 end为true时为严格模式时*/ let keys = []; let reg = pathToRegexp(path, keys, { end: exac }); keys = keys.map(v => v.name); let [url, ...values] = pathname.match(reg) || []; // console.log(keys)//['id','name'] // console.log(values);//['1','Amy'] /* 设置传给子节点的数据 */ let props = { ...state, match: { params: keys.reduce((data, item, idx) => { data[item] = values[idx]; return data; }, {})//结果 {id:'1',name:'Amy'} } } return reg.test(pathname) ? : null; }} ) } ``` ##### 过程描述 1. 假设`to="/user/detail/1/Amy"`,点击`link`标签,调用`state`传入的`state.history.push(to)`方法,改变`window.location.hash` 2. 触发`HashRouter`的`hashchange`事件,改变`HashRouter`的`state`重新`render`包含的内容 3. `App`组件的`Route`匹配到`/user`渲染`User`组件 4. `User`组件的`Route`匹配到`/user/detail/:id/:name`,将`{id:'1',name:'Amy'}`放到`props.history.match`,再将整个`props`解构传给`userDetail` ## 7. 总结 - `HashRouter`监听`hash`的改变并提供操作`hash`的方法,每次改变都会触发包裹的内部元素进行`render`,并使用`Provider`传递数据 - `Route、Link、Switch、Redirect`都是使用`Consumer`接受`HashRouter`关于`hash`的数据和方法,利用这些数据与属性传入数据进行比对,从而确定是否渲染组件或者子组件