简略版本的React-Router
React-Router须要把握的外围知识点是Context
React的上下文
用法:
import React from 'react';
// Provider 供应者
// Consumer 消费者
let {Provider, Consumer} = React.createContext();
export {
Provider,
Consumer
}
Provider
组件是数据的提供者,会将value
属性提供给Consumer
消费者组件。
Provider组件的用法
import React from 'react';
export default class HashRouter extends React.Component {
render() {
return (
<Provider value={{a:1}}>
this.props.children
</Provider>
)
}
}
Consumer组件的用法
Consumer
组件的内容是一个办法,办法承受一个参数,即是Provider
提供的数据。
import React from 'react';
export default class Route extends React.Component {
render() {
<Consumer>
{state => {
// 这里的state就是Provider提供的数据
console.log(state);
}}
</Consumer>
}
}
路由实现思维
创立一个context.js
的文件创建一个Context
类,并导出Provider
以及Consumer
组件,创立一个HashRouter
的组件,目标是 通过hashchange
的事件来监听location.hash
的变动。并用于包裹Switch
和Route
以及Redirect
组件,像子组件提供数据。Switch
组件的目标是为了让 门路匹配只匹配到一个,Route
组件用于定义路由。Redirect
组件是在未匹配到路由的时候,进行重定向操作。应用Link
组件来进行导航,Link
组件内通过Consumer
来生产HashRouter
传递过去的数据,调用HashRouter
通过Provider
上下文提供一个push
办法来扭转HashRouter
的state
属性达到从新渲染的目标。
组件剖析
react-router-dom/context.js
context.js
这个文件就是创立一个上下文环境,用以在HashRouter
组件中通过Provider
来提供属性,而后在其余组件中通过Consumer
来生产所提供的数据。代码如下:
import React from 'react';
// Provider 供应者
// Consumer 消费者
let {Provider, Consumer} = React.createContext();
export {
Provider,
Consumer
}
react-router-dom/index.js
在ECMASCRIPT6 MODULE
中,应用import
导入模块的时候,会默认去找导入目录上面的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
}
react-router-dom/HashRouter.js
HashRouter
组件作为最外层的根组件,用来解决路由变动,监听到hash
变动扭转本身的state
,而后从新渲染组件。在HashRouter
实例化后就会监听hashchange
事件,在hash
扭转后扭转state
。HashRouter
组件外面应用Provider
来包裹This.props.children
,这样子子组件就能够拜访到通过Provider
提供的上下文了。在HashRouter
组件里通过Provider
组件来提供两个属性,location
和history
属性。location
属性记录的是以后的window.location
对象,也是state
外面的location
字段;history
字段提供一个push
办法,push
办法用于设置window.location.hash
的值来实现路由跳转的性能。代码如下:
import React from 'react';
import {Provider} from './context';
class HashRouter extends React.Component {
constructor(props) {
super(props);
this.state = {
location: {
pathname: window.location.hash.slice(1) || '/'
},
}
}
componentDidMount() {
// 默认hash没有时候跳转到/
window.location.hash = window.location.hash || '/';
// 监听hash值变动,从新设置状态
window.addEventListener('hashchange', ()=> {
this.setState({
location: {
...this.state.location,
pathname: window.location.hash.slice(1) || '/'
}
})
})
}
render() {
const value = {
location: this.state.location,
history: {
push(to) {
window.location.hash = to;
}
}
}
return(
<Provider value={value}>
{this.props.children}
</Provider>
)
}
}
export default HashRouter;
react-router-dom/Switch.js
当存在Redirect
组件时,任何路由都将跳转到其配置的门路去,为了解决这个问题,创立了一个一个Switch
组件, Switch
会遍历所有的this.props.children
,将child
的props
上的path
字段用来跟从Consumer
组件获取到的location
外面的pathname
进行正则匹配,若匹配胜利,则返回该组件。代码如下:
import React from 'react';
import {Consumer} from "./context";
import pathToRegexp from "path-to-regexp";
/**
* 只匹配一个
*/
export default class Switch extends React.Component {
render() {
return (
<Consumer>
{
state => {
let pathname = state.location.pathname;
let children = this.props.children;
for (let i =0; i < children.length; i++) {
const child = children[i];
// 可能没有path属性,比方Redirect组件
let path = child.props.path || '';
const reg = pathToRegexp(path, [], {end: false});
// 匹配胜利
if (reg.test(pathname)) {
return child;
}
}
}
}
</Consumer>
)
}
}
react-router-dom/Route.js
Route
组件是用于申明路由,承受三个props
属性:
path
: 这个字段指定路由的门路component
: 这个字段用于指定路由所对应的组件exact
: 是否严格匹配,严格匹配是指门路以path
结尾。例如:能够匹配到/a
然而匹配不到/a/b
, 如果是非严格匹配的话则能够匹配到。
Route
组件通过本身的props
获取到path
属性,依据path
字段来应用pathToRegexp
库来创立一个正则来匹配从Consumer
生产到的location.pathname
属性,如果匹配到了,就返回props
字段外面的component
所申明的组件。pathToRegexp库承受一个门路、一个数组和其余配置,能够申明一个数组传递进去,用于存储路由携带的动静参数,例如
/a/:id/:name,应用
pathToRegexp`库的话,传递的数组参数会失去一个如下的值:
[{
name: 'id',
}, {
name: 'name'
}]
能够应用数组的map
办法遍历一下数组就能够通过name
属性获取到传入参数的键的一个数组,此时再应用pathname
字段用match
办法去匹配通过pathToRegexp
库所生成的正则,就能够获取到动静参数的值。应用reduce
办法遍历寄存参数建的数组,由此来拼装出一个参数对象。最初将从Consumer
获取的数据以及拼装好的params
参数数据通过props
传递给Component
。代码如下:
import React from 'react';
import pathToRegexp from "path-to-regexp";
import {Consumer} from './context';
class Route extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
return (
<Consumer>
{(state) => {
// path是Route中配置的。
const {path, component: Component, exact = false} = this.props;
// pathName是location中的
let pathname = state.location.pathname;
// 优化计划, 依据path实现一个正则
let keys = []
let reg = pathToRegexp(path, keys, {end: exact});
keys = keys.map(item => item.name); // 键
let res = pathname.match(reg);
let [url, ...values] = res || []; // 值
// if (pathname === path) {
let props = {
location: state.location,
history: state.history,
match: {
params: keys.reduce((obj, current, idx) => {
obj[current] = values[idx];
return obj;
}, {})
}
}
if (res) {
return <Component {...props}/>
}
return null
}}
</Consumer>
);
}
}
export default Route;
react-router-dom/Link.js
Link
组件用来路由导航。Link
承受一个to
的props
属性,用于申明跳转到哪个门路,Link
同理能够通过Consumer
来生产到从HashRouter
传递过去的数据,其中就蕴含history.push
这个办法,在组件上绑定一个点击事件,点击后调用history.push
这个办法就实现了设置window.location.hash
的性能,由此登程hashchange
事件,而后批改了HashRouter
的state
,从新渲染组件。代码如下:
import React from 'react';
import {Consumer} from "./context";
export default class Link extends React.Component {
render(){
return (
<Consumer>
{
state => {
return <button onClick = {
(e) => {
e.preventDefault();
state.history.push(this.props.to)
}
}>{this.props.children}</button>
}
}
</Consumer>
)
}
}
react-router-dom/Redirect.js
Redirect
是一个重定向组件,当后面的Route
组件没有找到匹配的路由的时候,会命中Redirect
组件进行重定向,实质就是通过Consumer
来调用HashRouter
外面的history.push
办法。代码如下:
import React from 'react';
import {Consumer} from "./context";
export default class Redirect extends React.Component {
render() {
return (
<Consumer>
{state => {
// 重定向就是匹配不到后间接跳转到redirect中的to门路
state.history.push(this.props.to);
return null;
}}
</Consumer>
)
}
}
demo例子
App.jsx
<div className="App">
<Router>
<>
<div>
<Link to={"/home"}>主页</Link>
<Link to={"/profile"}>配置</Link>
<Link to={"/user"}>用户</Link>
</div>
<div>
{/*
exact 严格匹配
路由/home/123不会加载/home的组件
*/}
<Switch>
<Route path="/home" exact={true} component={Home} />
<Route path="/home/123" component={Home12} />
<Route path="/profile" component={Profile} />
<Route path="/user" component={User} />
<Redirect to="/home" />
</Switch>
</div>
</>
</Router>
</div>
二级路由
User.js
import React from 'react';
import {Link, Route} from "../react-router-dom";
import UserAdd from "./UserAdd";
import UserList from "./UserList";
import UserDetail from "./UserDetail";
class User extends React.Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div style={{
display: "flex",
}}>
<nav>
<li><Link to={"/user/add"}>增加用户</Link></li>
<li><Link to={"/user/list"}>用户列表</Link></li>
</nav>
<div style={{
flexShrink: 0
}}>
<Route path={"/user/add"} component={UserAdd} />
<Route path={"/user/list"} component={UserList} />
<Route path={"/user/detail/:id"} exact={true} component={UserDetail} />
</div>
</div>
);
}
}
export default User;
手动调用办法实现路由跳转
UserAdd.js
import React from 'react';
export default class UserAdd extends React.Component {
constructor(props){
super(props);
this.nameInput = React.createRef();
}
handleSubmit = (e) => {
e.preventDefault();
console.log(this.nameInput.current.value);
this.props.history.push('/user/list');
}
render() {
console.log(this.props);
return (
<div>
<form onSubmit={this.handleSubmit}>
<input ref={this.nameInput} name={"name"} placeholder={"请输出姓名"} type={"text"}/>
<button type={"submit"}>提交</button>
</form>
</div>
)
}
}
获取参数
UserDetail.js
import React from 'react';
export default class UserDetail extends React.Component {
render() {
console.log(this.props )
return (
<div>
this is detail component {this.props.match.params.id}
</div>
)
}
}
发表回复