為什么會有 Vuex 這個東西 ?
現代前端框架主要解決的是 事件 -> 狀態 -> UI 將傳統前端在兩個過程的代碼剝離出來,變得更加容易維護;
vue的聲明式渲染,解決了 狀態 和 UI 的同步問題,從而使我們不需要由於狀態發生改變去寫大量的命令式改變 dom 的代碼。
而類似於 vuex 這類狀態管理的庫,則解決了 事件 -> 狀態 這個過程的維護問題。這類庫所做的事情就是管理從 事件源映射到狀態變化 這個過程(將這個映射過程從視圖組件中剝離出來,組織好這一部分的代碼,在組件外部進行狀態的管理)
具體表現就是一個全局的數據中心 store 配置,每個組件進行更新的時候就通知數據中心,數據中心改變后,再去觸發每個調用它的組件進行更新(這種更新是響應式的);幾個核心概念就是 mutations
里的方法可以直接 mutate
store 中的狀態,並且 mutation
過程必須同步的,需要通過 commit
去觸發;而 actions
則允許異步的操作,通過 commit
去觸發 mutation
,達到間接修改 store
的目的,action
本身需要通過 disptch
去觸發。
Vuex與全局對象的區別
其實,vuex
與全局對象有一定的共同之處,那就是狀態會被全局共享,無論是嵌套多少組件…
每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有以下兩點不同:
- Vuex 的狀態存儲是 響應式 的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到 高效更新。
- 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用。
Vuex常見的應用場景
管理狀態與共享狀態
應用簡單時,可以使用 prop
與 event
來完成 父子組件 的通信。使用 global event bus(event bus)
來實現 簡單的非父子組件之間的跨組件 通信。但對於 多層級組件嵌套 等較為復雜的場景,使用 vuex
能更好地應對。(使用 event bus 的缺點是當狀態較復雜,調用組件非常多,要挨個依次通知所有組件更新;每個組件對這個組件進行的狀態更新都要通知監聽該事件的所有組件;這樣會變得非常復雜)(你通知我, 我通知你, 你通知他, 他通知我 ... 大家之間是直接相互作用的)
vuex
狀態管理模型,擁有一個統一的數據中心Store,Store用來維護狀態數據;每個組件進行更新的時候就通知數據中心,數據中心改變后,再去統一觸發每一個調用它的組件進行更新(相當於由數據中心來統籌狀態變化以及狀態變化的分發,而不是由每個vue
組件直接去操作state) (有點類似於中介者模式, 大家把所有各種類型的信息都反饋給中介, 然后再由中介分發給訂閱了某個類型信息的用戶)

