vue-router

????窗外的綠色滿滿撞入我的眼睛,柳絮隔著紗窗熱情的邀舞完疫,可惜我不能出去。好了债蓝,這些都是題外話壳鹤。

一、實(shí)現(xiàn)原理

vue-router 三種運(yùn)行模式:

  1. hash: 使用 URL hash 值("#")來作路由饰迹。默認(rèn)模式芳誓。
  2. history: 依賴 HTML5 History API 和服務(wù)器配置。
  3. abstract: 支持所有 JavaScript 運(yùn)行環(huán)境啊鸭,如 Node.js 服務(wù)器端锹淌。
  • ps:在vue-router中是通過mode這一參數(shù)控制路由的實(shí)現(xiàn)模式的:
var router = new VueRouter({
  mode: 'history', //默認(rèn)是hash
  routes: [
    ...
  ]
});
hash模式
HTML5History
Hash模式
  • hash(“#”)符號(hào)的本來作用是加在URL中指示網(wǎng)頁中的位置;
  • hash雖然出現(xiàn)在URL中,但不會(huì)被包括在HTTP請(qǐng)求中莉掂。它是用來指導(dǎo)瀏覽器動(dòng)作的葛圃,對(duì)服務(wù)器端完全無用,因此憎妙,改變hash不會(huì)重新加載頁面;
  • hash模式下通過hashchange方法可以監(jiān)聽url中hash的變化库正,來實(shí)現(xiàn)更新頁面部分內(nèi)容的操作
window.addEventListener("hashchange", function(){}, false)
  • 每一次改變hash,都會(huì)在瀏覽器的訪問歷史中增加一個(gè)記錄(利用HashHistory.push()HashHistory.replace())厘唾,可以實(shí)現(xiàn)瀏覽器的前進(jìn)和后退功能褥符。

1. HashHistory.push()

push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.transitionTo(location, route => {
    pushHash(route.fullPath)
    onComplete && onComplete(route)
  }, onAbort)
}

function pushHash (path) {
  window.location.hash = path
}
  • 由此可見,push()方法最主要的是對(duì)window的hash進(jìn)行了直接賦值:
window.location.hash = route.fullPath 
  • hash的改變會(huì)自動(dòng)添加到瀏覽器的訪問歷史記錄中抚垃。


    HashHistory.push().jpg

2. HashHistory.replace()

  • replace()方法與push()方法不同之處在于喷楣,它并不是將新路由添加到瀏覽器訪問歷史的棧頂趟大,而是替換掉當(dāng)前的路由:
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.transitionTo(location, route => {
    replaceHash(route.fullPath)
    onComplete && onComplete(route)
  }, onAbort)
}
  
function replaceHash (path) {
  const i = window.location.href.indexOf('#')
  window.location.replace(
    window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
  )
}
  • 可以看出,它與push()的實(shí)現(xiàn)結(jié)構(gòu)上基本相似铣焊,不同點(diǎn)在于它不是直接對(duì)window.location.hash進(jìn)行賦值逊朽,而是調(diào)用window.location.replace方法將路由進(jìn)行替換。

總得來說就是初始化vueRouter的時(shí)候曲伊,建立路由和組件的映射關(guān)系叽讳,監(jiān)聽hashChange事件來更新路由,進(jìn)而渲染視圖


HTML5History模式
  • 通過 window.history.pushState(stateObject, title, URL)window.history.replaceState(stateObject, title, URL)修改瀏覽器地址坟募,觸發(fā)更新;
  • 通過監(jiān)聽popstate事件監(jiān)聽瀏覽器前進(jìn)或者后退,觸發(fā)更新;
  1. stateObject: 當(dāng)瀏覽器跳轉(zhuǎn)到新的狀態(tài)時(shí)岛蚤,將觸發(fā)popState事件,該事件將攜帶這個(gè)stateObject參數(shù)的副本懈糯。
  2. title: 所添加記錄的標(biāo)題(暫時(shí)沒有用處)涤妒。
  3. URL: 所添加記錄的URL。
    這兩個(gè)方法有個(gè)共同的特點(diǎn):當(dāng)調(diào)用他們修改瀏覽器歷史記錄棧后赚哗,雖然當(dāng)前URL改變了她紫,但瀏覽器不會(huì)立即發(fā)送請(qǐng)求該URL,這就為單頁應(yīng)用前端路由“更新視圖但不重新請(qǐng)求頁面 ”提供了基礎(chǔ)蜂奸。
    history.pushState

    history.replaceState()
