完整的導(dǎo)航解析流程
導(dǎo)航被觸發(fā)
-
在失活組件
(from)
中調(diào)用組件內(nèi)守衛(wèi)beforeRouteLeave
- 離開守衛(wèi)通常用來禁止用戶在還未保存修改前突然離開苦蒿。本次導(dǎo)航可以通過
next(false)
來取消
- 離開守衛(wèi)通常用來禁止用戶在還未保存修改前突然離開苦蒿。本次導(dǎo)航可以通過
-
調(diào)用全局的
beforeEach
守衛(wèi)- 一般被用于登錄驗(yàn)證
若是導(dǎo)航至重用組件佩迟,也就是相同組件,則此時(shí)調(diào)用組件內(nèi)守衛(wèi)
beforeRouteUpdate(2.2+)
調(diào)用路由配置中的
beforeEnter
-
開始解析異步路由組件
- 也就是這種形式導(dǎo)入的組件
component: () => import("url")
- 也就是這種形式導(dǎo)入的組件
-
在被激活的組件中調(diào)用
beforeRouteEnter
該守衛(wèi)中無法使用
this
惫撰,因?yàn)榇藭r(shí)組件實(shí)例還未被創(chuàng)建,所以為undefined
但可在
next
函數(shù)的參數(shù)中傳入回調(diào)函數(shù)厨钻,該回調(diào)函數(shù)參數(shù)vm
夯膀,即為實(shí)例,于導(dǎo)航被確認(rèn)時(shí)執(zhí)行若是重用組件蝴蜓,則只會(huì)在第一次進(jìn)入重用組件時(shí)調(diào)用該守衛(wèi)俺猿,重用組件自身切換不會(huì)再次調(diào)用
-
調(diào)用全局的
beforeResolve
守衛(wèi)(2.5+)
- 與
router.beforeEach
類似押袍,區(qū)別是在導(dǎo)航被確認(rèn)之前,同時(shí)在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后汽馋,該守衛(wèi)才被調(diào)用
- 與
導(dǎo)航被確認(rèn)
調(diào)用全局的
afterEach
守衛(wèi)觸發(fā)
DOM
更新用創(chuàng)建好的實(shí)例調(diào)用
beforeRouteEnter
守衛(wèi)中傳給next
的回調(diào)函數(shù)
原理
hash版
<a href="#/dasha">dasha</a>
-
若不為
a
標(biāo)簽,則監(jiān)控點(diǎn)擊事件即可<span onClick="routerChange('/dasha')">dasha</span>
直接傳入路徑作為參數(shù)
function routerChange(pathName) { location.hash = pathName; }
賦值后自動(dòng)添加#
監(jiān)聽
hashchange
事件window.addEventListener("hashchange", () => {})
通過
location.hash
可獲取頁面hash
值-
第一次進(jìn)入該頁面時(shí)悄雅,不會(huì)有
hashchange
煤伟,所以可以使用DOMContentLoaded
事件做初始化window.addEventListener("DOMContentLoaded", () => {})
history版
html
<span onclick="routerChange('/')">首頁</span>
<span onclick="routerChange('/chloe')">chloe</span>
-
js
通過
H5
中的history.pushState
實(shí)現(xiàn)不刷新頁面木缝,改變url
中的路徑通過
popstate
事件監(jiān)聽瀏覽器的前進(jìn)回退事件通過
location.pathname
可獲取當(dāng)前頁面路徑
function routerChange(pathName) {
history.pushState(null, null, pathName);
}
window.addEventListener("popstate", () => {})
hash與history區(qū)別
-
hash
- 帶有
#
我碟,不會(huì)和服務(wù)器產(chǎn)生交流
- 帶有
-
history
- 沒有
#
,正常的url
改變吱殉,會(huì)和服務(wù)端產(chǎn)生交流
- 沒有
實(shí)現(xiàn)基礎(chǔ)Vue Router
- 便于查看 所以未模塊化
當(dāng)前路由的信息
class History {
constructor() {
this.current = {
path: ""
};
}
}
router實(shí)例構(gòu)造函數(shù)
class VueRouter {
constructor(options) {
this.routesMap = this.createRouteMap(options.routes || []);
history即為$route
this.history = new History();
拿到模式
this.mode = options.mode || "hash";
初始化route信息
this.init();
}
處理路由組件信息 以路徑為key component為值
createRouteMap(routes) {
const routesMap = {};
for (let i = 0; i < routes.length; i++) {
routesMap[routes[i].path] = routes[i].component;
}
return routesMap;
}
初始化route信息
init() {
if (this.mode === "hash") {
location.hash || (location.hash = "/");
document.addEventListener("DOMContentLoaded", () => {
this.history.current.path = location.hash.slice(1);
});
window.addEventListener("hashchange", () => {
this.history.current.path = location.hash.slice(1);
});
} else {
document.addEventListener("DOMContentLoaded", () => {
this.history.current.path = location.pathname;
});
window.addEventListener("popstate", () => {
this.history.current.path = location.pathname;
});
}
}
}
Vue.use()會(huì)默認(rèn)調(diào)用install方法
VueRouter.install = (Vue) => {
this.$options.router即為new VueRouter創(chuàng)建的實(shí)例
給所有組件都混入beforeCreate鉤子 該組件的$options上若有router
則說明該組件為根實(shí)例 因?yàn)閞outer只在new Vue()上設(shè)置了
Vue.mixin({
給根實(shí)例添加_router和_route屬性
beforeCreate() {
if (this.$options.router) {
給_router賦值為該實(shí)例
this._router = this.$options.router;
給_route賦值 使用Vue的響應(yīng)式方法 在_route改變時(shí) 重新渲染頁面
Vue.util.defineReactive(this, "_route", this._router.history.current);
}
}
});
通過Object.defineProperty而不是Vue.prototype.$router給$router賦值
可實(shí)現(xiàn)在get方法中拿到調(diào)用$router的組件實(shí)例,this.$router
$router即為new VueRouter所創(chuàng)建的實(shí)例
Object.defineProperty(Vue.prototype, "$router", {
get() {
返回根實(shí)例上的router實(shí)例
return this.$root._router;
}
});
與$router同理
Object.defineProperty(Vue.prototype, "$route", {
get() {
返回根實(shí)例上的router上的history
return this.$root._route;
}
});
router-link組件
Vue.component("router-link", {
props: {
to: {
type: String,
require: true
},
tag: {
type: String,
default: "a"
}
},
methods: {
手動(dòng)改變hash值
handleClick() {
const mode = this.$router.mode;
if (mode === "hash") {
location.hash = this.to;
} else {
history.pushState(null, null, this.to);
this.$router.history.current.path = this.to;
}
}
},
render(h) {
const mode = this.$router.mode;
const data = {};
if (this.tag === "a" && mode === "hash") {
data.attrs = { href: "#" + this.to };
} else {
data.on = {
若不是a標(biāo)簽 則觸發(fā)點(diǎn)擊事件
click: this.handleClick
};
}
通過h而不是JSX來創(chuàng)建 可渲染不同的tag
return h(this.tag, data, this.$slots.default);
}
});
router-view組件
Vue.component("router-view", {
functional: true,
render(h, { parent }) {
拿到routesMap
const routesMap = parent.$router.routesMap;
const path = parent.$route.path;
return h(routesMap[path]);
}
});
};
export default VueRouter;