react-router 路由的实现原理

React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。本文从两个方便来解析 react-router 实现原理。一:介绍 react-router 的依赖库history;二:使用 history 库,实现一个简单的 react-router 路由。
history 介绍:
history 是一个 JavaScript 库,可让您在 JavaScript 运行的任何地方轻松管理会话历史记录。history 抽象出各种环境中的差异,并提供最小的 API ,使您可以管理历史堆栈,导航,确认导航以及在会话之间保持状态。

history 有三种实现方式:

  1. BrowserHistory:用于支持 HTML5 历史记录 API 的现代 Web 浏览器(请参阅跨浏览器兼容性)
  2. HashHistory:用于旧版Web浏览器
  3. MemoryHistory:用作参考实现,也可用于非 DOM 环境,如 React Native 或测试

三种实现方法,都是创建了一个 history 对象,这里主要讲下前面两种:

const history = {
    length: globalHistory.length, 
    action: "POP", 
    location: initialLocation,
    createHref,
    push, // 改变location
    replace,
    go,
    goBack,
    goForward,
    block,
    listen //监听路由变化
};

1.页面跳转实现
BrowserHistory:pushState、replaceState;
HashHistory:location.hash、location.replace

function push(){
  createKey(); // 创建location的key,用于唯一标示该location,是随机生成的
  if(BrowserHistory){
    globalHistory.pushState({ key, state }, null, href);
  }else if(HashHistory){
    window.location.hash = path;
  }
  //上报listener 更新state ...
}
function replace(){
  createKey(); // 创建location的key,用于唯一标示该location,是随机生成的
  if(BrowserHistory){
    globalHistory.replaceState({ key, state }, null, href); 
  }else if(HashHistory){
    window.location.replace(window.location.href.slice(0, hashIndex >= 0 ? hashIndex : 0) + "#" path);
  } 
  //上报listener 更新state ...  
}

2.浏览器回退
BrowserHistory:popstate;
HashHistory:hashchang;

if(BrowserHistory){
  window.addEventListener("popstate", routerChange);
}else if(HashHistory){
  window.addEventListener("hashchange", routerChange);
}
function routerChange(){
  const location = getDOMLocation(); //获取location
  //路由切换
  transitionManager.confirmTransitionTo(location,callback=()=>{
    //上报listener
    transitionManager.notifyListeners();
  });
}

通过 history 实现简单 react-router

import { Component } from 'react';
import createHistory from 'history/createHashHistory';
const history = createHistory(); //创建 history 对象
/**
 * 配置路由表
 * @type {{"/": string}}
 */
const router = {
  '/': 'page/home/index',
  '/my': 'page/my/index'
}
export default class Router extends Component {
  state = { page: null }

  async route(location) {
    let pathname = location.pathname;
    let pagePath = router[pathname];
    // 加 ./的原因 https://webpack.docschina.org/api/module-methods#import-
    const Page = await import(`./${pagePath}`); //获取路由对应的ui
    //设置ui
    this.setState({ 
      Page: Page.default 
    });
  }

  initListener(){
    //监听路由切换
    history.listen((location, action) => {
      //切换路由后,更新ui
      this.route(location);
    });
  }

  componentDidMount() {
    this.route(history.location);
    this.initListener();
  }

  render() {
    const { Page } = this.state;
    return Page && <Page {...this.props} />;
  }
}

猜你喜欢

转载自blog.csdn.net/weixin_48786946/article/details/106877267