兩種模式比較
hash模式 history模式
于多了一個(gè)#犁苏,所以u(píng)rl整體上不夠美觀 當(dāng)用戶刷新或直接輸入地址時(shí)會(huì)向服務(wù)器發(fā)送一個(gè)請(qǐng)求,所以需要服務(wù)端同學(xué)進(jìn)行支持扩所,
將路由都重定向到根路由
hash只可修改#后面的部分围详,故只可設(shè)置與當(dāng)前同文檔的URL pushState設(shè)置的新URL可以是與當(dāng)前URL同源的任意URL
hash設(shè)置的新值必須與原來不一樣才會(huì)觸發(fā)記錄添加到棧中 pushState設(shè)置的新URL可以與當(dāng)前URL一模一樣,這樣也會(huì)把記錄添加到棧中
hash只可添加短字符串 pushState通過stateObject可以添加任意類型的數(shù)據(jù)到記錄中
pushState可額外設(shè)置title屬性供后續(xù)使用


vue-router源碼分析:

install.js 分析

  1. 首先會(huì)對(duì)重復(fù)安裝進(jìn)行過濾
  2. 全局混入beforeCreatedestroyed 生命鉤子祖屏,為每個(gè)Vue實(shí)例設(shè)置 _routerRoot屬性助赞,并為根實(shí)例設(shè)置_router屬性
  3. 調(diào)用Vue中定義的defineReactive對(duì)_route進(jìn)行劫持,其實(shí)是執(zhí)行的依賴收集的過程袁勺,執(zhí)行_route的get就會(huì)對(duì)當(dāng)前的組件進(jìn)行依賴收集雹食,如果對(duì)_route進(jìn)行重新賦值觸發(fā)setter就會(huì)使收集的組件重新渲染,這里也是路由重新渲染的核心所在
Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) { // 設(shè)置根路由-根組件實(shí)例
        this._routerRoot = this
        this._router = this.$options.router
        this._router.init(this)
        // 定義響應(yīng)式的 _route 對(duì)象
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else { // 非根組件設(shè)置
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })
  1. 為Vue原型對(duì)象定義router和router和router和route屬性期丰,并對(duì)兩個(gè)屬性進(jìn)行了劫持群叶,使我們可以直接通過Vue對(duì)象實(shí)例訪問到
  2. 全局注冊(cè)了Routerview和RouterLink兩個(gè)組件,所以我們才可以在任何地方使用這兩個(gè)組件钝荡,這兩個(gè)組件的內(nèi)容我們稍后分析
 Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })



vue-router工作流程:

  1. 安裝 vue-router 插件(參考 install.js分析)
  2. new Router 實(shí)例
  3. 根實(shí)例創(chuàng)建之前街立,執(zhí)行init方法,初始化路由
  4. 執(zhí)行transitionTo方法埠通,同時(shí)hash模式下對(duì)瀏覽器hashChange事件進(jìn)行了監(jiān)聽赎离,執(zhí)行history.listen方法,將對(duì)_route重新賦值的函數(shù)賦給History實(shí)例的callback端辱,當(dāng)路由改變時(shí)對(duì)_route進(jìn)行重新賦值從而觸發(fā)組件更新
  5. transitionTo方法根據(jù)傳入的路徑從我們定義的所有路由中匹配到對(duì)應(yīng)路由梁剔,然后執(zhí)行confirmTransition
  6. confirmTransition首先會(huì)有重復(fù)路由的判斷虽画。如果進(jìn)入相同的路由,直接調(diào)用abort回調(diào)函數(shù)荣病,函數(shù)退出码撰,不會(huì)執(zhí)行后面的各組件的鉤子函數(shù),這也是為什么我們重復(fù)進(jìn)入相同路由不會(huì)觸發(fā)組建的重新渲染也不會(huì)觸發(fā)路由的各種鉤子函數(shù)众雷;如果判斷不是相同路由灸拍,就會(huì)執(zhí)行各組件的鉤子函數(shù)
  7. 按順序執(zhí)行好導(dǎo)航守衛(wèi)后做祝,就會(huì)執(zhí)行傳入的成功的回調(diào)函數(shù),從而對(duì)_route進(jìn)行賦值砾省,觸發(fā)setter,從而使組件重新渲染


二混槐、使用

1.路由文件的定義

/*
* 路由器模塊
* */
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter);

import Home from '../views/home'
import Messages from '../views/messages'
import MessagesDetail from '../views/messagesDetail'


function loadView(view) {
  return () => import(`@/views/${view}.vue`)
}

