亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

簡化版的vue-router實現思路詳解

2019-11-19 12:40:10
字體:
來源:轉載
供稿:網友

本文旨在介紹 vue-router 的實現思路,并動手實現一個簡化版的 vue-router 。我們先來看一下一般項目中對 vue-router 最基本的一個使用,可以看到,這里定義了四個路由組件,我們只要在根 vue 實例中注入該 router 對象就可以使用了.

import VueRouter from 'vue-router';import Home from '@/components/Home';import A from '@/components/A';import B from '@/components/B'import C from '@/components/C'Vue.use(VueRouter)export default new VueRouter.Router({ // mode: 'history', routes: [ {  path: '/',  component: Home }, {  path: '/a',  component: A }, {  path: '/b',  component: B }, {  path: '/c',  component: C } ]})

vue-router 提供兩個全局組件, router-view router-link ,前者是用于路由組件的占位,后者用于點擊時跳轉到指定路由。此外組件內部可以通過 this.$router.push , this.$rouer.replace 等api實現路由跳轉。本文將實現上述兩個全局組件以及 push 和 replace 兩個api,調用的時候支持 params 傳參,并且支持 hash 和 history 兩種模式,忽略其余api、嵌套路由、異步路由、 abstract 路由以及導航守衛等高級功能的實現,這樣有助于理解 vue-router 的核心原理。本文的最終代碼不建議在生產環境使用,只做一個學習用途,下面我們就來一步步實現它。

install實現

任何一個 vue 插件都要實現一個 install 方法,通過 Vue.use 調用插件的時候就是在調用插件的 install 方法,那么路由的 install 要做哪些事情呢?首先我們知道 我們會用 new 關鍵字生成一個 router 實例,就像前面的代碼實例一樣,然后將其掛載到根 vue 實例上,那么作為一個全局路由,我們當然需要在各個組件中都可以拿到這個 router 實例。另外我們使用了全局組件 router-view 和 router-link ,由于 install 會接收到 Vue 構造函數作為實參,方便我們調用 Vue.component 來注冊全局組件。因此,在 install 中主要就做兩件事,給各個組件都掛載 router 實例,以及實現 router-view router-link 兩個全局組件。下面是代碼:

const install = (Vue) => { if (this._Vue) { return; }; Vue.mixin({ beforeCreate() {  if (this.$options && this.$options.router) {  this._routerRoot = this;  this._router = this.$options.router;  Vue.util.defineReactive(this, '_routeHistory', this._router.history)  } else {  this._routerRoot = (this.$parent && this.$parent._routerRoot) || this  }  Object.defineProperty(this, '$router', {  get() {   return this._routerRoot._router;  }  })  Object.defineProperty(this, '$route', {  get() {   return {   current: this._routerRoot._routeHistory.current,   ...this._routerRoot._router.route   };  }  }) } }); Vue.component('router-view', { render(h) { ... } }) Vue.component('router-link', {  props: {  to: String,  tag: String, }, render(h) { ... } }) this._Vue = Vue;}

這里的 this 代表的就是 vue-router 對象,它有兩個屬性暴露出來供外界調用,一個是 install ,一個是 Router 構造函數,這樣可以保證插件的正確安裝以及路由實例化。我們先忽略 Router 構造函數,來看 install ,上面代碼中的 this._Vue 是個開始沒有定義的屬性,他的目的是防止多次安裝。我們使用 Vue.mixin 對每個組件的 beforeCreate 鉤子做全局混入,目的是讓每個組件實例共享 router 實例,即通過 this.$router 拿到路由實例,通過 this.$route 拿到路由狀態。需要重點關注的是這行代碼:

Vue.util.defineReactive(this, '_routeHistory', this._router.history)

這行代碼利用 vue 的響應式原理,對根 vue 實例注冊了一個 _routeHistory 屬性,指向路由實例的 history 對象,這樣 history 也變成了響應式的。因此一旦路由的 history 發生變化,用到這個值的組件就會觸發 render 函數重新渲染,這里的組件就是 router-view 。從這里可以窺察到 vue-router 實現的一個基本思路。上述的代碼中對于兩個全局組件的 render 函數的實現,因為會依賴于 router 對象,我們先放一放,稍后再來實現它們,下面我們分析一下 Router 構造函數。

Router構造函數

經過剛才的分析,我們知道 router 實例需要有一個 history 對象,需要一個保存當前路由狀態的對象 route ,另外很顯然還需要接受路由配置表 routes ,根據 routes 需要一個路由映射表 routerMap 來實現組件搜索,還需要一個變量 mode 判斷是什么模式下的路由,需要實現 push 和 replace 兩個api,代碼如下:

const Router = function (options) { this.routes = options.routes; // 存放路由配置 this.mode = options.mode || 'hash'; this.route = Object.create(null), // 生成路由狀態 this.routerMap = createMap(this.routes) // 生成路由表 this.history = new RouterHistory(); // 實例化路由歷史對象 this.init(); // 初始化}Router.prototype.push = (options) => { ... }Router.prototype.replace = (options) => { ... }Router.prototype.init = () => { ... }

我們看一下路由表 routerMap 的實現,由于不考慮嵌套等其他情況,實現很簡單,如下:

const createMap = (routes) => { let resMap = Object.create(null); routes.forEach(route => { resMap[route['path']] = route['component']; }) return resMap;}

RouterHistory 的實現也很簡單,根據前面分析,我們只需要一個 current 屬性就可以,如下:

const RouterHistory = function (mode) { this.current = null; }

有了路由表和 history , router-view 的實現就很容易了,如下:

Vue.component('router-view', { render(h) {  let routerMap = this._self.$router.routerMap;  return h(routerMap[this._self.$route.current]) } })

這里的 this 是一個 renderProxy 實例,他有一個屬性 _self 可以拿到當前的組件實例,進而訪問到 routerMap ,可以看到路由實例 history 的 current 本質上就是我們配置的路由表中的 path 。

接下來我們看一下 Router 要做哪些初始化工作。對于 hash 路由而言,url上 hash 值的改變不會引起頁面刷新,但是可以觸發一個 hashchange 事件。由于路由 history.current 初始為 null ,因此匹配不到任何一個路由,所以會導致頁面刷新加載不出任何路由組件?;谶@兩點,在 init 方法中,我們需要實現對頁面加載完成的監聽,以及 hash 變化的監聽。對于 history 路由,為了實現瀏覽器前進后退時準確渲染對應組件,還要監聽一個 popstate 事件。代碼如下:

Router.prototype.init = function () { if (this.mode === 'hash') { fixHash() window.addEventListener('hashchange', () => {  this.history.current = getHash(); }) window.addEventListener('load', () => {  this.history.current = getHash(); }) } if (this.mode === 'history') { removeHash(this); window.addEventListener('load', () => {  this.history.current = location.pathname; }) window.addEventListener('popstate', (e) => {  if (e.state) {  this.history.current = e.state.path;  } }) }}

當啟用 hash 模式的時候,我們要檢測url上是否存在 hash 值,沒有的話強制賦值一個默認 path , hash 路由時會根據 hash 值作為 key 來查找路由表。 fixHash 和 getHash 實現如下:

const fixHash = () => { if (!location.hash) { location.hash = '/'; }}const getHash = () => { return location.hash.slice(1) || '/';}

這樣在刷新頁面和 hash 改變的時候, current 可以得到賦值和更新,頁面能根據 hash 值準確渲染路由。 history 模式也是一樣的道理,只是它通過 location.pathname 作為 key 搜索路由組件,另外 history 模式需要去除url上可能存在的 hash , removeHash 實現如下:

const removeHash = (route) => { let url = location.href.split('#')[1] if (url) { route.current = url; history.replaceState({}, null, url) }}

我們可以看到當瀏覽器后退的時候, history 模式會觸發 popstate 事件,這個時候是通過 state 狀態去獲取 path 的,那么 state 狀態從哪里來呢,答案是從 window.history 對象的 pushState 和 replaceState 而來,這兩個方法正好可以用來實現 router 的 push 方法和 replace 方法,我們看一下這里它們的實現:

Router.prototype.push = (options) => { this.history.current = options.path; if (this.mode === 'history') { history.pushState({  path: options.path }, null, options.path); } else if (this.mode === 'hash') { location.hash = options.path; } this.route.params = { ...options.params }}Router.prototype.replace = (options) => { this.history.current = options.path; if (this.mode === 'history') { history.replaceState({  path: options.path }, null, options.path); } else if (this.mode === 'hash') { location.replace(`#${options.path}`) } this.route.params = { ...options.params }}

pushState 和 replaceState 能夠實現改變url的值但不引起頁面刷新,從而不會導致新請求發生, pushState 會生成一條歷史記錄而 replaceState 不會,后者只是替換當前url。在這兩個方法執行的時候將 path 存入 state ,這就使得 popstate 觸發的時候可以拿到路徑從而觸發組件渲染了。我們在組件內按照如下方式調用,會將 params 寫入 router 實例的 route 屬性中,從而在跳轉后的組件 B 內通過 this.$route.params 可以訪問到傳參。

this.$router.push({ path: '/b', params: {  id: 55 } });

router-link實現

router-view 的實現很簡單,前面已經說過。最后,我們來看一下 router-link 的實現,先放上代碼:

Vue.component('router-link', {  props: {  to: String,  tag: String, }, render(h) {  let mode = this._self.$router.mode;  let tag = this.tag || 'a';  let routerHistory = this._self.$router.history;  return h(tag, {  attrs: tag === 'a' ? {   href: mode === 'hash' ? '#' + this.to : this.to,  } : {},  on: {   click: (e) => {   if (this.to === routerHistory.current) {    e.preventDefault();    return;   }   routerHistory.current = this.to;   switch (mode) {    case 'hash':    if (tag === 'a') return;    location.hash = this.to;    break;    case 'history':    history.pushState({     path: this.to    }, null, this.to);    break;    default:   }   e.preventDefault();   }  },  style: {   cursor: 'pointer'  }  }, this.$slots.default) } })

router-link 可以接受兩個屬性, to 表示要跳轉的路由路徑, tag 表示 router-link 要渲染的標簽名,默認為標簽。如果是 a 標簽,我們為其添加一個 href 屬性。我們給標簽綁定 click 事件,如果檢測到本次跳轉為當前路由的話什么都不做直接返回,并且阻止默認行為,否者根據 to 更換路由。 hash 模式下并且是 a 標簽時候可以直接利用瀏覽器的默認行為完成url上 hash 的替換,否者重新為 location.hash 賦值。 history 模式下則利用 pushState 去更新url。

以上實現就是一個簡單的vue-router,完整代碼參見vue-router-simple 。

總結

以上所述是小編給大家介紹的簡化版的vue-router實現思路詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产亚洲精品成人av久久ww| 2019亚洲男人天堂| 91精品久久久久久久久| 亚洲精品成人网| 日韩av手机在线观看| 国产亚洲视频中文字幕视频| 最近2019年好看中文字幕视频| 欧美富婆性猛交| 91国偷自产一区二区三区的观看方式| 国产乱人伦真实精品视频| 中文字幕在线亚洲| 视频在线一区二区| 亚洲欧美制服丝袜| 亚洲性夜色噜噜噜7777| 国产香蕉97碰碰久久人人| 国产精品免费观看在线| 亚洲性生活视频在线观看| 97精品视频在线播放| 国产精品美腿一区在线看| 96pao国产成视频永久免费| 欧美在线视频一区二区| 欧美日韩亚洲高清| 亚洲精品久久久久国产| www.xxxx精品| 久久久久国产一区二区三区| 秋霞成人午夜鲁丝一区二区三区| 久久久人成影片一区二区三区| 日韩av电影在线免费播放| 欧美刺激性大交免费视频| 在线观看中文字幕亚洲| 日韩国产高清污视频在线观看| 国产亚洲精品综合一区91| 久久精彩免费视频| 中文字幕综合在线| 日韩美女av在线| 欧美性猛交xxxx乱大交| 亚洲无av在线中文字幕| 亚洲成人久久电影| 日韩av手机在线| 91网站在线看| 色综合91久久精品中文字幕| 91嫩草在线视频| 日韩美女在线播放| 亚洲性生活视频在线观看| 成人h视频在线观看播放| 亚洲一区二区久久| 欧美电影免费看| 亚洲国产日韩欧美综合久久| 欧美国产亚洲精品久久久8v| 性欧美激情精品| 中文字幕在线观看日韩| 久久色精品视频| 韩国19禁主播vip福利视频| 日韩精品在线看| 欧美日韩一区免费| 97精品一区二区视频在线观看| 久热精品视频在线| 在线看国产精品| 日韩在线欧美在线国产在线| 久久69精品久久久久久国产越南| 国产精品久久久久7777婷婷| 国产综合视频在线观看| 亚洲最大av网| 久久亚洲国产成人| 秋霞av国产精品一区| 欧美在线一区二区三区四| 中文字幕日韩在线观看| 国产精品免费看久久久香蕉| 亚洲午夜小视频| 国产成人在线亚洲欧美| 日本一区二三区好的精华液| 欧美在线视频a| 97在线视频国产| 国产中文字幕日韩| 日韩女优在线播放| 亚洲毛茸茸少妇高潮呻吟| 国产精品扒开腿做爽爽爽男男| 91超碰caoporn97人人| 欧美成人三级视频网站| 668精品在线视频| 精品少妇v888av| 欧美日韩国产123| 亚洲国产欧美一区| 国产日韩在线亚洲字幕中文| 亚洲精品一二区| 欧美国产日韩一区二区| 亚洲天堂影视av| 中文字幕视频在线免费欧美日韩综合在线看| 欧美成人免费视频| 欧美大片第1页| 色多多国产成人永久免费网站| 亚洲欧美国产日韩天堂区| 国产精品福利网| 国产精品久久久久久搜索| 成人在线免费观看视视频| 成人黄在线观看| 日韩精品www| 国产精品色视频| 日韩精品日韩在线观看| 欧美极品少妇与黑人| 91国偷自产一区二区三区的观看方式| 人人爽久久涩噜噜噜网站| 中文欧美在线视频| 国语自产精品视频在线看一大j8| 九色成人免费视频| 国产精品91在线| 亚洲精品国产免费| 久久久999精品免费| 91精品啪aⅴ在线观看国产| 亚洲欧洲在线免费| 久久夜精品va视频免费观看| 久久全球大尺度高清视频| 欧美国产一区二区三区| 欧美性在线观看| 一区二区三区回区在观看免费视频| 中文字幕亚洲精品| 日本一区二区三区在线播放| 亚洲欧美国产一本综合首页| 日韩av一区在线观看| 国产精品视频网址| 久久69精品久久久久久久电影好| 久久久噜噜噜久久中文字免| 亚洲午夜久久久久久久| 精品动漫一区二区三区| 亚洲日本aⅴ片在线观看香蕉| 亚洲视频第一页| 亚洲天堂网站在线观看视频| 美女av一区二区三区| 成人欧美一区二区三区黑人孕妇| 日本久久亚洲电影| 亚洲午夜精品久久久久久久久久久久| 久久亚洲国产精品成人av秋霞| 亚洲欧美日韩爽爽影院| 欧美日韩国产精品| 亚洲视频一区二区| 狠狠色狠狠色综合日日五| 日韩视频欧美视频| 欧美激情视频在线| 欧美亚洲激情视频| 精品国产一区久久久| 欧美日韩亚洲精品内裤| 在线观看久久av| 欧美激情a∨在线视频播放| 精品亚洲国产视频| 欧美高清视频在线观看| 最新69国产成人精品视频免费| 色妞欧美日韩在线| 日韩av123| 欧美精品亚州精品| 国产亚洲精品高潮| 2019中文字幕免费视频| 在线日韩中文字幕| 在线播放日韩欧美| 日韩av成人在线观看| 国产精品视频久久久久| 欧美人与物videos| 欧美性猛交99久久久久99按摩| 国产成人精品免高潮在线观看| 日本欧美在线视频| 国产精品一区二区久久精品| 欧美一级免费看| 亚洲欧美成人精品| 日韩中文有码在线视频| 在线看日韩av|