vuex
是通過將 state
作為數據中心,各個組件 共享 state 來實現跨組件通信的, 此時的數據完全獨立於組件。(點擊不同模塊的操作,不再需要去發送亂七八糟的事件, 只是去調用事件中心里的 mutation
動作, 從而實現模塊間共享狀態的功能)
需要構建一個中大型單頁應用時,很可能會考慮如何更好地 在組件外部 管理狀態
vuex更多地用於解決 跨組件通信 (多層嵌套組件之間的通信問題)以及作為 數據中心集中式存儲數據 (管理應用中錯綜復雜的狀態關系)
vuex 作為數據存儲中心
vuex
的 state
在單頁應用的開發中本身具有一個 數據庫 的作用,可以將組件用到的數據存儲在 state
中,並在 actions
中封裝數據讀寫的邏輯。目前主要有兩種數據會使用 vuex
進行管理
- 組件之間 全局共享 的數據
- 通過后端異步請求的數據
實際項目開發中更多的是用到第二種,即把通過后端異步請求的數據都納入 vuex
狀態管理,在 actions
中封裝數據的增刪改查等邏輯,這樣可以在一定程度上對前端的邏輯代碼進行分層,使組件中的代碼更多地關注頁面交互與數據渲染等 視圖層 的邏輯,而異步請求與狀態數據的持久化等則交由 vuex
管理
一般全局數據,會使用到 vuex
來管理。比如 用戶數據,系統數據 等,這些數據很多組件中都會使用,我們當然可以每次使用的時候都去請求,但是出於程序員的“潔癖”、“摳”等等優點,還是希望一次請求,到處使用。
這時候很自然的想到存儲在 localStorage
中,但是有個問題是,這些數據可能會變,如果沒能及時 同步 的話,就會用到不正確的數據,即使做了數據同步,但是 localStorage
中的數據不是響應式的,不能自動更新使用到這些數據的地方。這時候就想要開始使用 vuex
了。
Vuex代碼的組織方式
與 vue-router
類似,有非模塊化寫法與模塊化寫法(其實無論何種寫法本質上是一樣的,目的就是導出一份 router
或者 store
的配置數據)
以管理 count
與 用戶信息 userinfo
為例,介紹 vuex
代碼的組織方式
核心概念 state,getter,mutation,action,module
vuex
需要遵守的規則
應用層級的狀態應該集中到 單個 store
對象中
mutation
是直接操作state的方法(唯一能改變狀態的方法),過程要求必須同步
action
通過commit
去觸發mutation
,從而間接修改狀態,優點是允許異步邏輯,
非模塊化寫法
// 1.安裝 vuex
// 2.在入口文件中引入
// main.js
import Vuex from 'vuex'
// 3.Vue使用 vuex 插件
Vue.use(Vuex)
// 4.生成數據管理中心 store
const store = new Vuex.Store({
state: {
userinfo: null, // 需要給定初始值
count: 0
},
// 直接通過mutation方法來mutate操作state; 只能以同步的方式; mutation方法需要通過commit來觸發
mutations: {
userinfo: function (state, payload) {
state.userinfo = options
localStorage.userinfo = JSON.stringify(state.userinfo)
},
increment: function (state, payload) {
state.count += payload.amount
}
},
// 通過commit觸發mutations里的mutation方法, 以此間接修改 state; 允許異步操作;action方法需要通過dispatch來觸發
actions: {
increment: function (context, payload) {
context.commit('increment', payload)
},
incrementAsync: function (context) {
// 異步請求數據
setTimeout(() => {
var amount = 10; // 模擬異步請求得到數據
context.commit('increment', { amount })
}, 1000)
},
async userinfo (context) {
let response = await getUserInfo() // 異步請求后端數據 方法需要 import
if (response.ok) {
let json = await response.json()
context.commit('userinfo', json)
}
}
},
getters: {// 處理、過濾數據
}
})
// 5.通過 store 配置參數注入狀態管理,從而任何組件可通過 this.$store 訪問狀態中心
new Vue({
el: '#app',
store, // (*)
render: function (h) {
return h(App)
}
})
模塊化寫法
// 1.安裝vuex
// 2.引入
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
// import ...
// 3.Vue使用 vuex 插件
Vue.use(Vuex)
// 4.分模塊生成數據管理中心
// 配置 userinfo 狀態模塊 (還能再單獨拆出一個文件,然后import進來)
const moduleA = {
state: {
userinfo: null // 需要初始化響應式數據
},
mutations: {
userinfo: function (state, options) {
state.userinfo = options
localstorage.userinfo = JSON.stringify(state.userinfo)
}
},
actions: {
async userinfo (context) {
async userinfo (context) {
let response = await response.json()
if (response.ok) {
let json = await response.json()
context.commit('userinfo', json)
}
}
},
getters: {// 處理、過濾數據
}
}
}
// 配置 count 狀態模塊 (還能再單獨拆出一個文件,然后import進來)
const moduleB = {
state: {
count: 1,
},
mutations: {
increment:function (state, payload) {
state.count += payload
},
},
actions: {
increment: function (context, payload) {
context.comit('increment', payload)
},
incrementAsync: function (context) {
// 異步請求數據
setTimeout(() => {
var amount = 10; // 模擬異步請求得到數據
context.commit('increment', { amount })
}, 1000)
},
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
// 5.導出一份 store 的配置
export default new Vuex.Store({
modules: { // 這里與非模塊化寫法有點不一樣;原來整個對象是一份配置...
a: moduleA,
b: moduleB,
}
})
// store.state.a // -> moduleA 的狀態
// store.state.b // -> moduleB 的狀態
// 6.在根腳本 main.js 中引入 store 配置
import store from './store'
...
// 並通過 store 配置參數注入狀態管理,從而任何組件可通過 this.$store 訪問狀態中心
new Vue({
el: '#app',
router,
store,
render: function (h) {
return h(App);
}
});
在不同組件中使用或操作狀態
// 根組件
// App.vue
// 在生命周期中觸發全局共享數據的獲取
...
mounted () {
if (!this.$store.state.userinfo) { // this.$store.state.a.userinfo 模塊化寫法的話
this.$store.dispatch('userinfo')
}
}
// 使用 state 的數據
computed: {
userinfo () {
return this.$store.state.userinfo
// return this.$store.state.a.userinfo // 模塊化寫法的話
},
count () {
return this.$store.state.count
// return this.$store.state.b.count // 模塊化寫法的話
}
}
// 子組件
// NavBar.vue
...
// 改變 state 的數據
methods: {
// 使用 commit 觸發 mutations 中的 mutation 方法,直接修改 state 中的數據
addOne () {
this.$store.commit('increment', { amount: this.price })
},
// 使用 dispatch 觸發 actions 中的 action 方法;異步修改 state 中的數據
addTenAsync () {
this.$store.dispatch('incrementAsync')
}
}
// 使用 state 的數據
computed: {
userinfo () {
return this.$store.state.userinfo
// return this.$store.state.a.userinfo // 模塊化寫法的話
}
}
// 路由頁面組件
// Manage.vue
...
// 改變 state 的數據
methods: {
addTwo () {
this.$store.commit('increment', { amount: this.price })
}
}
// 使用 state 的數據
computed: {
count () {
return this.$store.state.count
// return this.$store.state.b.count // 模塊化寫法的話
}
},
總之,使用了 vuex
來管理狀態,點擊不同模塊的操作,不再需要去發送亂七八糟的事件, 只是去調用事件中心里的 mutation
動作, 從而實現模塊間共享狀態的功能;修改一處,全局共享(無論是組件還是路由頁面組件都能同步)
最后,再看看 vuex
官網的這張說明圖,是不是更加清晰了呢!

-項目地址- vuex-demo