React-Router源码分析-History库

更多内容,请访问:两种路由模式及React-Router中的History库源码分析

history源码


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

history源码

Action


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

  • POP
  • REPLACE
  • PUSH

Action 枚举:

export enum Action {
  Pop = "POP",
  Push = "PUSH",
  Replace = "REPLACE",
}

关于三种动作类型意思,可以进入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;
       };
    },
    

    Listener interface

     export interface Listener {
       (update: Update): void;
     }
    

    Update interface

     export interface Update {
       action: Action; // 动作类型
       location: Location; // 新的location对象
       delta: number | null; // 目的location对象(也可以理解为新的location对象)与之前的location,在history栈中之间的增量
      }
    
  • 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);
    }
    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值