1. 程式人生 > >前端入門之(vuex-router-sync解析)

前端入門之(vuex-router-sync解析)

前言:vue全家桶的內容我們已經研究過了vuex、vue-router,有興趣的童鞋可以去看看我之前的兩個系列的文章vuex原始碼解析一vue-router全解析一,之前結合專案分析vuex的時候,當我們需要在vuex的action中處理路由跳轉的時候,沒認識vuex-router-sync的時候,我一般都是直接拿到router物件,然後稍微封裝了一下進行跳轉的,哈哈~~ 在看vue的github官網的時候不小心看到了vuex-router-sync,哈哈!! 才知道原來官方已經有一個工具把store跟router連線起來了,翻了一下vuex-router-sync的原始碼才知道原來還可以這麼操作?於是打算把自己所理解的內容記錄下來,歡迎指正,大牛勿噴!!!

我們直接執行:

npm install vuex-router-sync --save

然後配置就很簡單了:

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

import {sync} from 'vuex-router-sync'
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  ddd: {name: 'yasin'},
  render(h) {
    return h(App)
  }
})

只需要通過sync方法把store跟router連線起來就可以了
然後我們在vue元件的mounted方法中列印一下route內容:

mounted(){
      console.log(this.$store.state.route)
    }

在這裡插入圖片描述

我們可以看到,在store的route物件其實就是我們router中的currentRoute物件(當前路由).

好啦~ 到這我們小夥伴可能要疑問啦,我直接用this.$router.route也可以拿到currentRoute物件啊,為啥還這麼複雜呢?這樣做又有啥好處呢?不急不急,我們直接看看它的原始碼來研究下它的功能哈~~

我們找到vuex-router-sync的原始碼:

exports.sync = function (store, router, options) {
  var moduleName = (options || {}).moduleName || 'route'

  store.registerModule(moduleName, {
    namespaced: true,
    state: cloneRoute(router.currentRoute),
    mutations: {
      'ROUTE_CHANGED': function ROUTE_CHANGED (state, transition) {
        store.state[moduleName] = cloneRoute(transition.to, transition.from)
      }
    }
  })

  var isTimeTraveling = false
  var currentPath

  // sync router on store change
  var storeUnwatch = store.watch(
    function (state) { return state[moduleName]; },
    function (route) {
      var fullPath = route.fullPath;
      if (fullPath === currentPath) {
        return
      }
      if (currentPath != null) {
        isTimeTraveling = true
        router.push(route)
      }
      currentPath = fullPath
    },
    { sync: true }
  )

  // sync store on router navigation
  var afterEachUnHook = router.afterEach(function (to, from) {
    if (isTimeTraveling) {
      isTimeTraveling = false
      return
    }
    currentPath = to.fullPath
    store.commit(moduleName + '/ROUTE_CHANGED', { to: to, from: from })
  })

  return function unsync () {
    // On unsync, remove router hook
    if (afterEachUnHook != null) {
      afterEachUnHook()
    }

    // On unsync, remove store watch
    if (storeUnwatch != null) {
      storeUnwatch()
    }

    // On unsync, unregister Module with store
    store.unregisterModule(moduleName)
  }
}

function cloneRoute (to, from) {
  var clone = {
    name: to.name,
    path: to.path,
    hash: to.hash,
    query: to.query,
    params: to.params,
    fullPath: to.fullPath,
    meta: to.meta
  }
  if (from) {
    clone.from = cloneRoute(from)
  }
  return Object.freeze(clone)
}


好吧,簡單的不能再簡單了,就70多行程式碼,這可是在github上好幾k的star的庫啊~~~ 小夥伴是不是跟我一樣驚訝呢? 小夥伴莫及哈,程式碼雖少,但其中的思路跟用法還是值得我們學習的,這也是我為啥寫這篇文章的原因了~ 廢話不多說了,我們開始帶著原始碼往下走.

首先是在我們的store中註冊了一個module,名字預設為“route”:

store.registerModule(moduleName, {
    namespaced: true,
    state: cloneRoute(router.currentRoute),
    mutations: {
      'ROUTE_CHANGED': function ROUTE_CHANGED (state, transition) {
        store.state[moduleName] = cloneRoute(transition.to, transition.from)
      }
    }
  })

module中提供了一個叫“ROUTE_CHANGED”的mutation處理方法,然後還把router物件中的currentRoute儲存在了state中,這也是我們為什麼能夠通過this.$store.state.route拿到currentRoute的原因.

然後就是監聽store中的route物件的變化了,當route發生變化並且當前路由名字不等於需要跳轉到路由的時候,直接通過router的push方法進行跳轉頁面:

 var storeUnwatch = store.watch(
    function (state) { return state[moduleName]; },
    function (route) {
      var fullPath = route.fullPath;
      if (fullPath === currentPath) {
        return
      }
      if (currentPath != null) {
        isTimeTraveling = true
        router.push(route)
      }
      currentPath = fullPath
    },
    { sync: true }
  )

store的watch方法我簡單說一下,watch跟我們vue中的watch是一個概念,也就是檢測某個屬性的變化,然後回撥.

最後通過router的全域性後置鉤子函式監聽當前路由物件,修改store中的當前state(當前路由物件):

 // sync store on router navigation
  var afterEachUnHook = router.afterEach(function (to, from) {
    if (isTimeTraveling) {
      isTimeTraveling = false
      return
    }
    currentPath = to.fullPath
    store.commit(moduleName + '/ROUTE_CHANGED', { to: to, from: from })
  })

好啦,整個庫的原始碼算是分析完畢了,小夥伴是不是還是很疑問呢? 這東西怎麼用呢? 我們接下來結合一個demo來用用它~

一般來說專案為了讓頁面跟資料邏輯分離出來,一般一些邏輯處理都在vuex的action中進行了,我就簡單結合之前的部落格中的demo操作了~

我們執行專案看到有一個a頁面:
在這裡插入圖片描述

<template>
  <div id="page-a-container">
    我是a頁面
  </div>
</template>
<script>
  export default {
    name: 'pageA',
    mounted(){
      console.log(this.$store.state.route)
    }
  }
</script>
<style scoped>
  #page-a-container{
    background-color: red;
    color: white;
    font-size: 24px;
    height: 100%;
  }
</style>

然後我們專案可能有這麼一個需求:“當點選a頁面的某個按鈕的時候,我們會調後臺介面,然後再跳轉某個具體的頁面~”

我們在a頁面放一個“登入”按鈕,然後點選去登入,登入成功跳轉到登入成功頁面:

page-a.vue:

<template>
  <div id="page-a-container">
    我是a頁面<br>
    <button @click="onClick">登入</button>
  </div>
</template>
<script>
  export default {
    name: 'pageA',
    mounted() {
      console.log(this.$store.state.route)
    },
    methods: {
      onClick() {
        this.$store.dispatch('login')
      }
    }
  }
</script>
<style scoped>
  #page-a-container {
    background-color: red;
    color: white;
    font-size: 24px;
    height: 100%;
  }
</style>

然後store中的action:

login({state, commit}) {
    setTimeout(() => {
      alert('登入成功')
      commit('route/ROUTE_CHANGED',{to: {path: '/b'}})
    }, 1000)
  }

我們直接模擬一下登入,然後跳轉到b頁面

在這裡插入圖片描述

好啦!! 我們vuex-router-sync就分析到這啦~~

歡迎入群,歡迎交流,qq群連結:
在這裡插入圖片描述