Vuex是做什么的乌妒?
- 官方解釋:Vuex是一個專為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式。
- 它采用
集中式存儲管理
應(yīng)用的所有組件狀態(tài)涌庭,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化芥被。 - Vuex也集成到Vue的官方調(diào)試工具
devtools extension
欧宜,提供了諸如零配置的time-travel調(diào)試坐榆、狀態(tài)快照導(dǎo)入導(dǎo)出等高級調(diào)試功能。
2. 狀態(tài)管理到底是什么?
- 狀態(tài)管理模式冗茸、集中式存儲管理這些名詞聽起來就非常高大上席镀,讓人捉摸不透。
- 其實夏漱,你可以簡單的將其看成把需要多個組件共享的變量全部存儲在一個對象里面豪诲。
- 然后,將這個對象放在頂層的Vue實例中挂绰,讓其他組件可以使用屎篱。
- 那么,多個組件是不是就可以共享這個對象中的所有變量屬性了呢葵蒂?
Vuex狀態(tài)管理圖例
簡單的案例
實現(xiàn)一個簡單的加減案例
-
首先交播,我們需要在某個地方存放我們的Vuex代碼:
- 這里,我們先創(chuàng)建一個文件夾store践付,并且在其中創(chuàng)建一個index.js文件
- 在index.js文件中寫入如下代碼:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count:0 }, mutations: { //方法 increment(state){ state.count++ } decrement(state){ state.count-- } }, })
-
其次秦士,我們讓所有的Vue組件都可以使用這個store對象
- 來到main.js文件,導(dǎo)入store對象永高,并且放在new Vue中
- 這樣隧土,在其他Vue組件中,我們就可以通過this.$store的方式命爬,獲取到這個store對象了
import Vue from 'vue' import App from './App.vue' import store from './store' new Vue({ store, render: h => h(App) }).$mount('#app')
-
使用Vuex的count
<template> <div> <p>{{count}}</p> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> </template> <script> export default { name: 'App', computed: { count: function () { return this.$store.state.count } }, methods: { increment: function() { this.$store.commit('increment') } decrement: function() { this.$store.commit('decrement') } } } </script>
以上曹傀,就是使用Vuex最簡單的方式了
對以上使用步驟做一個簡單的總結(jié):
- 提取出一個公共的store對象,用于保存在多個組件中共享的狀態(tài)
- 將store對象放置在new Vue對象中饲宛,這樣可以保證在所有的組件中都可以使用到
- 在其他組件中使用store對象中保存的狀態(tài)即可
通過this.$store.state.屬性的方式來訪問狀態(tài)
通過this.$store.commit('mutation中的方法')來修改狀態(tài)
-
注意事項:
- 我們通過提交mutation的方式卖毁,而非直接改變store.state.count。
- 這是因為Vuex可以更明確的追蹤狀態(tài)的變化落萎,所以不要直接改變store.state.count的值亥啦。
- 同步操作可以直接通過mutations來修改,但異步不行练链,得先通過Actions
Getters基本使用
- 有時候翔脱,我們需要從store中獲取一些state變異后的狀態(tài),比如下面的Store中:
獲取學(xué)生年齡大于20的學(xué)生媒鼓。const store = new Vue.store({ state: { students: [ {id:1, name: 'kk', age: 21}, {id:2, name: 'hh', age: 19}, ] } })
- 我們可以在Store中定義getters
getters: { more20Ages(state) { return state.students.filter(s => s.age >=20) } }
-
Getters作為參數(shù)和傳遞參數(shù)
- 如果我們已經(jīng)有了一個獲取所有年齡大于20歲學(xué)生列表的getters届吁,那么代碼可以這樣寫
getters: { more20Ages(state) { return state.students.filter(s => s.age >=20) }, more20AgesLength(state,getters) { return getters.more20Ages.length }, }
- getters默認(rèn)是不能傳遞參數(shù)的错妖,如果希望傳遞參數(shù),那么只能讓getters本身返回另一個函數(shù)疚沐。
//比如我們是希望根據(jù)年齡來獲取用戶信息 getters: { moreAgeStu(state) { return age => { return state.students.filter(s => s.age > age) } } }
Mutation狀態(tài)更新
- Vuex的store狀態(tài)的更新唯一方式:提交Mutation
- Mutation主要包括兩部分
- 字符串的事件類型(type)
- 一個回調(diào)函數(shù)(handler)暂氯,該回調(diào)函數(shù)的第一個參數(shù)就是state。
- mutations的定義方式:
mutations: {
increment(state) {
state.count++
}
}
- 通過mutation更新
increment: function () {
this.$store.commit('increment')
}
Mutation傳遞參數(shù)
- 在通過mutation更新數(shù)據(jù)的時候亮蛔,有可能我們希望攜帶一些額外的參數(shù)
參數(shù)被稱為是mutation的載荷(Payload) - Mutation中的代碼
decrement(state,n) {
state.count -= n
}
decrement: function (count) {
//payload:負(fù)載
//普通的的提交封裝
this.$store.commit('decrement',count)
}
- 但如果參數(shù)不是一個呢痴施?
比如我們有很多參數(shù)需要傳遞
這個時候,我們通常會以對象的形式傳遞究流,也就是payload是一個對象
這個時候可以再從對象中取出相關(guān)的信息
changeCount(state,payload) {
state.count = payload.count
}
changeCount: function () {
this.$store.commit('changeCount',{count: 0})
}
Mutation提交風(fēng)格
- 上面的通過commit進(jìn)行提交是一種普通的方式
- Vue還提供了另外一種風(fēng)格辣吃,它是一個包含type屬性的對象
this.$store.commit({
type: 'changeCount',
count
})
- Mutation中的處理方式是將整個commit的對象作為payload使用,所以代碼沒有改變芬探,依然如下:
changeCount(state,payload) {
state.count = payload.count
}
Mutation響應(yīng)規(guī)則
- Vuex的store中的state是響應(yīng)式的神得,當(dāng)state中的數(shù)據(jù)發(fā)生改變時,Vue組件會自動更新
- 這就要求我們必須遵守一些Vuex對應(yīng)的規(guī)則:
- 提前在store中初始化好所需的屬性
- 當(dāng)給state的對象添加新屬性時偷仿,使用下面的方式:
方式一:使用Vue.set(obj,'newProp',123) Vue.set(state.info,'address','泉州')
方式二:用新對象給舊對象重新賦值
Mutation常量類型 - 概念
- 在用mutation時會有以下問題
- 在mutation中哩簿,我們定義了很多事件類型(也就是其中的方法名稱)
- 當(dāng)我們的項目增大時,Vuex管理的狀態(tài)越來越多酝静,需要更新狀態(tài)的情況也越來也多节榜,那么意味著Mutation中的方法越來越多
- 方法過多,使用者需要花費大量的經(jīng)歷去記住這些方法形入,甚至是多個文件來回切換全跨,查看方法名稱,甚至如果不是復(fù)制的時候亿遂,可能還會出現(xiàn)寫錯的情況浓若。
- 改進(jìn)措施
- 在store下新建一個mutation-types.js文件
export const INCREMENT = 'increment'
- 在提交方法地方導(dǎo)入mutation-types.js這個文件
import {INCREMENT} from './store/mutation-types.js'
提交方法就可以改成以下方式
addition() { this.$store.commit(INCREMENT) }
- store.jsmutation方法也需要先導(dǎo)入mutation-types.js
import {INCREMENT} from './store/mutation-types.js'
mutation方法就可以改成以下方式
[INCREMENT](state) { state.counter++ }
- 在store下新建一個mutation-types.js文件
Mutation同步函數(shù)
- 通常情況下,Vuex要求我們Mutation中方法必須是同步方法
- 主要原因是當(dāng)我們使用devtools時蛇数,devtools可以幫助我們捕捉mutation的快照
- 但如果是異步操作挪钓,那么devtools將不能很好的追蹤這個操作什么時候會被完成
Action的基本定義
- 當(dāng)我們需要在Vuex中進(jìn)行異步操作,比如網(wǎng)絡(luò)請求耳舅,這時就可以用Action碌上,它是用來代替Mutation進(jìn)行異步操作
mutations: {
updateInfo(state) {
state.info.name = 'kk'
}
}
actions: {
//context上下文,這里就當(dāng)做store對象
/**aUpdateInfo(context,payload) {
setTimeout( () => {
context.commit('updateInfo')
console.log(payload.message) //我是攜帶的信息
payload.success() //里面異步操作已經(jīng)完成
},1000)
}**/
//可以優(yōu)化返回一個promise
aUpdateInfo(context,payload) {
return new promise( (resolve,reject) => {
setTimeout( () => {
context.commit('updateInfo')
console.log(payload) //我是攜帶的信息
resolve('kkkkk')
},1000)
})
}
}
updateInfo() {
/**this.$store.dispatch('aUpdateInfo',{
message: '我是攜帶的信息',
success: () => {
console.log('里面異步操作已經(jīng)完成')
}
})**/
//上述代碼還可以優(yōu)化成以下
this.$store.dispatch('aUpdateInfo','我是攜帶的信息').then(res => {
console.log('里面完成了提交')
console.log(res) //kkkkk
})
}
認(rèn)識Module
- Module是模塊的意思浦徊,為什么在Vuex中我們要使用模塊呢馏予?
- Vue使用單一狀態(tài)數(shù),那么也意味著很多狀態(tài)都會交給Vuex來管理
- 當(dāng)應(yīng)用邊得非常復(fù)雜時盔性,store對象就有可能變得相當(dāng)臃腫
- 為了解決這個問題霞丧,Vuex允許我們將store分割成模塊(Module),而每個模塊擁有自己的state冕香、mutations蛹尝、actions后豫、getters等
const moduleA = {
state: { name: 'kk' },
mutations: {},
actions: {},
getters: {}
}
const moduleB = {
state: {},
mutations: {},
actions: {},
getters: {}
}
const store = new Vuex.Store({
modules: {
a:moduleA,
b:moduleB
}
})
store.state.a.name // ->kk
store.state.b // ->moduleB的狀態(tài)