
1.什么是Vuex?
官方回答:Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。
2.Vuex能解決什么問題?
當我們的應(yīng)用遇到多個組件共享狀態(tài)時,單向數(shù)據(jù)流的簡潔性很容易被破壞:
- 多個視圖依賴于同一狀態(tài)。
- 來自不同視圖的行為需要變更同一狀態(tài)。
對于問題一,傳參的方法對于多層嵌套的組件將會非常繁瑣,并且對于兄弟組件間的狀態(tài)傳遞無能為力。
對于問題二,我們經(jīng)常會采用父子組件直接引用或者通過事件來變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會導(dǎo)致無法維護的代碼。
因此,把組件的共享狀態(tài)抽取出來,以一個全局單例模式管理。在這種模式下,我們的組件樹構(gòu)成了一個巨大的“視圖”,不管在樹的哪個位置,任何組件都能獲取狀態(tài)或者觸發(fā)行為!
通過定義和隔離狀態(tài)管理中的各種概念并通過強制規(guī)則維持視圖和狀態(tài)間的獨立性,我們的代碼將會變得更結(jié)構(gòu)化且易維護。

3.創(chuàng)建Vuex的步驟:
- 下載并引用vuex.js文件,插入到vue.js文件后面
- 創(chuàng)建Vuex實例對象
const store = new Vuex.Store({
state:{
msg:"魷小魚"
},
mutations:{},
actions:{},
getters:{}
});
- 在祖先組件中添加store的key保存Vuex對象; 只要祖先組件中保存了Vuex對象 , 那么祖先組件和所有的后代組件就可以使用Vuex中保存的共享數(shù)據(jù)了
Vue.component("father",{
template:"#father",
//在祖先組件中添加store的key保存Vuex對象
store:store,
data:function(){
return{}
},
methods:{},
computed:{},
components:{}
})
- 引用vuex中的共享數(shù)據(jù)方法
<template id="father">
<div>
<p>{{this.$store.state.msg}}</p>
<son></son>
</div>
</template>
4.State
- state屬性和組件的data屬性類似;state作用:用來
保存全局共享數(shù)據(jù)
在引入vuex 以后,我們需要在創(chuàng)建Vuex對象的state中定義變量:
const store = new Vuex.Store({
// 這里的state就相當于組件中的data, 就是專門用于保存共享數(shù)據(jù)的
state:{
msg:"魷小魚"
}
});
組件中內(nèi)容:
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<!--4.在使用Vuex中保存的共享數(shù)據(jù)的時候, 必須通過如下的格式來使用-->
<p>{{this.$store.state.msg}}</p>
<son></son>
</div>
</template>
<template id="son">
<div>
<p>{{this.$store.state.msg}}</p>
</div>
</template>
有了vuex,我們不必在考慮組件之間的傳值,直接就可以通過$store來獲取不同的數(shù)據(jù),,但是如果需要vuex中的多個數(shù)據(jù)的這時候,這樣寫就太啰嗦了,我們可以將它定義在computed中。
<template id="son">
<div>
<p>{{msg}}</p>
</div>
</template>
Vue.component("father", {
template: "#father",
// 在祖先組件中添加store的key保存Vuex對象
// 只要祖先組件中保存了Vuex對象 , 那么祖先組件和所有的后代組件就可以使用Vuex中保存的共享數(shù)據(jù)了
store:store,
// 子組件
components: {
"son": {
template: "#son",
computed: {
nickname(){
return this.$store.state.msg
}
}
}
}
});
這樣引入就方便了很多。
-
mapState 輔助函數(shù)
mapState輔助函數(shù)作用:可以輔助獲取到多個state的值
怎么使用?
1.Vuex實例對象部分:
state:{
msg:"魷小魚"
}
2.在.vue組件中引入,在js塊中引入
import { mapState } from 'vuex'
3.在vue組件computed中定義一個數(shù)組
computed:{
/*通過mapState將 store 中的 state 的全局共享數(shù)據(jù)名稱 “映射”到“局部計算屬性”*/
...mapState([ //mapState本是一個函數(shù),在里面寫一個數(shù)組,記得加...
'msg' //存的數(shù)據(jù),把 `this.msg` 映射為 `this.$store.state.msg`
])
}
這一句代碼就相當于下面這句:
computed:{
msg(){
return this.$store.state.msg
}
}
4.然后就可以不用$store.state.msg引用了,直接插值
{{msg}}
5.Getters
-
getters和computed相似;getters的作用:用來保存
獲取共享數(shù)據(jù)的計算屬性方法
就像計算屬性一樣,getter 的返回值會根據(jù)它的依賴被緩存起來,且只有當它的依賴值發(fā)生了改變才會被重新計算
補充:
- 什么是計算屬性?
?將計算結(jié)果緩存起來,只要數(shù)據(jù)沒有發(fā)生變化就只會計算一次,以后使用的都是緩存>起來的數(shù)據(jù)- 函數(shù)和計算屬性的區(qū)別?
?函數(shù)的特點:每次調(diào)用都會執(zhí)行
?計算屬性的特點:只要返回的結(jié)果沒有發(fā)生變化,那么計算屬性只會被執(zhí)行一次data:{ message:"abcdefg" }, //5.專門用于儲存監(jiān)聽事件回調(diào)函數(shù) methods:{ /*函數(shù)的特點:每次調(diào)用都會執(zhí)行*/ msg1(){ console.log("msg1被執(zhí)行了"); let res = this.message.split('').reverse().join(''); return res; } }, //6.專門用于定義計算屬性的 computed: { /*計算屬性的特點:只要返回的結(jié)果沒有發(fā)生變化,那么計算屬性只會被執(zhí)行一次 計算屬性應(yīng)用的場景:由于計算屬性會將返回的結(jié)果緩存起來 如果返回的數(shù)據(jù)不會頻繁的發(fā)生改變, 使用計算屬性會比函數(shù)的性能高 */ // 計算屬性的 getter reversedMessage: function () { console.log("reversedMessage被執(zhí)行了"); // `this` 指向 vm 實例 let res = this.message.split('').reverse().join(''); return res; } }
- 如何把數(shù)據(jù)緩存起來?
?這個數(shù)據(jù)如果是組件中的,就使用computed來緩存
?這個數(shù)據(jù)如果是Vuex中的,就使用getters來緩存效果:<div id="app"> <father></father> </div> <template id="father"> <div> <p>{{this.$store.getters.format}}</p> <p>{{this.$store.getters.format}}</p> <p>{{this.$store.getters.format}}</p> </div> </template> <script type="text/javascript"> const store = new Vuex.Store({ state:{ name:"魷小魚" }, mutations:{}, getters:{ format(state){ console.log("getters被調(diào)用了") return state.name + "2541873074@qq.com" } } }) Vue.component("father",{ template:"#father", store:store, }) let vue= new Vue({ el:"#app", }) </script>數(shù)據(jù)沒用發(fā)生改變,format方法只會被調(diào)用一次
- getters相當于vue中的計算屬性,通過getters進一步處理,得到我們想要的值,而且允許傳參;
第一個參數(shù)就是state`:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
通過屬性訪問:Getter 會暴露為 store.getters 對象,你可以以屬性的形式訪問這些值
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getter 也可以接受其他 getter 作為第二個參數(shù):
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
通過屬性訪問:
store.getters.doneTodosCount // -> 1
通過方法訪問:可以通過讓 getter 返回一個函數(shù),來實現(xiàn)給 getter 傳參。在你對 store 里的數(shù)組進行查詢時非常有用。
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
-
mapGetters 輔助函數(shù)
mapGetters 輔助函數(shù)的作用:僅僅是將 store 中的 getter 映射到局部計算屬性
怎么使用?
1.Vuex實例對象部分:
state:{
count:0
},
// getters:獲取全局共享的數(shù)據(jù)
getters: {
count (state) {
return state.count
}
}
2.在.vue組件中引入,在js塊中引入
import { mapGetters} from 'vuex'
3.在vue組件computed中定義一個數(shù)組:
computed: {
// 使用對象展開運算符將 getter 混入 computed 對象中
...mapGetters([
'count ', // 把 `this.count` 映射為 `this.$store.getters.count`
// ...
])
}
以上代碼就相當于下面的代碼:
computed: {
count(state){
return this.$store.getters.count
}
}
6.Mutations
- mutations和組件中的methods屬性類似;mutations作用:用來保存
修改全局共享數(shù)據(jù)的方法
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<son1></son1>
</div>
</template>
<template id="son1">
<div>
<button @click="add">增加</button>
<button @click="sub">減少</button>
<input type="text" :value="this.$store.state.count">
</div>
</template>
<script>
const store = new Vuex.Store({
// 這里的state就相當于組件中的data, 就是專門用于保存共享數(shù)據(jù)的
state: {
count:0
},
// mutations:用于保存修改共享數(shù)據(jù)的方法的
mutations: {
/*
注意點:在執(zhí)行mutations中定義的方法的時候, 系統(tǒng)會自動給這些方法傳遞一個state參數(shù)
state中就保存了共享的數(shù)據(jù)
*/
mAdd(state){
state.count = state.count + 1;
},
mSub(state){
state.count = state.count - 1;
}
}
});
Vue.component("father", {
template: "#father",
store:store,
//子組件
components: {
"son1": {
template: "#son1",
methods:{
add(){
//注意點:在Vuex中不推薦直接修改共享數(shù)據(jù)
// this.$store.state.count = this.$store.state.count + 1;
this.$store.commit("mAdd");
},
sub(){
// this.$store.state.count = this.$store.state.count - 1;
this.$store.commit("mSub");
}
}
}
}
});
let vue = new Vue({
el: '#app',
});
</script>
- mutations需要通過
commit來調(diào)用其里面的方法,它也可以傳入?yún)?shù),第一個參數(shù)是state,第二個參數(shù)是載荷(payLoad),也就是額外的參數(shù)
Vuex實例對象部分:
state:{
count:0
},
mutations: { //類似于methods
changecount(state,payLoad){ //第一個參數(shù)是`state`,第二個參數(shù)是`載荷(payLoad)`,也就是額外的參數(shù)
state.count += payLoad.number
}
}
template模板部分:
<div class="home">
<div><button @click="test">我是按鈕</button></div>
</div>
Vue實例對象部分:
methods:{
test(){
this.$store.commit(' changecount',{ //第二個參數(shù)最好寫成對象形式
number:5
})
}
}
調(diào)用的時候第二個參數(shù)最好寫成對象形式,這樣我們就可以傳遞更多信息。
但是,這樣寫還是會遇到同樣的問題,就是如果需要操作多個數(shù)據(jù),就會變的麻煩,這時候我們就需要mapMutations輔助函數(shù),通過它將mutations中方法映射到組件的methods屬性中
這里是通過
mapMutations將mutations中方法映射組件的methods屬性,需要提前做的準備和須知:
- 使用常量替代 Mutation 事件類型
① 新建一個mutations-type.js文件:
// mutation-types.js export const CHANGE_COUNT = 'CHANGE_COUNT'② store.js文件中:
// store.js import Vuex from 'vuex' import { CHANGE_COUNT } from './mutation-types' const store = new Vuex.Store({ state: { count:0 }, //這里在做項目時也可以把mutation中代碼單獨提取到一個文件中,命名為mutatons.js文件 mutations: { // 我們可以使用 ES2015 風(fēng)格的計算屬性命名功能來使用一個常量作為函數(shù)名 [CHANGE_COUNT] (state,payLoad) { // payLoad 傳遞的值 // mutate state state.count = payLoad } } })為什么要使用常量替代 Mutation 事件類型?
?因為在Actions中通過commit('changecount', playLoad)觸發(fā)Mutations中changecount修改數(shù)據(jù)的方法時,用的是“”將changecount包裹成字符串,由于字符串在書寫過程中不會報錯,所以將Mutations中的方法名定義為常量,這樣在書寫不正確時會報錯,方便維護使用常量之
前在actions中調(diào)用mutations中方法://這里在做項目時也可以把actions中代碼單獨提取到一個文件中,命名為actions.js文件 actions: { //用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用 `commit` 很多次的時候) setchangecount({ commit },playLoad){ commit('changecount',playLoad) } }使用常量之
后在actions中調(diào)用mutations中方法://這里在做項目時也可以把actions中代碼單獨提取到一個文件中,命名為actions.js文件 import {CHANGE_COUNT} from './mutations-type' actions: { //用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用 `commit` 很多次的時候) setchangecount({ commit },playLoad){ commit(CHANGE_COUNT,playLoad) } }
- Mutation 必須是同步函數(shù)
一條重要的原則就是要記住mutation必須是同步函數(shù)。
下面Vue官網(wǎng)原話:mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) } }現(xiàn)在想象,我們正在 debug 一個 app 并且觀察 devtool 中的
mutation日志。每一條mutation被記錄,devtools 都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照。然而,在上面的例子中mutation中的異步函數(shù)中的回調(diào)讓這不可能完成:因為當mutation觸發(fā)的時候,回調(diào)函數(shù)還沒有被調(diào)用,devtools 不知道什么時候回調(diào)函數(shù)實際上被調(diào)用——實質(zhì)上任何在回調(diào)函數(shù)中進行的狀態(tài)的改變都是不可追蹤的。
-
mapMutations輔助函數(shù)
mapMutations輔助函數(shù)的作用:在組件中調(diào)用mutaions中的方法;
原理: 就如同,通過store.commit(mutations中保存的修改state數(shù)據(jù)的方法)調(diào)用mutaions中的方法
怎么使用?
1.Vuex實例對象部分:
state:{
count:0
},
mutations: { //類似于methods
changecount(state,payLoad){
state.count = payLoad
}
}
2.在.vue組件中引入,在js塊中引入
import { mapMutations } from 'vuex'
3.在vue組件methods中定義一個數(shù)組(Vue實例對象部分):
methods: {
...mapMutations([
'changecount', // 將 `this.changecount()` 映射為 `this.$store.commit('changecount')`
])
}
以上代碼就相當于下面的代碼:
methods: {
changecount(playLoad){
this.$store.commit('changecount',playLoad)
}
}
5.參數(shù)我們可以在調(diào)用這個方法的時候?qū)懭?/p>
<button @click="changecount({number:5})">我是按鈕</button>
5.使用常量之前在actions中調(diào)用mutations中方法:
//這里在做項目時也可以把actions中代碼單獨提取到一個文件中,命名為actions.js文件
actions: {
setchangecount({ commit },playLoad){
commit('changecount',playLoad)
}
}
- 為什么要繞一圈,從mutations里面去改state呢?我能不能直接改state呢?
比如這樣:
changecount(){
this.$store.state.count +=5;
}
實際看結(jié)果也可以,那我為什么從mutations里面中轉(zhuǎn)一下呢?以下的解釋是我看別人寫的,寫的不錯就在這里利用一下
原因如下:
① 在mutations中不僅僅能做賦值操作
② 作者在mutations中做了類似埋點操作,如果從mutations中操作的話, 能被檢測到,可以更方便用調(diào)試工具調(diào)試,調(diào)試工具可以檢測到實時變化,而直接改變state中的屬性,則無法實時監(jiān)測
注意:mutations只能寫同步方法,不能寫異步,比如axios、setTimeout等,這些都不能寫,mutations的主要作用就是為了修改state的。
原因類似:如果在mutations中寫異步,也能夠調(diào)成功,但是由于是異步的,不能被調(diào)試工具追蹤到,所有不推薦這樣寫,不利于調(diào)試,這是官方的約定。
7.Actions
- action類似于mutation;actions的作用:保存
觸發(fā)Mutations中修改全局共享數(shù)據(jù)的方法
不同在于:
action提交(commit)的是mutation,而不是直接變更狀態(tài)action包含異步操作,類似于axios請求,可以都放在action中寫action中的方法默認的就是異步,并且返回promise
代碼如下:
sate.js
export default {
count: 0
}
mutations-typa.js
export const CHANGE_COUNT = 'CHANGE_COUNT'
mutations.js
import {CHANGE_COUNT} from './mutations-type'
export default {
[CHANGE_COUNT] (state,payLoad) {
state.count = payLoad
}
}
actions.js
import {CHANGE_COUNT} from './mutations-type'
export default {
setchangecount (context) {
context.commit(CHANGE_COUNT)
}
}
Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調(diào)用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。當我們在之后介紹到 Modules 時,你就知道 context 對象為什么不是 store 實例本身了。
- 實踐中,我們會經(jīng)常用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用
commit很多次的時候):
actions: {
//用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用 `commit` 很多次的時候)
setchangecount ({ commit } , payLoad) {
commit(CHANGE_COUNT , payLoad)
}
}
- 在action內(nèi)部可以執(zhí)行異步操作:
actions: {
setchangecount ({ commit }) {
setTimeout(() => {
commit(CHANGE_COUNT)
}, 1000)
}
}
-
mapActions 輔助函數(shù)
mapActions 輔助函數(shù)的作用: 將actions中的方法映射到組件中;
原理:就如同,通過store.dispatch(actions保存的觸發(fā)mutation中的方法)映射actions中的方法
分發(fā)Action的兩種方式:
分發(fā)Action:也就相當于在界面中觸發(fā)Action方法
1.Action 通過 store.dispatch 方法觸發(fā):
store.dispatch('setchangecount')
2.在組件中分發(fā) Action:使用 mapActions 輔助函數(shù)將組件的 methods 映射為 store.dispatch 調(diào)用
如何使用mapActions 輔助函數(shù)?
1.Vuex實例對象部分:
state:{
count:0
},
//這里并沒有使用常量替代 Mutation 事件
mutations: { //類似于methods
changecount(state,payLoad){
state.count = payLoad
}
}
actions: {
setchangecount ({ commit } , payLoad) {
commit('changecount' , payLoad)
}
}
2.在.vue組件中引入,在js塊中引入
import { mapActions } from 'vuex'
3.在vue組件methods中定義一個數(shù)組(Vue實例對象部分):
methods: {
...mapActions([
'setchangecount', // 將 `this.setchangecount()` 映射為`this.$store.dispatch('setchangecount')`
]),
}
上面的代碼就相當于下面的代碼:
methods: {
setchangecount(){
return this.$store.dispatch('setchangecount')
}
}
- Actions 支持同樣的載荷方式和對象方式進行分發(fā)
// 以載荷形式分發(fā)
store.dispatch('setchangecount', {
amount: 10
})
// 以對象形式分發(fā)
store.dispatch({
type: 'setchangecount',
amount: 10
})
