Vue Router的手寫(xiě)實(shí)現(xiàn)方法實(shí)現(xiàn)
為什么需要前端路由
在前后端分離的現(xiàn)在,大部分應(yīng)用的展示方式都變成了 SPA(單頁(yè)面應(yīng)用 Single Page Application)的模式。為什么會(huì)選擇 SPA 呢?原因在于:
- 用戶的所有操作都在同一個(gè)頁(yè)面下進(jìn)行,不進(jìn)行頁(yè)面的跳轉(zhuǎn)。用戶體驗(yàn)好。
- 對(duì)比多頁(yè)面,單頁(yè)面不需要多次向服務(wù)器請(qǐng)求加載頁(yè)面(只請(qǐng)求一次.html文件),只需要向服務(wù)器請(qǐng)求數(shù)據(jù)(多虧了 ajax)。因此,瀏覽器不需要渲染整個(gè)頁(yè)面。用戶體驗(yàn)好。
歸根結(jié)底,還是因?yàn)?SPA 能夠提供更好的用戶體驗(yàn)。
為了更好地實(shí)現(xiàn) SPA,前端路由是必不可少的。假設(shè)一個(gè)場(chǎng)景:用戶在 SPA 頁(yè)面的某個(gè)狀態(tài)下,點(diǎn)擊了強(qiáng)制刷新按鈕。如果沒(méi)有前端路由記住當(dāng)前狀態(tài),那么用戶點(diǎn)擊該按鈕之后,就會(huì)返回到最開(kāi)始的頁(yè)面狀態(tài)。這不是用戶想要的。
當(dāng)然,需要前端路由另一個(gè)點(diǎn)在于:我們可以更好地進(jìn)行 SPA 頁(yè)面的管理。通過(guò)將組件與路由發(fā)生配對(duì)關(guān)聯(lián),依據(jù)路由的層級(jí)關(guān)系,可為 SPA 內(nèi)部的組件劃分與管理提供一個(gè)依據(jù)參考。
Hash 路由模式 與 History 路由模式
這是兩種常見(jiàn)的前端路由模式。
Hash 路由模式
Hash 模式使用了瀏覽器 URL 后綴中的#xxx部分來(lái)實(shí)現(xiàn)前端路由。默認(rèn)情況下,URL后綴中的#xxx hash 部分是用來(lái)做網(wǎng)頁(yè)的錨點(diǎn)功能的,現(xiàn)在前端路由看上了這個(gè)點(diǎn),并對(duì)其加以利用。
比如這個(gè) URL:http://www.abc.com/#/hello,hash 的值為 #/hello。
為什么會(huì)看上瀏覽器URL后綴中的 hash 部分呢?原因也簡(jiǎn)單:
- 瀏覽器URL后綴中的 hash 改變了,不會(huì)觸發(fā)請(qǐng)求,對(duì)服務(wù)器完全沒(méi)有影響。它的改變不會(huì)重新加載瀏覽器頁(yè)面。
- 更關(guān)鍵的一點(diǎn)是,因?yàn)閔ash發(fā)生變化的url都會(huì)被瀏覽器記錄下來(lái),從而你會(huì)發(fā)現(xiàn)瀏覽器的前進(jìn)后退都可以用了,頁(yè)面的狀態(tài)與瀏覽器的URL就發(fā)生了掛鉤。
hash模式背后的原理是onhashchange事件,可以在window對(duì)象上監(jiān)聽(tīng)這個(gè)事件。
History 路由模式
隨著 HTML5 中 history api 的到來(lái),前端路由開(kāi)始進(jìn)化了。hashchange 只能改變 # 后面的代碼片段,history api (pushState、replaceState、go、back、forward) 則給了前端完全的自由。簡(jiǎn)單講,它的功能更為強(qiáng)大了:分為兩大部分,切換和修改。
路由切換
參考MDN,切換歷史狀態(tài)包括 back、forward、go 三個(gè)方法,對(duì)應(yīng)瀏覽器的前進(jìn),后退,跳轉(zhuǎn)操作。
history.go(-2);//后退兩次 history.go(2);//前進(jìn)兩次 history.back(); //后退 hsitory.forward(); //前進(jìn)
路由修改
修改歷史狀態(tài)包括了pushState,replaceState兩個(gè)方法:
/** ** 參數(shù)含義 ** state: 需要保存的數(shù)據(jù),這個(gè)數(shù)據(jù)在觸發(fā)popstate事件時(shí),可以在event.state里獲取 ** title:標(biāo)題,基本沒(méi)用,一般傳 null ** url:設(shè)定新的歷史記錄的 url */ window.history.pushState(state, title, url) //假設(shè)當(dāng)前的url是:https://www.abc.com/a/ //例子1 history.pushState(null, null, './cc/') //此時(shí)的url為https://www.abc.com/a/cc/ //例子2 history.pushState(null, null, '/bb/') //此時(shí)的url為https://www.abc.com/bb/
同樣的,history 模式可以監(jiān)聽(tīng)到對(duì)應(yīng)的事件:
window.addEventListener("popstate", function() { // 監(jiān)聽(tīng)瀏覽器前進(jìn)后退事件,pushState 與 replaceState 方法不會(huì)觸發(fā) });
History 模式的注意點(diǎn)
和 Hash 模式相比,History 模式存在著更多的選擇。但是也有一些自身的注意點(diǎn):在用戶點(diǎn)擊強(qiáng)制刷新的時(shí)候,History 模式會(huì)向服務(wù)器發(fā)送請(qǐng)求。
為了解決這個(gè)問(wèn)題,需要服務(wù)器做對(duì)應(yīng)的處理。服務(wù)器可以針對(duì)不同的URL進(jìn)行處理,當(dāng)然,也可以簡(jiǎn)單處理:只要是未匹配到的URL請(qǐng)求,一律返回同一個(gè) index.html 頁(yè)面。
Vue Router 做了什么?
Vue Router 作為 Vue 生態(tài)系統(tǒng)中非常重要的一個(gè)成員,它實(shí)現(xiàn)了 Vue 應(yīng)用的路由管理??梢哉f(shuō),Vue Router 是專門(mén)為 Vue 量身定制的路由管理器,功能點(diǎn)非常多。它的內(nèi)部實(shí)現(xiàn)是與 Vue 自身是有強(qiáng)耦合關(guān)系的(Vue Router 內(nèi)部利用了 Vue 的數(shù)據(jù)響應(yīng)式)。
我們來(lái)看一個(gè)典型的 Vue Router 配置:
import Vue from "vue"; import App from "./vue/App.vue"; import VueRouter from 'vue-router'; //以插件的形式,使用VueRouter Vue.use(VueRouter); //路由配置信息,可以從外部文件引入,在此直接寫(xiě)是為了方便演示 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } const routes = [ { path: '/', component: Foo }, { path: '/bar', component: Bar } ] //初始化并與 Vue 實(shí)例關(guān)聯(lián) const router = new VueRouter({routes}); new Vue({ router, render: h => h(App), }).$mount("#root");
可看出,VueRouter 是作為插件的形式引入到 Vue 系統(tǒng)內(nèi)部的。而將具體的 router 信息嵌入到每個(gè) Vue 實(shí)例中,則是作為 Vue 的構(gòu)造函數(shù)參數(shù)傳入。
同時(shí)來(lái)看看如何使用它:
//routerExample.vue <template> <div> <h1 @click="goBack">App Test</h1> <router-link to="/">foo</router-link> <router-link to="/bar">bar</router-link> <router-view></router-view> </div> </template> <script> export default { methods: { goBack() { console.log(this.$router); window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/') } } } </script> <style lang="less" scoped> </style>
上面的代碼中,我們可以直接使用router-link和router-view這兩個(gè)組件。它們是隨著 Vue Router 一起引入的,作為全局組件使用。
這就是一個(gè)最簡(jiǎn)單的 Vue Router 的使用方式。我們下面就來(lái)看看,該如何自己實(shí)現(xiàn)上面的簡(jiǎn)單功能,做一個(gè)自己的 Vue Router。
一個(gè)簡(jiǎn)單的 Vue Router 實(shí)現(xiàn)
看了上面的這個(gè)過(guò)程,最簡(jiǎn)單的 Vue Router 應(yīng)該包括以下實(shí)現(xiàn)步驟:
實(shí)現(xiàn) Vue 規(guī)定的插件的寫(xiě)法,將我們自己的Vue Router 作為插件引入 Vue 系統(tǒng)中。
- router功能一:解析傳入的routes選項(xiàng),以備調(diào)用
- router功能二:監(jiān)控URL變化(兩種路由方式:history、hash)
實(shí)現(xiàn)兩個(gè)全局組件:router-link和router-view
看看自定義的 Vue Router 的實(shí)現(xiàn):
//FVueRouter.js let Vue; //保存 Vue 構(gòu)造函數(shù)的引用,與 Vue 深度綁定 class FVueRouter { constructor(options){ this.$options = options; //保存路由的路徑與路由組件的對(duì)應(yīng)關(guān)系 this.routerMap = {}; //當(dāng)前的URL必須是響應(yīng)式的,使用一個(gè)新的 Vue 實(shí)例來(lái)實(shí)現(xiàn)響應(yīng)式功能 this.app = new Vue({ data: {current : "/"} }) } init(){ //監(jiān)聽(tīng)路由事件 this.bindEvents(); //解析傳入的routes this.createRouterMap(); //全局組件的聲明 this.initComponent(); } bindEvents(){ window.addEventListener('hashchange', this.onHashChange.bind(this)); } onHashChange(){ this.app.current = window.location.hash.slice(1) || '/'; } createRouterMap(){ this.$options.routes.forEach(route => { this.routerMap[route.path] = route; }) } initComponent() { // 形式:<router-link to="/"> 轉(zhuǎn)換目標(biāo)=> <a href="#/" rel="external nofollow" >xxx</a> Vue.component("router-link", { props: { to: String, }, render(h) { // h(tag, data, children) return h('a', { attrs: {href: '#' + this.to} }, [this.$slots.default]) }, }); // 獲取path對(duì)應(yīng)的Component將它渲染出來(lái) Vue.component("router-view", { render: (h) => { //此處的this 能夠正確指向 FVouter內(nèi)部,是因?yàn)榧^函數(shù) const Component = this.routerMap[this.app.current].component; return h(Component) } }) } } // 所有的插件都需要實(shí)現(xiàn)install 方法,傳入?yún)?shù)是Vue的構(gòu)造函數(shù) FVueRouter.install = function(_Vue){ //將Vue的構(gòu)造函數(shù)保存起來(lái) Vue = _Vue; //實(shí)現(xiàn)一個(gè)混入操作的原因,插件的install階段非常早,此時(shí)并沒(méi)有Vue實(shí)例 //因此,使用mixin,延遲對(duì)應(yīng)操作到Vue實(shí)例構(gòu)建的過(guò)程中來(lái)執(zhí)行。 Vue.mixin({ beforeCreate(){ //獲取到Router的實(shí)例,并將其掛載在原型上 if(this.$options.router){ //根組件beforeCreate時(shí)只執(zhí)行一次 Vue.prototype.$router = this.$options.router; this.init(); } } }) } export default FVueRouter;
這里是最為簡(jiǎn)單的一種實(shí)現(xiàn)。有幾個(gè)值得注意的點(diǎn):
- 如上代碼,將最基本的一個(gè)Vue Router 的代碼架子搭建起來(lái)了,能夠運(yùn)行。但細(xì)微處依然需要酌情考慮。
- 關(guān)于插件的寫(xiě)法:自定義插件內(nèi)部必須實(shí)現(xiàn)一個(gè) install 方法,傳入?yún)?shù)是Vue的構(gòu)造函數(shù)。
- 使用了一個(gè)新的Vue 實(shí)例,將 URL 的 hash 變量進(jìn)行數(shù)據(jù)響應(yīng)化處理。
- 關(guān)于渲染函數(shù) render 的參數(shù) h,它實(shí)際上是 createElement 函數(shù)。具體用法值得深究。代碼中使用的是最為簡(jiǎn)單的處理方式。
結(jié)尾
在本文中,我們講解了 前端路由常見(jiàn)的兩種模式:Hash 模式與 History 模式。同時(shí),我們嘗試自己實(shí)現(xiàn)了一個(gè)最為簡(jiǎn)單的 Vue Router。更多相關(guān)的 Vue Router 的細(xì)節(jié),可以參考其官網(wǎng)。希望本文對(duì)你有用。
到此這篇關(guān)于Vue Router的手寫(xiě)實(shí)現(xiàn)方法實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Vue Router手寫(xiě)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 區(qū)分vue-router的hash和history模式
- vue-router 控制路由權(quán)限的實(shí)現(xiàn)
- vue-router 按需加載 component: () => import() 報(bào)錯(cuò)的解決
- Vue this.$router.push(參數(shù))實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)操作
- 解決Vue-Router升級(jí)導(dǎo)致的Uncaught (in promise)問(wèn)題
- vue-router重寫(xiě)push方法,解決相同路徑跳轉(zhuǎn)報(bào)錯(cuò)問(wèn)題
- 解決vue的router組件component在import時(shí)不能使用變量問(wèn)題
- vue項(xiàng)目使用$router.go(-1)返回時(shí)刷新原來(lái)的界面操作
- 解決VUE-Router 同一頁(yè)面第二次進(jìn)入不刷新的問(wèn)題
- vue-router之解決addRoutes使用遇到的坑
- 解決vue+router路由跳轉(zhuǎn)不起作用的一項(xiàng)原因
- 如何手寫(xiě)簡(jiǎn)易的 Vue Router
相關(guān)文章
VUE3+mqtt封裝解決多頁(yè)面使用需重復(fù)連接等問(wèn)題(附實(shí)例)
最近了解到mqtt這樣一個(gè)協(xié)議,可以在web上達(dá)到即時(shí)通訊的效果,下面這篇文章主要給大家介紹了關(guān)于VUE3+mqtt封裝解決多頁(yè)面使用需重復(fù)連接等問(wèn)題的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04element 穿梭框性能優(yōu)化的實(shí)現(xiàn)
本文主要介紹了element 穿梭框性能優(yōu)化,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10vue動(dòng)態(tài)禁用控件綁定disable的例子
今天小編就為大家分享一篇vue動(dòng)態(tài)禁用控件綁定disable的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-10-10vue如何設(shè)置輸入框只能輸入數(shù)字且只能輸入小數(shù)點(diǎn)后兩位,并且不能輸入減號(hào)
這篇文章主要介紹了vue如何設(shè)置輸入框只能輸入數(shù)字且只能輸入小數(shù)點(diǎn)后兩位,并且不能輸入減號(hào)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05tomcat部署前端vue項(xiàng)目步驟(項(xiàng)目上線具體操作)
在實(shí)際開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到將Vue項(xiàng)目部署到Tomcat服務(wù)器上的需求,下面這篇文章主要給大家介紹了關(guān)于tomcat部署前端vue項(xiàng)目(項(xiàng)目上線具體操作)的相關(guān)資料,需要的朋友可以參考下2024-07-07vue中對(duì)象的賦值Object.assign({}, row)方式
這篇文章主要介紹了vue中對(duì)象的賦值Object.assign({}, row)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03解決antd 表單設(shè)置默認(rèn)值initialValue后驗(yàn)證失效的問(wèn)題
這篇文章主要介紹了解決antd 表單設(shè)置默認(rèn)值initialValue后驗(yàn)證失效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11