vuex是什么?
-
Vuex
是一個(gè)專為Vue.js
應(yīng)用程序開發(fā)的狀態(tài)管理庫,可以用于各種復(fù)雜組件之間的通信婚脱,屬于vue
全家桶相關(guān)的生態(tài)工具鏈
什么情況下需要使用vuex?
- 如果開發(fā)過程中存在各種組件或者很多復(fù)雜的組件勺像,且各種組件之間需要進(jìn)行相互通信(個(gè)人認(rèn)為即如果把某一個(gè)頁面拆成各種小組件時(shí)障贸,便需要考慮使用
vuex
進(jìn)行通訊,提升組件通信間的開發(fā)效率)
如果不使用Vuex通信還能使用什么吟宦?
- 父子組件通訊
- 兄弟組件通信
- 隔代組件通訊
vuex的工作流程
- 圖示如下:
image.png
- 工作流程文字描述如下:
- 在
vue
組件里面篮洁,通過dispatch
來觸發(fā)actions
提交修改數(shù)據(jù)的操作。 - 然后再通過
actions
的commit
來觸發(fā)mutations
來修改數(shù)據(jù)殃姓。 -
mutations
接收到commit
的請求袁波,就會(huì)自動(dòng)通過Mutate
來修改state
里面的值。 - 最后由
store
觸發(fā)每一個(gè)調(diào)用它的組件的更新
如何使用Vuex
使用
vue-cli
生成項(xiàng)目時(shí)蜗侈,可以選擇集成vuex
到項(xiàng)目中篷牌。此時(shí),vue-cli
會(huì)自動(dòng)安裝vuex
踏幻,并在src
文件夾下生成store文件下的 store.js
完成vuex
的引入和配置枷颊。-
手動(dòng)引入安裝
npm install vuex //或者 yarn add vuex
(備注)如果是
vue-cli
創(chuàng)建的項(xiàng)目,且在最開始集成了vuex
則不需要進(jìn)行如下配置该面,否則請參考如下配置- image.png
- image.png
vuex的五大核心要素
Start
-
state
表示狀態(tài)夭苗,類似于vue
中的data
,用來存放狀態(tài)值,里面存放的數(shù)據(jù)是響應(yīng)式的
// 代碼參考
// store.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
text: "hello word",
},
getters: {},
mutations: {},
actions: {},
modules: {},
});
//某組件代碼(其他頁面訪問方式相同)
<template>
<div>
<h2>我是某組件代碼</h2>
//頁面通過 $store.state.text 可以獲取相關(guān)值
<h3>這是vuex中的數(shù)據(jù):{{ $store.state.text }}</h3>
</div>
</template>
<script>
export default {
name: "allHome",
methods: {
getVuex() {
//這里通過 this.$store.state.text 可以獲取相關(guān)值
console.log("這是vuex中的數(shù)據(jù):" + this.$store.state.text);
},
},
mounted() {
this.getVuex();
},
};
</script>
<style></style>
Getter
- 類似于 Vue 中的 計(jì)算屬性(可以認(rèn)為是 store 的計(jì)算屬性)吆倦,getter 的返回值會(huì)根據(jù)它的依賴被緩存起來听诸,且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算
// 代碼參考 ... 表示省略這里的代碼(因?yàn)檫@里代碼是相同的),省略部分請參考對照上方代碼
// store.js
....
state: {
students: [
{ id: 0, name: "張三", age: 25 },
{ id: 1, name: "李四", age: 30 },
{ id: 2, name: "王二", age: 19 },
{ id: 3, name: "麻子", age: 18 },
],
},
getters: {
greaterAgeCount: (state) => {
return state.students.filter((s) => s.age > 20).length;
},
},
//也可以采取下面這種寫法蚕泽,不過這種寫法需要通過方法訪問
// getters: {
// greaterAgeCount: (state) => {
// return function (age) {
// return state.students.filter((s) => s.age > age).length;
// };
// },
// },
....
});
//某組件代碼(其他頁面訪問方式相同)
<template>
<div>
<h2>我是某組件代碼</h2>
<h3>這是vuex中的 getter 返回值的數(shù)據(jù):{{ stundetConut }}</h3>
<!-- 也可以采用方法的方式訪問晌梨,參考如下 -->
<!-- <h3>
我是屬性寫法的得到的getter 返回值 {{ $store.getters.greaterAgeCount(10) }}
</h3> -->
</div>
</template>
<script>
export default {
name: "allHome",
computed: {
stundetConut() {
return this.$store.getters.greaterAgeCount;
},
},
// 也可以采用方法的方式訪問桥嗤,參考如下
// methods: {
// gevue() {
// console.log(this.$store.getters.greaterAgeCount(10));
// },
// },
// mounted() {
// this.gevue();
// },
};
</script>
<style></style>
Mutaion
- (只能處理同步)可以理解為官方規(guī)定修改
Vuex
的store
中的狀態(tài)的唯一方法,類似于事件仔蝌,如果要修改state
的值泛领,需要通過它進(jìn)行,才能更好的使用devtools
追蹤狀態(tài)變化敛惊。當(dāng)然你也可以不在這里修改渊鞋,因?yàn)檫@里只是官方推薦的規(guī)范和vuex
架構(gòu)設(shè)計(jì)的概念。
// 代碼參考 ... 表示省略這里的代碼(因?yàn)檫@里代碼是相同的)瞧挤,省略部分請參考對照上方代碼
// store.js
....
mutations: {
//state 表示數(shù)據(jù) value 表示傳遞過來的值
addText(state锡宋,value) {
state.text = state.text + value;
},
},
....
});
//某組件代碼(其他頁面訪問方式相同)
<template>
<div>
<h2>我是某組件代碼</h2>
<h3>這是vuex中的數(shù)據(jù):{{ $store.state.text }}</h3>
<button @click="changeVue('good')">點(diǎn)擊變化vuex值</button>
</div>
</template>
<script>
export default {
name: "allHome",
methods: {
getVuex() {
console.log("這是vuex中的數(shù)據(jù):" + this.$store.state.text);
},
changeVue(text) {
/**
* 這里通過 this.$store.commit("a",b)可以觸發(fā)對應(yīng)事件 a 表示需要vuex中 mutations定義的 事件名,b表示需要傳遞給vuex的值特恬,如果是多個(gè)值請使用對象表示
* text{
name:"aaa"
age:18
}
*/
this.$store.commit("addText", text);
},
},
mounted() {
this.getVuex();
},
};
</script>
<style></style>
Action
- (可以處理異步)执俩,這里可以理解為
action
是用來分發(fā)將要進(jìn)行的操作事件的,它將要做的事情癌刽,提交給mutation
役首,再由mutations
進(jìn)行修改。 - 當(dāng)然這里也可以直接修改
state
的值(不過在vue2
的vuex
中官方不是很推薦显拜,vue3
中的推薦pinia
狀態(tài)管理庫已經(jīng)是使用這個(gè)修改State
值了)
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
//某組件代碼(其他頁面訪問方式相同)
<template>
<div>
<h2>我是某組件代碼</h2>
<div>{{ $store.state.count }}</div>
<!-- 這里使用 $store.dispatch('a') 觸發(fā) mutation 中的事件衡奥,再由 mutation 進(jìn)行修改,其中 a 表示mutation 中的事件名字远荠, -->
<button @click="$store.dispatch('increment')">修改count</button>
</div>
</template>
<script>
export default {
name: "allHome",
};
</script>
<style></style>
Module
- 用于在存在很多狀態(tài)值的時(shí)候矮固,進(jìn)行模塊劃分的,特別是在大型應(yīng)用中矮台,如果使用單一狀態(tài)樹(只寫一個(gè)文件)乏屯,應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對象∈莺眨可能會(huì)導(dǎo)致代碼不好維護(hù)和store 對象臃腫辰晕。這時(shí)我們可以將 store 分割為模塊(module),每個(gè)模塊擁有自己的
state
确虱、getters
含友、mutations
、actions
校辩、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割窘问。
// 主狀態(tài)store.js
import Vuex from 'vuex';
import moduleA from './modules/modulesA';
import moduleB from './modules/modulesB';
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {
moduleA
moduleB,
},
});
// 拆分的模塊狀態(tài)一 moduleA.js
export default {
//這里使用的命名空間,避免內(nèi)部如果存在名字一樣方法的時(shí)候觸發(fā)其它模塊的同名方法
//如果使用的命名空間后宜咒,如果要觸發(fā)模塊中的的 mutation 就需要加上路徑才能調(diào)用 store.commit('moduleA/print');
namespaced: true,
state: () => ({
a1: 'aaa',
}),
mutations: {
print(state) {
console.log(state.a1);
},
},
actions: {},
getters: {},
};
// 拆分的模塊狀態(tài)一 moduleB.js
export default {
//這里使用的命名空間惠赫,避免內(nèi)部如果存在名字一樣方法的時(shí)候觸發(fā)其它模塊的同名方法
//如果使用的命名空間后,如果要觸發(fā)模塊中的的 mutation 就需要加上路徑才能調(diào)用 store.commit('moduleA/print');
namespaced: true,
state: () => ({
a2: 'bbb',
}),
mutations: {
print(state) {
console.log(state.a2);
},
},
actions: {},
getters: {},
};
vuex輔助函數(shù)
- 主要是為了節(jié)約代碼書寫
- (備注)輔助函數(shù)具體是否使用故黑,可以根據(jù)自己需要進(jìn)行選擇
mapState
// 原寫法
<template>
<div>
<span>{{ customerName }}</span>
</div>
</template>
<script>
export default {
computed: {
customerName() {
return this.$store.state.customerName;
}
}
}
</script>
// 使用 mapState 輔助函數(shù)寫法
<template>
<div>
<span>{{ customerName }}</span>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: mapState({
// 箭頭函數(shù)可使代碼更簡練
count: (state) => state.count,
// 傳字符串參數(shù) 'count' 等同于 `state => state.count`
countAlias: "count",
// 為了能夠使用 `this` 獲取局部狀態(tài)儿咱,必須使用常規(guī)函數(shù)
countPlusLocalState(state) {
return state.count + this.localCount;
},
}),
// 個(gè)人推薦這種寫法庭砍,給 mapState 傳一個(gè)字符串?dāng)?shù)組
// 當(dāng)映射的計(jì)算屬性的名稱與 state 的子節(jié)點(diǎn)名稱相同時(shí),我們也可以給 mapState 傳一個(gè)字符串?dāng)?shù)組混埠。
// 映射 this.count 為 store.state.count
// computed: mapState([
// // 映射 this.count 為 store.state.count
// 'count'
// ])
}
</script>
mapGetters
// 原寫法
<template>
<div>
<span>Shopping Cart ({{ cartItemCount }} items)</span>
</div>
</template>
<script>
export default {
computed: {
cartItemCount() {
return this.$store.getters.cartItemCount;
}
}
}
</script>
// 使用 mapGetters 輔助函數(shù)寫法
<template>
<div>
<span>Shopping Cart ({{ cartItemCount }} items)</span>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters(['cartItemCount'])
}
}
</script>
mapMutations
// 原寫法
<template>
<div>
<p>{{ customerName }}</p>
<input type="text" @input="updateName" :value="customerName" />
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "Example",
computed: {
...mapState(['customerName'])
},
methods: {
updateName(event) {
this.$store.commit('setCustomerName', event.target.value);
}
}
}
</script>
// 使用 mapMutations 輔助函數(shù)寫法
import { mapState, mapMutations } from 'vuex';
export default {
name: "Example",
computed: {
...mapState(['customerName'])
},
methods: {
...mapMutations(['setCustomerName']),
updateName(event) {
this.setCustomerName(event.target.value);
}
}
}
mapActions
// 原寫法
<template>
<div>
<p>{{ customerName }}</p>
<input type="text" @input="updateName" :value="customerName" />
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "Example",
computed: {
...mapState(['customerName'])
},
methods: {
updateName(event) {
this.$store.dispatch('setCustomerName', event.target.value);
}
}
}
</script>
// 使用 mapActions 輔助函數(shù)寫法
import { mapState, mapActions } from 'vuex';
export default {
name: "Example",
computed: {
...mapState(['customerName'])
},
methods: {
...mapActions (['setCustomerName']),
updateName(event) {
this.setCustomerName(event.target.value);
}
}
}
createNamespacedHelpers
- 如果要使用輔助函數(shù)引入
vuex
模塊化中的內(nèi)容怠缸,寫起來會(huì)比較繁瑣 - 一般解決方法有如下2兩種
// 原寫法,引入模塊化后相對繁瑣
// Component.vue
computed: {
...mapState({
a1: (state) => state.moduleA.a1,
a2: (state) => state.moduleA.a2,
})
},
methods: {
...mapActions({
foo: 'moduleA/foo',
boo: 'moduleA/boo',
}),
}
//解決方法一:可以將模塊化的空間名稱作為第一個(gè)參數(shù)傳入輔助函數(shù)钳宪,這樣就會(huì)自動(dòng)添加 前綴名 于是上面的例子可以簡化為如下寫法:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}
// 解決方法二:通過使用 createNamespacedHelpers 創(chuàng)建基于某個(gè)命名空間輔助函數(shù)揭北。進(jìn)行自動(dòng)綁定,使其中能自動(dòng)查找綁定的模塊名
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module` 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `some/nested/module` 中查找
...mapActions([
'foo',
'bar'
])
}
}
在vuex請求數(shù)據(jù)
-
之前網(wǎng)上看到有這種寫法吏颖,本人不是很喜歡這種寫法搔体,建議還是按照正常接口方案寫
//vuex 狀態(tài)文件 import Vue from "vue"; import Vuex from "vuex"; import axios from "axios"; Vue.use(Vuex); export default new Vuex.Store({ state: { users: [], isLoading: false, }, mutations: { setLoadingTrue(state) { state.isLoading = true; }, setLoadingFalse(state) { state.isLoading = false; }, setUsers(state, users) { state.users = users; }, setCustomerName(state, name) { state.customerName = name; } }, actions: { getUsers(context) { context.commit('setLoadingTrue'); axios.get('/api/users') .then(response => { context.commit('setUsers', response.data); context.commit('setLoadingFalse'); }) .catch(error => { context.commit('setLoadingFalse'); // handle error }); } } }); //組件文件 <template> <div> <div id="spinner" v-if="isLoading"> <img src="spinner.gif" /> </div> <ul v-else> <li v-for="(user, index) in users" :key="index" >{{ user }}</li> </ul> </div> </template> <script> import { mapActions, mapState } from "vuex"; export default { computed: { ...mapState([ 'isLoading', 'users' ]) }, methods: { ...mapActions(['getUsers']) }, created() { this.getUsers(); } } </script>
vuex 模塊化文件結(jié)構(gòu)參考
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API請求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我們組裝模塊并導(dǎo)出 store 的地方
├── actions.js # 根級別的 action
├── mutations.js # 根級別的 mutation
└── modules
├── cart.js # 購物車模塊
└── products.js # 產(chǎn)品模塊