深入了解vue-router原理并實(shí)現(xiàn)一個(gè)小demo
插件編寫(xiě)的基本方法
推薦大家先看看官方給出的插件使用和開(kāi)發(fā)方法
https://vuejs.bootcss.com/guide/plugins.html?
需求分析
我們先看看vue-router的使用步驟
1.use
Vue.use(VueRouter)
注意??:
Vue.use()
主要是調(diào)用插件內(nèi)部的install方法,并將Vue實(shí)例作為參數(shù)傳入?
2.new 一個(gè)router實(shí)例
const router = new VueRouter({ // 實(shí)例化router傳入的參數(shù) mode: 'history', base: process.env.BASE_URL, routes })
3.new Vue() ,把實(shí)例放在vue的配置項(xiàng)里面
new Vue({ router, // 注意router的實(shí)例也往里傳 render: h => h(App) }).$mount('#app')
4.使用路由組件<router-view/>
、<router-link></router-link>
或者在組件中使用this.$router
由此我們看看vue-router內(nèi)部做了什么?
將$router掛載到全局上實(shí)現(xiàn)并聲明了兩個(gè)組件:<router-view/>
、<router-link></router-link>
?
實(shí)現(xiàn)思路
首先我們看看如何將$router掛載到組件上?
let Vue; // 保存vue的構(gòu)造函數(shù),避免打包將其打進(jìn)去 VueRouter.install = function (_Vue) { Vue = _Vue; console.log("options", Vue.$options); Vue.mixin({ beforeCreate() { console.log("inner", this); console.log(" this.$options.router", this.$options.router); if (this.$options.router) { Vue.prototype.$router = this.$options.router; } }, }); console.log("end"); };
可以看到:
1、第一次執(zhí)行的時(shí)候,即在Vue.use(Router)時(shí),還沒(méi)有實(shí)例化vue(因?yàn)?code>Vue.use()發(fā)生在 new Vue()
之前),所以Vue.$option本身是拿不到的(ps: option就是new Vue()
時(shí)傳入的參數(shù),router也往里面?zhèn)鳎藭r(shí)既然拿不到router的實(shí)例,所以不能直接在install方法里面掛載;?
?2、我們可以在use的時(shí)候做一個(gè)全局混入,在合適的時(shí)間點(diǎn),獲取到Vue根實(shí)例配置項(xiàng)中的router實(shí)例, 執(zhí)行掛載。緊接著在new Vue()根實(shí)例創(chuàng)建的時(shí)候,因?yàn)樽⑷肓藃outer實(shí)例,所以再執(zhí)行全局混入(mixin)中的生命周期時(shí),這個(gè)時(shí)候根實(shí)例的配置項(xiàng)this.$options
已經(jīng)包含了router實(shí)例,可以此時(shí)把router掛載到Vue的原型上。之后所有Vue實(shí)例擴(kuò)展來(lái)的VueCompont都可以通過(guò)this.$router
訪問(wèn)到這個(gè)屬性
?如何實(shí)現(xiàn)那兩個(gè)路由組件
先看看路由組件如何使用
<div id="app"> <div id="nav"> <!-- a標(biāo)簽控制跳轉(zhuǎn) --> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> </div> <!-- 路由出口 --> <router-view /> </div>
由上面可以看出,點(diǎn)擊router-link
,就相當(dāng)于點(diǎn)了a標(biāo)簽,然后a標(biāo)簽的href屬性控制頁(yè)面路由發(fā)生了變化;監(jiān)聽(tīng)路由變化,然后仔router-view里面輸出不同的模板;?
先來(lái)看看router-link
class VueRouter { constructor(options) { // 接受傳入的參數(shù) this.$options = options; const initial = "/"; // 將current變成響應(yīng)式數(shù)據(jù), //這樣在hashchange的回掉中修改curent時(shí), //用到current的router-view的render函數(shù)就會(huì)重新渲染 Vue.util.defineReactive(this, "current", initial); // 監(jiān)聽(tīng)路由變化 window.addEventListener("hashchange", () => { // 獲取當(dāng)前url中的hash this.current = window.location.hash.slice(1); }); } } VueRouter.install = function (_Vue) { Vue = _Vue; Vue.component("router-view", { render(h) { // 獲取當(dāng)前路由所對(duì)應(yīng)的組件,然后把它渲染出來(lái) const { current, $options } = this.$router; // 這里要注意 我們傳進(jìn)來(lái)的routes是一個(gè)路由表,如下圖一 // 所以這里我們是找出匹配到當(dāng)前current路由的項(xiàng),然后直接渲染組件 const route = $options.routes.find((item) => { return item.path === current; }); let component = route ? route.component : null; return h(component); }, }); }
?再來(lái)看看router-view
class VueRouter { constructor(options) { // 接受傳入的參數(shù) this.$options = options; const initial = "/"; // 將current變成響應(yīng)式數(shù)據(jù), //這樣在hashchange的回掉中修改curent時(shí), //用到current的router-view的render函數(shù)就會(huì)重新渲染 Vue.util.defineReactive(this, "current", initial); // 監(jiān)聽(tīng)路由變化 window.addEventListener("hashchange", () => { // 獲取當(dāng)前url中的hash this.current = window.location.hash.slice(1); }); } } VueRouter.install = function (_Vue) { Vue = _Vue; Vue.component("router-view", { render(h) { // 獲取當(dāng)前路由所對(duì)應(yīng)的組件,然后把它渲染出來(lái) const { current, $options } = this.$router; // 這里要注意 我們傳進(jìn)來(lái)的routes是一個(gè)路由表,如下圖一 // 所以這里我們是找出匹配到當(dāng)前current路由的項(xiàng),然后直接渲染組件 const route = $options.routes.find((item) => { return item.path === current; }); let component = route ? route.component : null; return h(component); }, }); }
圖一
完整demo代碼
// 我們要實(shí)現(xiàn)什么 // 1、插件 // 2、兩個(gè)組件 // 保存vue的構(gòu)造函數(shù),避免打包將其打進(jìn)去 let Vue; class VueRouter { constructor(options) { this.$options = options; const initial = "/"; Vue.util.defineReactive(this, "current", initial); this.current = "/"; window.addEventListener("hashchange", () => { // 獲取當(dāng)前url中的hash this.current = window.location.hash.slice(1); }); } } // 參數(shù)1在Vue.use()調(diào)用時(shí)傳進(jìn)來(lái), VueRouter.install = function (_Vue) { Vue = _Vue; console.log("options", this); // 全局混入 // 目的:延遲下面的邏輯 到 router創(chuàng)建完畢并且附加到選項(xiàng)上時(shí)才執(zhí)行 Vue.mixin({ // 在每個(gè)組件創(chuàng)建實(shí)例時(shí)都會(huì)執(zhí)行 beforeCreate() { // this.$options.router ;即new Vue時(shí)放進(jìn)去的router實(shí)例 if (this.$options.router) { Vue.prototype.$router = this.$options.router; } }, }); // 注冊(cè)并且實(shí)現(xiàn)兩個(gè)組件 Vue.component("router-link", { props: { to: { required: true, }, }, render(h) { return h( "a", { attrs: { href: "#" + this.to }, }, this.$slots.default ); }, }); Vue.component("router-view", { render(h) { // 獲取當(dāng)前路由所對(duì)應(yīng)的組件,然后把它渲染出來(lái) const { current, $options } = this.$router; const route = $options.routes.find((item) => { return item.path === current; }); let component = route ? route.component : null; return h(component); }, }); }; export default VueRouter;
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
vue簡(jiǎn)單實(shí)現(xiàn)轉(zhuǎn)盤(pán)抽獎(jiǎng)
這篇文章主要為大家詳細(xì)介紹了vue簡(jiǎn)單實(shí)現(xiàn)轉(zhuǎn)盤(pán)抽獎(jiǎng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03vue模仿網(wǎng)易云音樂(lè)的單頁(yè)面應(yīng)用
這篇文章主要介紹了vue仿網(wǎng)易云音樂(lè)的單頁(yè)面應(yīng)用實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04vue移動(dòng)端輕量級(jí)的輪播組件實(shí)現(xiàn)代碼
這篇文章主要介紹了vue移動(dòng)端輕量級(jí)的輪播組件實(shí)現(xiàn)代碼,一個(gè)簡(jiǎn)單的移動(dòng)端卡片滑動(dòng)輪播組件,適用于Vue2.x。本文給大家?guī)?lái)了實(shí)例代碼,需要的朋友參考下吧2018-07-07Vue設(shè)置長(zhǎng)時(shí)間未操作登錄自動(dòng)到期返回登錄頁(yè)
這篇文章主要介紹了Vue設(shè)置長(zhǎng)時(shí)間未操作登錄以后自動(dòng)到期返回登錄頁(yè),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2020-01-01解決el-date-picker日期選擇控件少一天的問(wèn)題
這篇文章主要介紹了解決el-date-picker日期選擇控件少一天的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10vue3點(diǎn)擊不同的菜單頁(yè)切換局部頁(yè)面實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于vue3點(diǎn)擊不同的菜單頁(yè)切換局部頁(yè)面實(shí)現(xiàn)的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue3具有一定的參考價(jià)值,需要的朋友可以參考下2023-08-08Vue實(shí)現(xiàn)的父組件向子組件傳值功能示例
這篇文章主要介紹了Vue實(shí)現(xiàn)的父組件向子組件傳值功能,結(jié)合完整實(shí)例形式簡(jiǎn)單分析了vue.js組件傳值的相關(guān)操作技巧,需要的朋友可以參考下2019-01-01利用vue3+threejs仿iView官網(wǎng)大波浪特效實(shí)例
最近好幾個(gè)vue項(xiàng)目都是用ivew作為UI框架,所以下面這篇文章主要給大家介紹了關(guān)于如何利用vue3?+?threejs仿iView官網(wǎng)大波浪特效的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-12-12vue實(shí)現(xiàn)ToDoList簡(jiǎn)單實(shí)例
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)ToDoList簡(jiǎn)單實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02