var router = new VueRouter({
  mode: 'history',
  routes: [
    {
      name: 'app',
      path: '/',
      // component: App,
      redirect: '/home',
    },
    {
      name: 'about',
      path: '/about',
      component: loadView('about'),
      children: [
        {
          name: 'news',
          path: '/about/news',
          component: loadView('news')
        },
        {
          name: 'messages',
          path: '/about/messages',
          component: Messages,
          children: [
            {
              path: '/about/messages/:id',
              component: MessagesDetail,
              //主要用于寫某個(gè)指定路由跳轉(zhuǎn)時(shí)需要執(zhí)行的邏輯
              beforeEnter: (to, from, next) => {
                console.log('beforeEnter-to: ', to)
                console.log('beforeEnter-from: ', from)
                next();
              },
              afterEnter: (to, from, next) => {
                console.log('---afterEnter-to: ', to)
                console.log('---afterEnter-from: ', from)
                next()
              }
            },
          ],
          /*
          *某個(gè)路由的路由鉤子函數(shù)
          */
          //主要用于寫某個(gè)指定路由跳轉(zhuǎn)時(shí)需要執(zhí)行的邏輯
          beforeEnter: (to, from, next) => {
            console.log('beforeEnter-to: ', to)
            console.log('beforeEnter-from: ', from)
            next();
          },
          afterEnter: (to, from, next) => {
            console.log('-----afterEnter-to: ', to)
            console.log('-----afterEnter-from: ', from)
            next()
          }
        },
      ]
    },
    {
      name: 'home',
      path: '/home',
      component: Home,
    }
  ]
});
 /*
  *全局路由鉤子
  */
//這類鉤子主要作用于全局,一般用來判斷權(quán)限,以及以及頁面丟失時(shí)候需要執(zhí)行的操作
router.beforeEach((to, from, next) => {
  console.log('beforeEach-to: ', to)
  console.log('beforeEach-from: ', from)
  next();
})
router.afterEach((to, from, next) => {
  console.log('afterEach-to: ', to)
  console.log('afterEach-from: ', from)
})
export default router

vue-router的結(jié)構(gòu).jpg
  • 另:需將路由全局注入main.js
import Vue from 'vue'
import App from './App.vue'
import router from '@/router'
import less from 'less'
Vue.use(less)

require('./assets/style/iconfont.css')

Vue.config.productionTip = false; //作用是阻止 vue 在啟動(dòng)時(shí)生成生產(chǎn)提示
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

三编兄、有關(guān)知識(shí)點(diǎn)

1. router 與 route

先上一張圖來:


routerPKroute.jpg
  • 由此可見,router是VueRouter的一個(gè)對(duì)象声登,通過Vue.use(VueRouter)和VueRouter的構(gòu)造函數(shù)得到的實(shí)力對(duì)象狠鸳,該對(duì)象是一個(gè)全局對(duì)象,包含了許多關(guān)鍵對(duì)象與屬性悯嗓,例如:history:
methods: {
        go(num){
          if (num === 1){
            this.$router.replace({ name: 'news' })
          }else if (num === 2){
            this.$router.push({ name: 'messages' })
          }
        }
      },
  • $route是一個(gè)當(dāng)前路由的路由對(duì)象件舵,每個(gè)路由都會(huì)有一個(gè)route對(duì)象,是一個(gè)局部對(duì)象脯厨,可獲取對(duì)應(yīng)的name,path,params,query等
<h3>ID:{{$route.params.id}}</h3>

2. 路由鉤子(分三類)

在使用那部分已經(jīng)貼出完整的代碼铅祸,以及應(yīng)用場(chǎng)景,故此處只做簡(jiǎn)單的列舉:

  • 全局路由鉤子
    ——這類鉤子主要作用于全局,一般用來判斷權(quán)限,以及以及頁面丟失時(shí)候需要執(zhí)行的操作
router.beforeEach((to, from, next) => {
  next();
})
router.afterEach((to, from, next) => {
})
  • 某個(gè)路由獨(dú)有的路由鉤子
    ——主要用于寫某個(gè)指定路由跳轉(zhuǎn)時(shí)需要執(zhí)行的邏輯
beforeEnter: (to, from, next) => {
     next();
},
afterEnter: (to, from, next) => {
    next()
}
  • 路由組件內(nèi)的路由鉤子
export default {
    name: "messages",
    data() {
      return {}
    },
    beforeRouteEnter (to, from, next) {
      // 在渲染該組件的對(duì)應(yīng)路由被 confirm 前調(diào)用
      // 不能獲取組件實(shí)例 `this`
      // 因?yàn)楫?dāng)鉤子執(zhí)行前合武,組件實(shí)例還沒被創(chuàng)建
      next();
    },
    beforeRouteUpdate (to, from, next) {
      // 在當(dāng)前路由改變临梗,但是該組件被復(fù)用時(shí)調(diào)用
      // 舉例來說,對(duì)于一個(gè)帶有動(dòng)態(tài)參數(shù)的路徑 /foo/:id稼跳,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時(shí)候盟庞,
      // 由于會(huì)渲染同樣的 Foo 組件,因此組件實(shí)例會(huì)被復(fù)用汤善。而這個(gè)鉤子就會(huì)在這個(gè)情況下被調(diào)用什猖。
      // 可以訪問組件實(shí)例 `this`
      next()
    },
    beforeRouteLeave (to, from, next) {
      // 導(dǎo)航離開該組件的對(duì)應(yīng)路由時(shí)調(diào)用
      // 可以訪問組件實(shí)例 `this`
      next()
    }
  }

