react 路由的基本原理及实现

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;
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端小超人rui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值