Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。(源於官網)vue
狀態的自我管理應該包含:vuex
Vuex 應用的核心就是 store(倉庫,能夠理解爲容器),store 包含着應用中的大部分狀態(state),用戶不能直接改變 store 中的狀態。改變 store 中的狀態的惟一途徑就是顯式地提交 (commit) mutation(源於官網)redux
/// index.js 入口文件
import Vue from 'vue';
import Vuex from 'vuex';
import main from '../component/main'
Vue.use(Vuex); // 要注意這裏須要啓用 Vuex
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
}) // 設置了一個 Store
let vm = new Vue({
el: '#app',
render: h => h(main),
store // 全局注入,全部組件都可使用這個 store
})
...
/// main.vue 子組件
<template>
<div>
{{ msg }} - {{ count }}
<button @click="incream">add count</button>
</div>
</template>
<script>
export default {
data () {
return {
msg: 'msg'
}
},
computed: {
count () {
return this.$store.state.count
}
},
methods: {
incream () {
this.$store.commit('increment');
}
}
}
</script>
/// 注入的store,能夠經過 this.$store 獲取
複製代碼
Vuex 使用單一狀態樹,用一個對象就包含了所有的應用層級狀態。單一狀態樹讓咱們可以直接地定位任一特定的狀態片斷,在調試的過程當中也能輕易地取得整個當前應用狀態的快照。(源於官網)數組
從 store 實例中讀取 state 最簡單的方法就是在 computed 中返回某個狀態(源於官網)緩存
若是把數據獲取置於data中,初始化時能夠正常獲取,但更新後並不能同步更新bash
mapState 輔助函數幫助咱們生成計算屬性(源於官網)app
import { mapState } from 'vuex';
...
computed: mapState({
count (state) { // 在這個方法中能夠直接使用state,不用使用 this.$store.state
return state.count
},
count1: state => state.count, // 這種是上面寫法的簡寫
count2: 'count' // 這種是 state => state.count 的簡寫
}), // 這個方法最終會返回一個對象
...
/// 若是映射的計算屬性的名稱與 state 的子節點名稱相同時,咱們能夠簡化給 mapState 傳一個字符串數組
computed: mapState([
'count' // 映射 this.count 爲 store.state.count
])
...
/// 通常不會使用上面寫法,由於這樣其餘計算屬性就沒法添加,最好利用這個函數會返回對象的特性,直接使用對象展開的寫法
computed: {
...mapState([
'count'
]),
info () {
return `${this.msg} info`
}
},
複製代碼
Getter(能夠認爲是 store 的計算屬性),就像計算屬性同樣,Getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算(源於官網,稍改)異步
/// 定義時,能夠把其看成 state 的計算屬性
const store = new Vuex.Store({
state: {
count: 0
},
getters: {
setCount (state) {
return `set count: ${state.count}`
}
},
複製代碼
computed: {
showSetCount () { // 訪問時能夠經過 this.$store.getters 訪問
return this.$store.getters.setCount
},
複製代碼
getters: {
setCount (state) {
return `set count: ${state.count}`
},
allMsg (state, getters) {
return `all ${getters.setCount}` // 這裏的 getters 和 state 同樣都是當前 store 下的
}
}
複製代碼
同上文的 mapState 同樣使用async
import { mapState, mapGetters } from 'vuex';
...
computed: {
...mapGetters({
showSetCount: 'setCount',
showAllMsg: 'allMsg'
}),
}
複製代碼
mutation 是更改 store 中狀態的惟一方式,每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler),不能直接調用一個 mutation handler,須要經過 store.commit 方法進行調用(源於官網,稍改)函數
mutations: {
increment (state) {
state.count++
}
}
...
<button @click="incream">add count</button>
...
methods: {
incream () {
this.$store.commit('increment'); // 經過 store.commit 觸發
}
}
複製代碼
/// commit 接收第二個參數,這樣外部數據就能夠更新到倉庫中
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
...
methods: {
incream () {
this.$store.commit('increment', {
amount: 20
});
}
},
/// 若是你是redux的使用者,可能更習慣對象風格的代碼提交
incream () {
this.$store.commit({
type: 'increment',
amount: 20
})
}
複製代碼
/// 若是使用了 types 的方式,應該都要用下面方式,調用時直接傳 payload
methods: {
...mapMutations({
'incream': INCREMENT
})
},
...
/// 在調用處,直接傳參
<button @click="incream({amount: 20})">add count</button>
複製代碼
state.obj = { ...state.obj, newProp: 123 }
複製代碼
/// types.js
export const INCREMENT = 'INCREMENT';
...
/// index.js
import { INCREMENT } from './types';
...
mutations: {
[INCREMENT] (state, payload) {
state.count += payload.amount
}
}
...
/// main.vue
import { INCREMENT } from '../script/types';
...
methods: {
incream () {
this.$store.commit({
type: INCREMENT,
amount: 20
})
}
},
複製代碼
/// index.js
actions: {
// action 能夠是同步方法,也能夠是異步方法
increment (context) {
context.commit(INCREMENT, {amount: 10})
}
}
...
/// main.vue
methods: {
incream () {
this.$store.dispatch('increment') // 經過 dispatch 觸發 action
}
},
複製代碼
// 使用對象解構簡化操做
increment ({commit}) {
commit(INCREMENT, {amount: 10})
}
複製代碼
increment ({commit}, payload) {
commit(INCREMENT, {amount: payload.amount})
}
...
/// main.vue
incream () {
// 以 payload 的形式調用
this.$store.dispatch('increment', {
amount: 15
})
...
// 還能夠用對象形式調用
this.$store.dispatch({
type: 'increment',
amount: 15
})
}
複製代碼
methods: {
...mapActions({
incream: 'increment'
})
}
...
// 在方法調用處傳 payload
<button @click="incream({amount: 15})">add count</button>
複製代碼
能夠在action中寫各類異步方法,來組合複雜的業務邏輯
actions: {
increment ({commit, dispatch}, payload) { // context 對象也有 dispatch
return new Promise((resolve, reject) => {
setTimeout(() => {
commit(INCREMENT, {amount: payload.amount}) // 觸發 Mutations 更新
dispatch('log') // 在一個 action 中觸發另一個 action
resolve({
code: '000000'
}) // 返回異步處理狀況
}, 500); // 模擬接口返回
})
},
log () {
console.log('日誌.....')
}
}
...
/// main.vue
<button @click="incream({amount: 15}).then(doneCheck)">add count</button>
...
methods: {
...mapActions({
incream: 'increment'
}),
doneCheck (status) {
console.log('status', status); // 這裏能夠拿到異步處理結果來繼續後續邏輯
}
},
複製代碼
這裏用 Promise 模擬了一個接口處理狀況,在 action 中觸發狀態更新,並觸發另一個 action,返回異步結果供調用函數使用(還可使用 async / await 更加簡化代碼)
使用單一狀態樹,應用的全部狀態會集中到一個比較大的對象。當應用變得很是複雜時,store 對象就有可能變得至關臃腫。
Vuex 容許咱們將 store 分割成模塊(module),每一個模塊擁有本身的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行一樣方式的分割(源於官網)
/// index.js
const countModule = {
state: () => ({ // 子 store 下的 state 用這種函數的返回的形式,緣由和組件內data 定義同樣
count: 0
}),
getters: {
setCount (state) {
return `set count: ${state.count}`
},
allMsg (state, getters) {
return `all ${getters.setCount}`
}
},
mutations: {
[INCREMENT] (state, payload) {
state.count += payload.amount
}
},
actions: {
increment ({commit, dispatch}, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit(INCREMENT, {amount: payload.amount})
dispatch('log')
resolve({
code: '000000'
})
}, 500);
})
},
log () {
console.log('日誌.....')
}
}
}
const infoModule = {
state: () => ({
info: 'store info'
}),
getters: {
allMsg (state) {
return `show ${state.info}`
}
}
}
const store = new Vuex.Store({
modules: { // 分別定義兩個store經過modules的方式組合在一塊兒
countModule,
infoModule
}
})
...
/// main.vue
/// 使用時,除state外,和以前使用方式一致
computed: {
...mapState({
count: state => state.countModule.count, // state 要指定模塊
info: state => state.infoModule.info
}),
...mapGetters({
showSetCount: 'setCount',
showAllMsg: 'allMsg'
}),
},
methods: {
...mapActions({
incream: 'increment'
}),
}
...
/// 若是想保持 mapState 中數組的簡介寫法,能夠這麼改造下
...mapState([
'countModule',
'infoModule'
]), // mapState 中直接導入模塊名
...
{{ countModule.count }} - {{ infoModule.info }}
/// 使用時按模塊名去獲取 state
複製代碼
模塊中只有 state 在使用時須要特別指明模塊,action、mutation 和 getter 由於是註冊在全局 store 上,因此能夠直接使用,經過打印 this.$store
也看到兩個不一樣模塊的 getters 是都加掛在 store 實例對象上
/// 若是外層 store 中也有 state,能夠經過 rootState 獲取
const store = new Vuex.Store({
modules: {
countModule,
infoModule
},
state: {
copyright: 'xxx company'
}
})
/// getters、mutations 的第三個參數表示的是 rootState(名字能夠隨意)
getters: {
setCount (state, getters, rootState) {
return `set count: ${state.count}`
},
...
mutations: {
[INCREMENT] (state, payload, rootState) {
...
/// action中則必須使用(rootState這個變量名)
actions: {
increment ({commit, dispatch, rootState}, payload) {
複製代碼
我感受不必使用,若是咱們在拆分模塊時,模塊名是一個有明顯語意的名字,在定義 action、getters 這些內容時,用 模塊名+屬性/方法名 的方式同樣能達到相同的功效,這樣也能夠很明晰的知道這個方法屬於哪一個模塊。
這樣也能夠有很高的複用性,封裝型也不差,使用起來也簡單。
/// index.js
const infoModule = {
getters: {
infoModuleShowInfo (state) {
return `show ${state.info}`
}
}
...
/// main.vue
...mapGetters({
showInfo: 'infoModuleShowInfo'
}),
...
/// 模塊名+屬性名,使用時直接用 infoModuleShowInfo,並且能看出是屬於 infoModule
複製代碼
能夠在 store 的根節點下,使用 plugins 的形式加掛插件
const store = new Vuex.Store({
// ...
plugins: [myPlugin]
})
複製代碼
Vuex 內置了 logger 插件
import createLogger from 'vuex/dist/logger'
const store = new Vuex.Store({
plugins: [createLogger()]
})
複製代碼
Vuex 的 state 上使用 v-model,須要使用帶有 setter 的雙向綁定計算屬性
const store = new Vuex.Store({
modules: {
countModule,
infoModule
},
state: {
copyright: '2020 company',
auther: 'rede'
},
getters: {
bookInfo (state) {
return `${state.auther} @ ${state.copyright}`
}
},
actions: {
updataAutherAction ({commit}, payload) {
commit('updataAuther', {
value: payload.value
})
}
},
mutations: {
updataAuther (state, payload) {
state.auther = payload.value
}
}
})
...
/// main.vue
<input type="text" v-model="auther">
<div>{{bookInfo}}</div>
...
computed: {
...mapGetters({
bookInfo: 'bookInfo'
}),
auther:{
get () {
return this.$store.state.auther
},
set (value) {
this.updataAutherAction({
value
})
}
},
...
複製代碼