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

