• React-Router源码分析-History库


    history源码


    history 在 v5 之前使用单独的包, v6 之后再 router 包中单独实现。

    history源码

    Action


    路由切换的动作类型,包含三种类型:

    • POP
    • REPLACE
    • PUSH

    Action 枚举:

    export enum Action {
      Pop = "POP",
      Push = "PUSH",
      Replace = "REPLACE",
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    关于三种动作类型意思,可以进入Actions了解。

    createLocation(current, to , state, key)


    创建一个含有唯一值key的location对象。

    当前方法是一个公共方法,在createBrowserHistory/createHashHistory/createMemoryHistory中都使用其创建location对象。

    如果你需要更深层次了解,请进入createLocation

    getUrlBasedHistory(getLocation, createHref, validateLocation, options)


    createBrowserHistory/createHashHistory函数都基于getUrlBasedHistory,执行getUrlBasedHistory后,会返回一个history对象。

    History库


    history库文件暴露出createMemoryHistory、createBrowserHistory、createHashHistory三个方法,每种方法作用不一样。

    • createMemoryHistory:用于非 dom 环境,react-native 和测试环境

    • createBrowserHistory/createHashHistory:用于浏览器环境,createBrowserHistory对应于history路由模式,而createHashHistory应用于hash模式路由,两者方法的底层都是利用了HTML5 history API方法实现(即监听popstate事件及replaceState、pushState无刷新更改location URL)

    createBrowserHistory/createHashHistory函数都基于getUrlBasedHistory ,提供不同的:

    • getLocation
    • createHref
    • validateLocation
    • options

    属性实现不同的 history 对象

    执行步骤
    history-library history-library

    history对象属性和方法


    1. action

      当前location对象变化的动作类型,关于三种动作类型意思,可以进入Actions了解。

    2. location

      返回当前的location对象。

      底层:getLocation => createLocation()

      具体内容可访问createLocation

    3. listen(fn: Listener)

      在createBrowserHistory函数(此刻以此举例)中,会有一个listener变量来接收传入的监听回调函数fn。

      注意:一个history中,有且仅有一个活跃的listen监听函数,否则会抛出一个异常。

      如果你想要继续传入一个监听回调事件,你可以先执行history.listen(fn)的返回值(作用:清除监听事件),再传入fn。

      监听location变化的函数,传入一个回调函数fn,并将代表location变化的一个update对象传入回调函数中。


      内部逻辑:

      (1) 创建一个popstate监听事件,回调函数handlePop

      (2) 将listener = fn;

      (3)返回一个函数(作用:执行这个函数,可以取消当前的listener);

      源码:

      listen(fn: Listener) {
         if (listener) {
           throw new Error("A history only accepts one active listener");
         }
         window.addEventListener(PopStateEventType, handlePop);
         listener = fn;
      
         return () => {
           window.removeEventListener(PopStateEventType, handlePop);
           listener = null;
         };
      },
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      Listener interface

       export interface Listener {
         (update: Update): void;
       }
      
      • 1
      • 2
      • 3

      Update interface

       export interface Update {
         action: Action; // 动作类型
         location: Location; // 新的location对象
         delta: number | null; // 目的location对象(也可以理解为新的location对象)与之前的location,在history栈中之间的增量
        }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • createHref(to)

      创建地址

      createBrowserHistory

      如果to是一个string,返回to,否则createPath(to),如果想了解createPath,请访问createPath

      内部调用: createBrowserHref(window, to)

      function createBrowserHref(window: Window, to: To) {
        return typeof to === "string" ? to : createPath(to);
      }
      
      • 1
      • 2
      • 3

      createHashHistory

      如果to是一个string,返回to,否则createPath(to),如果想了解createPath,请访问createPath

      内部调用: createBrowserHref(window, to)

      function createHashHref(window: Window, to: To) {
        let base = window.document.querySelector("base");
        let href = "";
      
        if (base && base.getAttribute("href")) {
          let url = window.location.href;
          let hashIndex = url.indexOf("#");
          href = hashIndex === -1 ? url : url.slice(0, hashIndex);
        }
      
        return href + "#" + (typeof to === "string" ? to : createPath(to));
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    • go(n)

      指定跳转地址,调用和HTML5 history api的go方法一样, 如果想了解原生的history,请访问history

    • push

      添加一个新的历史记录

      function push(to: To, state?: any) {
        // 1. 更改动作类型action
        action = Action.Push;
        // 2. 创建一个新的location对象
        let location = createLocation(history.location, to, state);
        if (validateLocation) validateLocation(location, to);
        // 3. 当前索引idx
        index = getIndex() + 1;
        // 4. state状态对象{ idx: index,usr: state, key: 唯一的key值 }
        let historyState = getHistoryState(location, index);
        let url = history.createHref(location);
      
        // try...catch because iOS limits us to 100 pushState calls :/
        try {
          globalHistory.pushState(historyState, "", url);
        } catch (error) {
          if (error instanceof DOMException && error.name === "DataCloneError") {
            throw error;
          }
          window.location.assign(url);
        }
      
        if (v5Compat && listener) {
          // 执行location变化的监听回调事件listener--调用history.listen中传入的事件
          listener({ action, location: history.location, delta: 1 });
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
    • replace

      替换当前的历史记录

      function replace(to: To, state?: any) {
        // 1. 更改动作类型action
        action = Action.Replace;
        // 2. 创建location对象
        let location = createLocation(history.location, to, state);
        if (validateLocation) validateLocation(location, to);
        // 3. 返回state状态对象中的idx,否则返回null
        index = getIndex();
        // 生成一个新对象,包含usr、key、idx:
        let historyState = getHistoryState(location, index);
        // 创建新的URL path
        let url = history.createHref(location);
        // history API中的replaceState替换当前的历史记录
        globalHistory.replaceState(historyState, "", url);
      
        if (v5Compat && listener) {
          listener({ action, location: history.location, delta: 0 });
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19

    更多内容,欢迎访问https://www.wangyuegyq.top

  • 相关阅读:
    GIS工具maptalks开发手册(三)03——官网示例之添加图层和移除图层
    java基于微信小程序的在线药品购买销售商城系统 uinapp 计算机毕业设计
    轻量封装WebGPU渲染系统示例<21>- 3D呈现元胞自动机之生命游戏(源码)
    设计模式---六大设计原则
    基于Python的医院信息管理系统
    竞赛选题 基于机器学习与大数据的糖尿病预测
    200PPI转以太网与易控modbusTCP客户端通信配置
    如何利用自动发现将现网的进程纳入到监控系统中?
    霍夫hough变换直线检测浅显理解
    监控方法论
  • 原文地址:https://blog.csdn.net/wangyuegyq/article/details/134440501