前端入門之(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群連結: