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对象属性和方法
-
action
当前location对象变化的动作类型,关于三种动作类型意思,可以进入Actions了解。
-
location
返回当前的location对象。
底层:getLocation => createLocation()
具体内容可访问createLocation。
-
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. 更改动作类型actionaction = Action.Push;// 2. 创建一个新的location对象let location = createLocation(history.location, to, state);if (validateLocation) validateLocation(location, to);// 3. 当前索引idxindex = 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. 更改动作类型actionaction = Action.Replace;// 2. 创建location对象let location = createLocation(history.location, to, state);if (validateLocation) validateLocation(location, to);// 3. 返回state状态对象中的idx,否则返回nullindex = getIndex();// 生成一个新对象,包含usr、key、idx:let historyState = getHistoryState(location, index);// 创建新的URL pathlet url = history.createHref(location);// history API中的replaceState替换当前的历史记录globalHistory.replaceState(historyState, "", url);if (v5Compat && listener) {listener({ action, location: history.location, delta: 0 });} }
更多内容,欢迎访问https://www.wangyuegyq.top