vue中兩種路由模式的實(shí)現(xiàn)詳解
平時(shí)我們編寫(xiě)路由時(shí),通常直接下載插件使用,在main.js文件中引入直接通過(guò)引入vue-router中的Router通過(guò)Vue.use使用以后定義一個(gè)routeMap數(shù)組,里邊是我們編寫(xiě)路由的地方,最后通過(guò)實(shí)例化一個(gè) Router實(shí)例 將routes=我們定義的routeMao路由數(shù)組。
但是我們并不知道它是如何實(shí)現(xiàn)的,因此我們可以通過(guò)自己編寫(xiě)插件的形式,實(shí)現(xiàn)一個(gè)vue-router
常用路由步驟:
//router/index.js import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export const routeMap= [ { path: '/home', component: () => import('../components/Home/Home.vue') }, { path: '/list', component: () => import('../components/List/List.vue') }, ] const router = new Router({ routes: constantRouterMap }) export default router //main.js import router from './router' new Vue({ router, store, render: h => h(App), }).$mount('#app')
我們來(lái)實(shí)現(xiàn)一下如何實(shí)現(xiàn)一個(gè)hash模式的路由
由于我們不適用插件形式實(shí)現(xiàn),因此我們使用步驟與平常實(shí)現(xiàn)有一些差異。
router/index.js是我們的路由文件,里面放置我們的路由
plugin/router.js是我們的自定義插件文件,里面放置我們自定義插件內(nèi)容
components是我們組件目錄,放置我們的路由組件(通常使用view定義路由組件)
main.js是我們的入口文件,需要在該文件引入router并且實(shí)例化vue的時(shí)候需要將引入的router使用
1、首先我們?cè)贏pp.vue文件中,通過(guò)<router-view/>以及<router-link/>定義聲明式導(dǎo)航和路由占位
<div> <router-link to="/home">首頁(yè)</router-link> <router-link to="/list">列表</router-link> <hr> <router-view></router-view> </div>
這里我們會(huì)發(fā)現(xiàn)使用以后會(huì)報(bào)錯(cuò),因?yàn)槲覀儧](méi)有下載插件,因此沒(méi)有這兩個(gè)組件。
我們需要配置plugin插件
由于沒(méi)有這兩個(gè)全局組件因此我們需要配置兩個(gè)全局組件
install方法是為了將我們的路由掛載在我們的組件實(shí)例上,通過(guò)mixin全局混入,將我們的實(shí)例上掛載$router屬性
然后定義兩個(gè)全局組件,router-link由于它渲染出來(lái)相當(dāng)于html中的a標(biāo)簽,使用render方法,參數(shù)為一個(gè)createElement方法接收三個(gè)參數(shù)(第一個(gè)參數(shù)創(chuàng)建的元素,第二個(gè)參數(shù)元素具有的屬性, 第三個(gè)參數(shù)元素的內(nèi)容)
router-view由于是一個(gè)路由占位符,因此返回的是個(gè)組件,渲染組件。
通過(guò)routesMap是我們存放的路由,current是我們的當(dāng)前的path路由,因此可以通過(guò)查找路由中key值為我們當(dāng)前path路徑的,查找到我們的組件。通過(guò)return渲染我們的組件
VueRouter.install = function (_Vue) { //1、保存Vue Vue = _Vue //2、將以下代碼延遲到vue實(shí)例初始化完畢執(zhí)行 Vue.mixin({ beforeCreate() { //判斷如果有router屬性就執(zhí)行下方代碼 if (this.$options.router) { Vue.prototype.$router = this.$options.router; } } }) //創(chuàng)建全局組件router-link和router-view //創(chuàng)建router-link組件 Vue.component('router-link', { props: { to: { type: String | Object, required: true } }, render(h) {// h 有三個(gè)參數(shù),第一個(gè)參數(shù)創(chuàng)建的元素,第二個(gè)參數(shù)元素具有的屬性, 第三個(gè)參數(shù)元素的內(nèi)容 return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default) } }) //創(chuàng)建router-view組件 Vue.component('router-view', { // 組件要重新渲染,必須要讓數(shù)據(jù)發(fā)生改變的時(shí)候,重新去執(zhí)行render函數(shù) render(h) { const router = this.$router console.log(router.routesMap, 'routesMap'); let component = router.routesMap[router.current];//組件 //獲取到外部傳遞的路由表 return h(component) } }) }
2、我們定義一個(gè)路由類(lèi),該類(lèi)會(huì)獲取到我們的路由信息從而進(jìn)行路由監(jiān)聽(tīng)以及組件渲染。
(1)、該類(lèi)中constructor是我們通過(guò)實(shí)例化vueRouter傳入的數(shù)據(jù),我們需要通過(guò)將數(shù)據(jù)保存在vueRouter實(shí)例上,獲取到當(dāng)前的url path路徑。
(2)、通過(guò)監(jiān)聽(tīng)方法hashchange監(jiān)聽(tīng)我們hash值的變化,通過(guò)方法進(jìn)行監(jiān)聽(tīng),但是一定要注意這里方法的this指向必須是vueRouter實(shí)例不可以是window,因此需要通過(guò)bind改變this指向
(3)、通過(guò)defineReactive來(lái)講我們的path路徑變?yōu)轫憫?yīng)式,這樣每次路徑發(fā)生變化可以監(jiān)測(cè)到變化。
(4)、定義routesMap為我們的路由對(duì)象,遍歷我們傳入的路由數(shù)組,將我們每個(gè)path路徑對(duì)應(yīng)組件,為一組一組的鍵值對(duì)。
這樣可以實(shí)現(xiàn)一套hash路由模式的路由跳轉(zhuǎn)。
全部代碼如下:
//plugin/router.js let Vue; class VueRouter { constructor(options) { //保存選項(xiàng) this.options = options; // console.log(options, '路由數(shù)據(jù)'); // 定義一個(gè)響應(yīng)式的變量current,保存當(dāng)前的hash值 let url = location.hash.slice(1,) || '/'; // defineReactive定義響應(yīng)式的對(duì)象 Vue.util.defineReactive(this, 'current', url) // hashchange事件來(lái)監(jiān)聽(tīng)hash值的變化, 注意this指向問(wèn)題 addEventListener('hashchange', this.changeHash.bind(this)) this.routesMap = {}; //對(duì)routes中的對(duì)象做一個(gè)映射:routesMap = {'/home':component,'/list': component} this.options.routes.forEach(route => { this.routesMap[route.path] = route.component }) } //監(jiān)聽(tīng)hash值變化的函數(shù) changeHash() { //通過(guò)location.hash來(lái)獲取到hash值 this.current = location.hash.slice(1,) console.log(this.routesMap, '555'); // console.log(this.current);//當(dāng)前path路徑 } } VueRouter.install = function (_Vue) { //1、保存Vue Vue = _Vue //2、將以下代碼延遲到vue實(shí)例初始化完畢執(zhí)行 Vue.mixin({ beforeCreate() { //判斷如果有router屬性就執(zhí)行下方代碼 if (this.$options.router) { Vue.prototype.$router = this.$options.router; } } }) //創(chuàng)建全局組件router-link和router-view //創(chuàng)建router-link組件 Vue.component('router-link', { props: { to: { type: String | Object, required: true } }, render(h) {// h 有三個(gè)參數(shù),第一個(gè)參數(shù)創(chuàng)建的元素,第二個(gè)參數(shù)元素具有的屬性, 第三個(gè)參數(shù)元素的內(nèi)容 return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default) } }) //創(chuàng)建router-view組件 Vue.component('router-view', { // 組件要重新渲染,必須要讓數(shù)據(jù)發(fā)生改變的時(shí)候,重新去執(zhí)行render函數(shù) render(h) { const router = this.$router console.log(router.routesMap, 'routesMap'); let component = router.routesMap[router.current];//組件 console.log(component, 'component'); //獲取到外部傳遞的路由表 return h(component) } }) } export default VueRouter;
接下來(lái)實(shí)現(xiàn)歷史路由模式
我們知道歷史路由模式和hash路由模式區(qū)別是域名是否存在#因此我們可以通過(guò)在路由文件中定義mode屬性判斷是歷史路由模式還是hash路由模式。
先大白話(huà)來(lái)說(shuō)一下大概的實(shí)現(xiàn)思路(hash和history實(shí)現(xiàn)區(qū)別):
1、我們通過(guò)我們定義的mode判斷是hash路由模式還是history路由模式,router-link點(diǎn)擊跳轉(zhuǎn)的時(shí)候如果是hash路由模式,我們通過(guò)render方法返回我們渲染到瀏覽器域名的數(shù)據(jù),hash模式是帶#的,因此我們定義href屬性需要+#,但是history模式是不帶#的,因此我們不需要加#。其次,因?yàn)閔ash模式自動(dòng)檢測(cè)我們的域名,因此我們實(shí)現(xiàn)歷史模式需要手動(dòng)加一個(gè)點(diǎn)擊事件,并且需要阻止a鏈接默認(rèn)跳轉(zhuǎn)的行為(preventDefault取消默認(rèn)行為),并且通過(guò)pushState方法,實(shí)現(xiàn)跳轉(zhuǎn),這是歷史路由模式的方法pushState(obj,title,url),最后設(shè)置我們的路由當(dāng)前path路徑為我們跳轉(zhuǎn)的路徑。
2、當(dāng)我們定義好它的router-link全局組件以后,這時(shí)我們hash路由模式和history路由模式已經(jīng)可以基本實(shí)現(xiàn),hash模式帶#,history路由模式不帶#。但是我們?nèi)砸x一個(gè)監(jiān)聽(tīng)歷史路由模式變化的監(jiān)聽(tīng)事件。這里我們通過(guò)mode判斷是什么模式從而監(jiān)聽(tīng)不同的事件,歷史路由模式監(jiān)聽(tīng)需要通過(guò)監(jiān)聽(tīng)popState方法,來(lái)判斷是否發(fā)生變化,它堅(jiān)挺的是我們?yōu)g覽器的前進(jìn)后退的變化。
歷史路由模式監(jiān)聽(tīng)事件為hashchange方法,這個(gè)事件是用來(lái)監(jiān)聽(tīng)我們hash值的變化,通過(guò)設(shè)置兩個(gè)方法從而設(shè)置我們當(dāng)前路徑為對(duì)應(yīng)的path路徑。hash模式會(huì)通過(guò)slice剪切到#,歷史路由模式則通過(guò)location.pathname獲取到當(dāng)前path路徑。
實(shí)現(xiàn)如下:
let Vue; class VueRouter { constructor(options) { //保存選項(xiàng) this.options = options; // console.log(options, '路由數(shù)據(jù)'); // 定義一個(gè)響應(yīng)式的變量current,保存當(dāng)前的hash值 let url = location.hash.slice(1,) || '/'; // defineReactive定義響應(yīng)式的對(duì)象 Vue.util.defineReactive(this, 'current', url) //判斷當(dāng)前的路由模式 if (this.options.mode === 'hash') { // hashchange事件來(lái)監(jiān)聽(tīng)hash值的變化, 注意this指向問(wèn)題 addEventListener('hashchange', this.changeHash.bind(this)) } else if (this.options.mode === 'history') { // history模式監(jiān)聽(tīng)的事件叫做:popstate, 監(jiān)聽(tīng)的是瀏覽器左上角的兩個(gè)小箭頭的變化 addEventListener('popstate', this.changeHistory.bind(this)) } this.routesMap = {}; //對(duì)routes中的對(duì)象做一個(gè)映射:routesMap = {'/home':component,'/list': component} this.options.routes.forEach(route => { this.routesMap[route.path] = route.component }) } //監(jiān)聽(tīng)hash值變化的函數(shù) changeHash() { //通過(guò)location.hash來(lái)獲取到hash值 this.current = location.hash.slice(1,) console.log(this.routesMap, '555'); // console.log(this.current);//當(dāng)前path路徑 } // history changeHistory() { // console.log(location.pathname) this.current = location.pathname; } }
VueRouter.install = function (_Vue) { //1、保存Vue Vue = _Vue //2、將以下代碼延遲到vue實(shí)例初始化完畢執(zhí)行 Vue.mixin({ beforeCreate() { //判斷如果有router屬性就執(zhí)行下方代碼 if (this.$options.router) { Vue.prototype.$router = this.$options.router; } } }) //創(chuàng)建全局組件router-link和router-view //創(chuàng)建router-link組件 Vue.component('router-link', { props: { to: { type: String | Object, required: true } }, render(h) {// h 有三個(gè)參數(shù),第一個(gè)參數(shù)創(chuàng)建的元素,第二個(gè)參數(shù)元素具有的屬性, 第三個(gè)參數(shù)元素的內(nèi)容 const router = this.$router; if (router.options.mode === 'hash') { return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default) } else if (router.options.mode === 'history') { return h('a', { attrs: { href: this.to }, on: { 'click': ev => { // 1. 阻止a鏈接的默認(rèn)跳轉(zhuǎn)行為 ev.preventDefault() // 2. 調(diào)用pushState方法來(lái)實(shí)現(xiàn)跳轉(zhuǎn): pushState(obj, title, url) history.pushState({}, '', this.to) // 3. 設(shè)置current的值 router.current = this.to; } } }, this.$slots.default) } } }) //創(chuàng)建router-view組件 Vue.component('router-view', { // 組件要重新渲染,必須要讓數(shù)據(jù)發(fā)生改變的時(shí)候,重新去執(zhí)行render函數(shù) render(h) { const router = this.$router console.log(router.routesMap, 'routesMap'); let component = router.routesMap[router.current];//組件 console.log(component, 'component'); //獲取到外部傳遞的路由表 return h(component) } }) }
這一期我們講解了,如何實(shí)現(xiàn)一個(gè)基本的hash路由模式以及history路由模式,但是我們通常使用情況下路由嵌套通過(guò)一級(jí)路由下定義children屬性來(lái)定義二級(jí)路由,這并沒(méi)有實(shí)現(xiàn),下一期來(lái)實(shí)現(xiàn)路由嵌套。
到此這篇關(guān)于vue中兩種路由模式的實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)vue路由模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+Element實(shí)現(xiàn)登錄隨機(jī)驗(yàn)證碼
這篇文章主要為大家詳細(xì)介紹了vue+Element實(shí)現(xiàn)登錄隨機(jī)驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04vue-cli2.x項(xiàng)目?jī)?yōu)化之引入本地靜態(tài)庫(kù)文件的方法
這篇文章主要介紹了vue-cli2.x項(xiàng)目?jī)?yōu)化之引入本地靜態(tài)庫(kù)文件的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06vue?模板循環(huán)繪制多行上傳文件功能實(shí)現(xiàn)
這篇文章主要為大家介紹了vue?模板循環(huán)繪制多行上傳文件功能實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07vue項(xiàng)目中如何實(shí)現(xiàn)網(wǎng)頁(yè)的截圖功能?(html2canvas)
這篇文章主要介紹了vue項(xiàng)目中如何實(shí)現(xiàn)網(wǎng)頁(yè)的截圖功能?(html2canvas),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02網(wǎng)站國(guó)際化多語(yǔ)言處理工具i18n安裝使用方法圖文詳解
國(guó)際化是設(shè)計(jì)軟件應(yīng)用的過(guò)程中應(yīng)用被使用與不同語(yǔ)言和地區(qū),下面這篇文章主要給大家介紹了關(guān)于網(wǎng)站國(guó)際化多語(yǔ)言處理工具i18n安裝使用方法的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09Vue2.0結(jié)合webuploader實(shí)現(xiàn)文件分片上傳功能
這篇文章主要介紹了Vue2.0結(jié)合webuploader實(shí)現(xiàn)文件分片上傳功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-03-03使用electron打包Vue前端項(xiàng)目的詳細(xì)流程
這篇文章主要介紹了使用electron打包Vue前端項(xiàng)目的詳細(xì)流程,文中通過(guò)圖文結(jié)合的方式給大家介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)electron打包Vue有一定的幫助,需要的朋友可以參考下2024-04-04vue2中provide/inject的使用與響應(yīng)式傳值詳解
Vue中 Provide/Inject實(shí)現(xiàn)了跨組件的通信,下面這篇文章主要給大家介紹了關(guān)于vue2中provide/inject的使用與響應(yīng)式傳值的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09