React Router 是建立在 history 之上的。 简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。
history 是 React-Router 极少的依赖模块之一,它主要作用是在不同的环境下管理会话。经常会使用到的 history 有:
-
browser history:DOM 的具体应用,支持 HTML5 的 history API。
-
hash history:DOM 的具体应用,用以兼容老版本的浏览器。
-
memory history:在无 DOM 或测试环境下通过内存方式处理来实施,适合 React-Native。 和history 对象相关的主要属性和方法有:
-
length:history 栈中的状态路径个数。
-
action:当前的操作,字符串形式(PUSH, REPLACE, POP)
-
location:当前的状态路径。
- pathname: URL 路径字段
- search:URL 查询字段
- hash:URL 的 hash 字段
- state:路径的状态,只在 browser 和 memory history 中有效。
- push(path, [state]):将状态路径压入 history 栈。
- replace(path, [state]):替换当前的状态路径。
- go(n):向前移动当前指针 n 次。
- goBack():go(-1)
- goForward():go(1)
- block(prompt):暂时阻止导航(“您确定离开”)。
history 是可变的,因此状态路径的访问方式推荐使用 的属性(props)里间接获取,而不是直接使用 history.location。这可以保证在 React 生存周期里能够获得正确的比较。
index.js
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route, Switch} from './react-router-dom'
import 'bootstrap';
import App from './components/App'
import User from './components/User';
import Login from './components/Login';
import Protected from './components/Protected'
let Home = (props,context)=>{
console.log(props,context)
return <div>首页</div>
}
//let User = ()=><div>用户管理</div>
let Profile = ()=><div>个人设置</div>
ReactDOM.render(
<App>
<Route path="/home" component={Home} />
<Route path="/user" component={User} />
<Route path="/login" component={Login} />
<Protected path="/profile" component={Profile} />
</App>
,document.querySelector('#root')
)
/**
*
{
history:{
push()
},
location:{pathname:'/home'},
match{
params:{},
path:'/home',
url:'/home'
}
}
url /user/datail/1
path /user/datail/:id
params= {}
*/
HashRouter.js
import React,{Component} from 'react';
import PropTypes from 'prop-types';
export default class HashRouter extends Component{
static childContextTypes = {
location:PropTypes.object,
history:PropTypes.object
}
constructor(props){
super(props)
this.state = { location: { pathname: window.location.hash.slice(1) || '/' }, state: {} };
}
getChildContext() {
let that = this
return {
location: this.state.location,
history: {
push(path) {
if (typeof path == 'object') {
let { pathname, state } = path;
that.setState({
location: { ...that.state.location, state }
}, () => {
console.log('this.state.location.state', that.state.location.state);
window.location.hash = pathname;
});
} else {
window.location.hash = path;
}
}
}
}
}
componentDidMount(){
window.location.hash = window.location.hash || '/'
let render = ()=>{
this.setState({ location: { ...this.state.location, pathname: window.location.hash.slice(1) || '/' } });
}
window.addEventListener('hashchange',render)
}
render(){
return this.props.children
}
}
Route.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import pathToRegexp from 'path-to-regexp';
export default class Route extends Component {
constructor(props) {
super(props);
let { path } = props;// /user/detail/:id
this.keys = [];
this.regexp = pathToRegexp(path, this.keys, { end: false });
this.keys = this.keys.map(key => key.name);
}
static contextTypes = {
location: PropTypes.object,
history: PropTypes.object
}
render() {
let { path, component: Component, render, children } = this.props;
let { location } = this.context;
let result = location.pathname.match(this.regexp);
let props = {
location,
history: this.context.history
}
if (result) {
let [url, ...values] = result;
props.match = {
url,
path,
params: this.keys.reduce((memo, key, idx) => {
memo[key] = values[idx];
return memo;
}, {})
}
if (Component) {
return <Component {...props} />
} else if (render) {
return render(props);
} else if (children) {
return children(props);
} else {
return null;
}
} else {
if (children) {
return children(props);
} else {
return null;
}
}
}
}
history Hash 的关键是context