知識導航:
- 路由重要知識點
- 手寫一個路由
知識點(建議最後看,放到是爲了方便複習)
1. 路由重要知識點
vue-router對於寫過vue的人想必是很熟悉了,所以這裏忽略它的一些基本用法了。
1.1 動態路由
所謂動態路由就是類似那種restful接口對接形式,即以/demo/:id,即可匹配到demo路由同時可在demo路由下的組件中拿到傳過來的id值。
示例:
路由配置中:
{
path: "/",
component: Home,
children: [
{ path: "", name: "home", component: List },
{ path: "/demo/:id", component: Demo},
]
}
發出鏈接組件中的router-link
<router-link to="/demo/1">...</router-link>
接收組件
{{$route.params.id}}
或者
將路由routes配置中更改爲{ path: "/demo/:id", component: Demo,props:true}
這時候接收組件便可以通過props來接收參數了,例如{ props: ['id'] }
1.2 路由守衛
1.2.1 全局守衛
就是一些鉤子,通過運行這些鉤子的執行時期來做一些我們想要的控制。即每次發生路由的跳轉變化執行一下我們所設置的某些邏輯
1.2.1.1 前置鉤子(即前置守衛)
//to:即要進入的路由
//from:即要離開的路由
//next:它是一個函數,即可控制是否能夠進入下個路由
//它的參數形式(1)next(); //默認路由(2)next(false); //阻止路由跳轉(3)next({path:'/'}); //阻止默認路由,跳轉到指定路徑
router.beforeEach((to, from, next) = {
if (to.path == "/login") {
next();
} else {
next("/login")
}
})
1.2.1.1 後置鉤子(即後置守衛)
後置鉤子用法和前置鉤子基本一樣,只不過它所接受的函數參數中沒有了next
router.afterEach((to, from) => {
//邏輯代碼
})
1.2.2 路由獨享
使用和全局類似。
示例:
//登錄模塊
path: '/login',
component:Login,
beforeEnter: (to, from, next) => {
if (to.meta.needLogin && !$store.state.isLogin) {
}
1.2.3 組件內的守衛
直接放進組件中的路由鉤子。
即:(說明放在代碼中)
export default {
//出去時觸發
beforeRouteLeave(to, from, next) {
next();
},
//進來時觸發
beforeRouteEnter(to, from, next) {
next();
},
//它的場景是那種動態路由,如此組件匹配的是/demo/:id,即雖路由發生了變化但還是進到當前組件
beforeRouteUpdate(to, from, next) {
next();
},
data: {},
method: {}
};
2. 手寫一個路由插件
思路:回想我們在使用路由的時候進行了哪些配置。(ps只是基礎實現功能)
觀察
路由配置js文件中:
入口main.js文件:
通過觀察可以知道我們接下來要做什麼了。
- 寫一個路由類
- 構造函數需要接受傳過來的參數
- 要有一個path和組件信息字典即需要把參數整成{path:組件名}
- 需要有個響應式的東西用於存放url中的哈希值
- 需要監聽url中哈希值的變化
- 需要做兩個組件,router-link和 router-view
- 需要把它做成插件
1.開始
current用於存放哈希值借用vue的響應式數據,map用於存放path和組件信息的映射
class MyRouter {
constructor(routes) {
this.$routes = routes;
this.map = {};
this.app = new Vue({
data: {
current: '/'
}
})
}
2.接下來先把它整成一個組件吧
首先我們要理解在路由配置js文件中Vue.use()。它的作用是安裝插件,如果插件是一個對象則該對象必須提供install方法。如果插件是一個方法,那麼這個方法便會被做爲install方法。並且在調用install方法時,Vue會作爲參數傳進去。
即我們想要把上面寫的東西搞成一個組件,就得實現一個install方法。
下面我們用了一下混入,即Vue.mixin({})。簡單說一下它,它的作用是全局註冊一個混入,即它會影響註冊之後創建的每一個vue實例。既然可以影響每個vue實例,那我們可以搞一個vue的鉤子。控制vue實例在它的生命週期中做些我們讓它做的事情
MyRouter.install = function(_vue) {
Vue = _vue;
Vue.mixin({
beforeCreate() {
//目前,在vue根據組件實例時初始化路由,只有根組件實例化時有router。看上面入口文件
if (this.$options.router) {
this.$options.router.init();
}
}
})
}
3.實現init方法
init() {
// 初始化要做的事情:
// 1.進行哈希值的監聽
this.addEvent();
// 2.做好字典映射
this.creatMap(this.$routes);
// 3.整好路由的兩個專有組件
this.creatCom();
}
4.實現監聽addEvent
addEvent() {
哈希值給到current,current是響應式的即哈希值一變,所有的用到current的地方便 跟着變化
window.addEventListener("hashchange", function() {
// 哈希值一改變對應的current便需要改變,注意這裏的this指向爲window
this.app.current = window.location.hash.slice(1) || "/";
}.bind(this));
window.addEventListener('load', function() {
this.app.current = window.location.hash.slice(1) || "/";
}.bind(this));
}
5.實現path與組件信息的映射creatMap(routes);
creatMap(routes) {
routes.routes.forEach(item => {
this.map[item.path] = item;
});
}
6.創鍵兩個專有組件creatCom()
creatCom() {
Vue.component("router-link", {
props: {
to: String
},
//就是渲染一個a標籤
render(h) {
return h("a", {
attrs: {
href: "#" + this.to
}
}, this.$slots.default)
},
});
Vue.component("router-view", {
//將組件渲染成真正的DOM視圖
render: (h) => {
return h(this.map[this.app.current].component)
}
})
}
測試:引用我們寫的路由
效果:
路由完整代碼:
// 思路
// 1.接收一個map的配置信息
// 2.監聽哈希值的變化
// 3.哈希值一旦法師變化,重新渲染頁面
let Vue;
class MyRouter {
constructor(routes) {
this.$routes = routes;
this.map = {};
this.app = new Vue({
data: {
current: '/'
}
})
}
init() {
// 初始化要做的事情:
// 1.進行哈希值的監聽
// 2.做好字典
// 3.整好路由的兩個專有組件
this.addEvent();
this.creatMap(this.$routes);
this.creatCom();
}
addEvent() {
window.addEventListener("hashchange", function() {
// 哈希值一改變對應的current便需要改變,注意這裏的this指向爲window
this.app.current = window.location.hash.slice(1) || "/";
}.bind(this));
window.addEventListener('load', function() {
this.app.current = window.location.hash.slice(1) || "/";
}.bind(this));
}
creatMap(routes) {
routes.routes.forEach(item => {
this.map[item.path] = item;
});
}
creatCom() {
Vue.component("router-link", {
props: {
to: String
},
render(h) {
return h("a", {
attrs: {
href: "#" + this.to
}
}, this.$slots.default)
},
});
Vue.component("router-view", {
render: (h) => {
return h(this.map[this.app.current].component)
}
})
}
}
// 寫成一個插件
// 即實現一個install方法
MyRouter.install = function(_vue) {
Vue = _vue;
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
this.$options.router.init();
}
}
})
}
export default MyRouter