zhuanlan.zhihu.com/p/337694767
History API
- window对象通过history对象提供了对浏览器的会话历史的访问;
- 它暴露了很多有用的方法和属性,允许你在用户的浏览历史中向前和向后跳转;
- 使用back()、forward()和go()方法来完成在用户历史记录中向后和向前的跳转
- 用go()方法跳转到history中指定的一个点
- window.history.length确定历史堆栈中页面的数量
- 从HTML5开始提供了对history栈中内容的操作。
- history.pushState()添加历史记录条目
- history.replaceState()修改历史记录条目
- window.onpopstate
不同的历史模式
在创建路由器实例时,history配置允许我们在不同的历史模式中进行选择。
Hash模式
hash模式是用createWebHashHistory()创建的
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
HTML5模式
HTML5模式是通过createWebHistory创建的
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
原理
- 改变url,页面不刷新
- 改变url时,我们可以监听到路由的变化并能够做出一些处理(如更新DOM)
如何实现改变url,页面不刷新?
- HTML5模式,使用replaceState或pushState改变url
- 浏览器不支持replaceState和pushState,使用hash模式,再通过window.location.replace()或window.location.assign()替换url
hash模式举例:
如当前url为http://127.0.0.1:9090/Test#/chart
执行window.location.replace('#/uploadFile')
url改变为http://127.0.0.1:9090/Test#/uploadFile
页面不刷新
如何监听路由变化,触发DOM更新?
- createRouter时创建currentRoute对象为响应式
const START_LOCATION_NORMALIZED = {
path: '/',
name: undefined,
params: {},
query: {},
hash: '',
fullPath: '/',
matched: [],
meta: {},
redirectedFrom: undefined,
};
const currentRoute =shallowRef(START_LOCATION_NORMALIZED)
- 当路由导航被点击,修改currentRoute的value,触发视图重新渲染
onClick: link.navigate,
function navigate(e = {}) {
if (guardEvent(e))
return router[unref(props.replace) ? 'replace' : 'push'](unref(props.to));
return Promise.resolve();
}
function push(to) {
return pushWithRedirect(to);
}
function pushWithRedirect(to, redirectedFrom) {
failure = finalizeNavigation(toLocation, from, true, replace, data);
}
function finalizeNavigation(toLocation, from, isPush, replace, data) {
const error = checkCanceledNavigation(toLocation, from);
if (error)
return error;
const isFirstNavigation = from === START_LOCATION_NORMALIZED;
const state = !isBrowser ? {} : history.state;
if (isPush) {
if (replace || isFirstNavigation)
routerHistory.replace(toLocation.fullPath, assign({
scroll: isFirstNavigation && state && state.scroll,
}, data));
else
routerHistory.push(toLocation.fullPath, data);
}
currentRoute.value = toLocation; //currentRoute被修改,触发响应
handleScroll(toLocation, from, isPush, isFirstNavigation);
markAsReady();
}
- router-view组件定义watch,监听路由的变化
watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => {
if (to) {
to.instances[name] = instance;
if (from && instance === oldInstance) {
to.leaveGuards = from.leaveGuards;
to.updateGuards = from.updateGuards;
}
}
if (instance &&
to &&
(!from || !isSameRouteRecord(to, from) || !oldInstance)) {
(to.enterCallbacks[name] || []).forEach(callback => callback(instance));
}
}, { flush: 'post' });
- router-view组件渲染 router-view组件拿到当前路由,利用全局matcher维护的匹配器匹配到当前路由对应的组件并渲染它。