3. 子路由

<router-link :to="`/about/messages/${v.id}`" class="">{{v.content}}</router-link>
<keep-alive>
      <router-view></router-view>
</keep-alive>
 .router-link-active{
        color: burlywood !important;
  }
  • 在動(dòng)態(tài)組件上使用 keep-alive
    ——當(dāng)在多個(gè)組件之間切換的時(shí)候,有時(shí)會(huì)想保持這些組件的狀態(tài)红淡,以避免反復(fù)重渲染導(dǎo)致的性能問題不狮。
    (https://cn.vuejs.org/v2/guide/components-dynamic-async.html?_sw-precache=3b921049bd7cca2444e9efa512a9d9f5#%E5%9C%A8%E5%8A%A8%E6%80%81%E7%BB%84%E4%BB%B6%E4%B8%8A%E4%BD%BF%E7%94%A8-keep-alive "在動(dòng)態(tài)組件上使用 keep-alive")
  • 當(dāng)組件在 <keep-alive> 內(nèi)被切換,它的 activateddeactivated這兩個(gè)生命周期鉤子函數(shù)將會(huì)被對(duì)應(yīng)執(zhí)行锉屈。
  • activated鉤子函數(shù)
    ——keep-alive 組件激活時(shí)調(diào)用荤傲。該鉤子在服務(wù)器端渲染期間不被調(diào)用。
  • deactivated鉤子函數(shù)
    ——實(shí)例銷毀之前調(diào)用颈渊。在這一步遂黍,實(shí)例仍然完全可用终佛。該鉤子在服務(wù)器端渲染期間不被調(diào)用。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雾家,一起剝皮案震驚了整個(gè)濱河市铃彰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芯咧,老刑警劉巖牙捉,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異敬飒,居然都是意外死亡邪铲,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門无拗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來带到,“玉大人,你說我怎么就攤上這事英染±咳牵” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵四康,是天一觀的道長(zhǎng)搪搏。 經(jīng)常有香客問我,道長(zhǎng)闪金,這世上最難降的妖魔是什么疯溺? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮毕泌,結(jié)果婚禮上喝检,老公的妹妹穿的比我還像新娘。我一直安慰自己撼泛,他們只是感情好挠说,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愿题,像睡著了一般损俭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上潘酗,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天杆兵,我揣著相機(jī)與錄音,去河邊找鬼仔夺。 笑死琐脏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播日裙,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼吹艇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了昂拂?” 一聲冷哼從身側(cè)響起受神,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎格侯,沒想到半個(gè)月后鼻听,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡联四,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年撑碴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碎连。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灰羽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鱼辙,到底是詐尸還是另有隱情,我是刑警寧澤玫镐,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布倒戏,位于F島的核電站,受9級(jí)特大地震影響恐似,放射性物質(zhì)發(fā)生泄漏杜跷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一矫夷、第九天 我趴在偏房一處隱蔽的房頂上張望葛闷。 院中可真熱鬧,春花似錦双藕、人聲如沸淑趾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扣泊。三九已至,卻和暖如春嘶摊,著一層夾襖步出監(jiān)牢的瞬間延蟹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工叶堆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阱飘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像沥匈,于是被迫代替她去往敵國(guó)和親果录。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 用Vue.js + vue-router創(chuàng)建單頁應(yīng)用咐熙,是非常簡(jiǎn)單的弱恒,基本是這樣的: 組件 → 路由 → 渲染地方 ...
    阿go閱讀 1,399評(píng)論 0 0
  • 介紹 vue-router是一個(gè)vue插件。其實(shí)質(zhì)是在location.hash棋恼、location.replace...
    AmazRan閱讀 1,554評(píng)論 0 6
  • 隨著前端應(yīng)用的業(yè)務(wù)功能起來越復(fù)雜返弹,用戶對(duì)于使用體驗(yàn)的要求越來越高,單面(SPA)成為前端應(yīng)用的主流形式爪飘。大型單頁應(yīng)...
    bayi_lzp閱讀 5,775評(píng)論 0 2
  • 編程式導(dǎo)航 1 .用在可復(fù)用的路由視圖里面义起,比如所有的需要跳轉(zhuǎn)到一個(gè)文章具體內(nèi)容的路由,每一次跳轉(zhuǎn)到新路由的時(shí)候师崎,...
    skoll閱讀 652評(píng)論 0 1
  • 這是我第5篇簡(jiǎn)書犁罩。 ??由于Vue在開發(fā)時(shí)對(duì)路由支持的不足齐蔽,于是官方補(bǔ)充了vue-router插件。vue的單頁面...
    東西里閱讀 45,171評(píng)論 20 212