1. react 路由原理
不同路径渲染不同的组件
有两种实现方式
● HasRouter 利用hash实现路由切换
● BrowserRouter 实现h5 API实现路由切换
1. 1 HasRouter
利用hash 实现路由切换
1.2 BrowserRouter
利用h5 Api实现路由的切换
1.2.1 history
- HTML5规范给我们提供了一个history接口
- HTML5 HIstory API包含两个方法:history.pushState()和history.replaceState(),和一个事件
window.onpopstate pushState
1.2.1.1 history.pushState(stateObject,title,url)
● 第一个参数用于存储该url对应的状态对象,该对象可在onpopstate事件中获取,也可在history对象中获取
● 第二个参数是标题,目前浏览器并未实现
● 第三个参数是设定的url
pushState函数向浏览器的历史堆栈中压入一个url为设定值的记录,并改变历史堆栈的当前指针至栈顶
1.2.1.2 replaceState
● 该接口与pushState参数相同,含义 也相同
● 唯一的区别在于replaceState是替换浏览器历史栈中的当前历史记录为设定的url
● 需要注意的是replaceState 不会改动浏览器历史堆栈的当前指针
1.2.1.3 onpopstate
● 该事件是window属性
● 该事件会在调用浏览器的前进,后退以及在执行history.forward,history.back 和history.go 的时候触发。因为这些操作有一个共性,即修改了历史堆栈的当前指针
● 在不改变document 的前提下,一旦触发当前指针改变则会触发onpopstate事件
2 实现基本路由
2.1 HashRouter 基本用法及实现
import React from 'react';
import { Router } from '../react-router';
import { createHashHistory } from '../history';
class HashRouter extends React.Component {
constructor(props) {
super(props);
this.history = createHashHistory(props)
}
render() {
return (
<Router history={this.history}>
{this.props.children}
</Router>
)
}
}
export default HashRouter;
history 下的 createHashHistory.js
/**
* 工厂方法,用来返回一个历史对象
*/
function createHashHistory(props) {
let stack = [];//模拟一个历史条目栈,这里放的都是每一次的location
let index = -1;//模拟一个当前索引
let action = 'POP';//动作
let state;//当前状态
let listeners = [];//监听函数的数组
let currentMessage;
let userConfirm = props.getUserConfirmation?props.getUserConfirmation():window.confirm;
function go(n) {//go是在历史条目中跳前跳后,条目数不会发生改变
action = 'POP';
index += n;
if(index <0){
index=0;
}else if(index >=stack.length){
index=stack.length-1;
}
let nextLocation = stack[index];
state=nextLocation.state;
window.location.hash = nextLocation.pathname;//用新的路径名改变当前的hash值
}
function goForward() {
go(1)
}
function goBack() {
go(-1)
}
let listener = ()=>{
let pathname = window.location.hash.slice(1);// /users#/api /api
Object.assign(history,{action,location:{pathname,state}});
if(action === 'PUSH'){
stack[++index]=history.location;//1 2 3 6 5
//stack.push(history.location);
}
listeners.forEach(listener=>listener(history.location));
}
window.addEventListener('hashchange',listener);
//to={pathname:'',state:{}}
function push(to,nextState){
action = 'PUSH';
let pathname;
if(typeof to === 'object'){
state = to.state;
pathname = to.pathname;
}else {
pathname = to;
state = nextState;
}
if(currentMessage){
let message = currentMessage({pathname});
let allow = userConfirm(message);
if(!allow) return;
}
window.location.hash = pathname;
}
function listen(listener) {
listeners.push(listener);
return function () {//取消监听函数,如果调它的放会把此监听函数从数组中删除
listeners = listeners.filter(l => l !== listener);
}
}
function block(newMessage){
currentMessage = newMessage;
return ()=>{
currentMessage=null;
}
}
const history = {
action,//对history执行的动作
push,
go,
goBack,
goForward,
listen,
location:{pathname:window.location.hash.slice(1),state:undefined},
block
}
if(window.location.hash){
action = 'PUSH';
listener();
}else{
window.location.hash='/';
}
return history;
}
export default createHashHistory;
2.2 BrowserRouter基本用法及实现
import React from 'react';
import { Router } from '../react-router';
import { createBrowserHistory } from '../history';
class BrowserRouter extends React.Component {
constructor(props) {
super(props);
this.history = createBrowserHistory(props)
}
render() {
return (
<Router history={this.history}>
{this.props.children}
</Router>
)
}
}
export default BrowserRouter;
history 下的 createBrowserHistory.js
/**
* 工厂方法,用来返回一个历史对象
*/
function createBrowserHistory(props){
let globalHistory = window.history;
let listeners = [];
let currentMessage;
let userConfirm = props.getUserConfirmation?props.getUserConfirmation():window.confirm;
function go(n){
globalHistory.go(n);
}
function goForward(){
globalHistory.goForward();
}
function goBack(){
globalHistory.goBack();
}
function listen(listener){
listeners.push(listener);
return function(){//取消监听函数,如果调它的放会把此监听函数从数组中删除
listeners = listeners.filter(l=>l!==listener);
}
}
window.addEventListener('popstate',(event)=>{//push入栈 pop类似于出栈
setState({action:'POP',location:{state:event.state,pathname:window.location.pathname}});
});
function setState(newState){
Object.assign(history,newState);
history.length = globalHistory.length;
listeners.forEach(listener=>listener(history.location));
}
/**
* push方法
* @param {*} path 跳转的路径
* @param {*} state 跳转的状态
*/
function push(to,nextState){//对标history pushState
const action = 'PUSH';
let pathname;
let state;
if(typeof to === 'object'){
state = to.state;
pathname = to.pathname;
}else {
pathname = to;
state = nextState;
}
if(currentMessage){
let message = currentMessage({pathname});
let allow = userConfirm(message);
if(!allow) return;
}
globalHistory.pushState(state,null,pathname);
let location = {state,pathname};
setState({action,location});
}
function block(newMessage){
currentMessage = newMessage;
return ()=>{
currentMessage=null;
}
}
const history = {
action:'POP',//对history执行的动作
push,
go,
goBack,
goForward,
listen,
location:{pathname:window.location.pathname,state:globalHistory.state},
block
}
return history;
}
export default createBrowserHistory;