一蒜鸡、初始化Vuex
Vuex是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式瓢姻。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài)肉康,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。
如果一份數(shù)據(jù)需要在多個(gè)組件中使用诫钓,組件間傳值又比較復(fù)雜赋咽,就可以使用vuex托管數(shù)據(jù)。
1芹扭、安裝
npm install vuex --save
2麻顶、導(dǎo)入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
3、創(chuàng)建狀態(tài)管理對象 store
state選項(xiàng):定義狀態(tài)(狀態(tài)就是數(shù)據(jù))舱卡。
mutations選項(xiàng):定義修改狀態(tài)的方法(注意:這里面只能定義同步方法)辅肾。
export default new Vuex.Store({
// 定義全局狀態(tài)(狀態(tài)就是數(shù)據(jù))
state:{
car:{
name:'奔馳',
price:'40W'
}
},
// 定義修改狀態(tài)的方法
mutations:{
//該方法,修改汽車信息
updateCar(state,val){
state.car = val
}
}
})
4轮锥、注冊給Vue
// 導(dǎo)入當(dāng)前項(xiàng)目中的全局狀態(tài)管理對象
import store from './store'
new Vue({
// 在vue實(shí)例中使用全局狀態(tài)管理對象
store,
render: h => h(App)
}).$mount('#app')
5矫钓、簡單使用
$store:返回的是當(dāng)前項(xiàng)目中的全局狀態(tài)對象。
commit()方法:用于執(zhí)行指定的mutations里面的方法。
(1)獲取數(shù)據(jù)
在組件中新娜,直接通過$store.state就可以獲取到全局狀態(tài)對象管理的狀態(tài)數(shù)據(jù)赵辕,直接渲染到頁面。
<div>車輛名稱:{{ $store.state.car.name }}</div>
<div>車輛價(jià)格:{{ $store.state.car.price }}</div>
(2)修改數(shù)據(jù)
<div>車輛名稱:{{ $store.state.car.name }}</div>
<div>車輛價(jià)格:{{ $store.state.car.price }}</div>
<button @click="updateCar">修改汽車信息</button>
methods: {
updateCar() {
this.$store.commit("updateCar", { name: "奔馳", price: "60W" });
}
}
二杯活、核心概念
1匆帚、state
state選項(xiàng):定義狀態(tài)(狀態(tài)就是數(shù)據(jù))熬词。
state: {
name:'張三'
}
通過$store.state.數(shù)據(jù)名使用旁钧。
<div>姓名:{{ $store.state.name }}</div>
2、getters
getters選項(xiàng):定義計(jì)算屬性互拾。方法的參數(shù)是狀態(tài)對象歪今。
getters:{
// 方法的第一個(gè)參數(shù)是全局狀態(tài)
nameInfo(state){
return `我的名字叫${state.name}`
}
}
通過$store.getters.屬性名使用計(jì)算屬性。
<div>{{ $store.getters.nameInfo }}</div>
3颜矿、mutations
mutations選項(xiàng):定義修改狀態(tài)的方法(注意:這里的方法一般都是同步方法)寄猩。方法的第一個(gè)參數(shù)是狀態(tài)對象,第二個(gè)參數(shù)是新值骑疆。
mutations:{
// 修改姓名
updateName(state,val){
state.name = val
}
},
通過commit()方法田篇,調(diào)用mutations里面的方法。
this.$store.commit("updateName", '李四');
4箍铭、actions
actions選項(xiàng):定義操作狀態(tài)的方法(這里的方法可以定義異步方法)泊柬。
注意:actions里的方法最好不要直接操作state狀態(tài),而是通過調(diào)用mutations里面的方法去修改狀態(tài)诈火。所以兽赁,actions直接操作的是mutations。
state: {
carAddress:'意大利'
},
mutations:{
//修改汽車的產(chǎn)地
updateCarAddress(state,val){
state.carAddress = val
}
},
actions:{
//修改汽車的產(chǎn)地
//方法的第一個(gè)參數(shù)是全局狀態(tài)管理對象冷守,第二個(gè)參數(shù)是具體的值
updateCarAddress(store,val){
axios.get(val).then(({data})=>{
// 方式一:這里可以直接修改狀態(tài)
// store.state.carAddress = data.address
// 方式二:通過調(diào)用mutations里面的方法修改狀態(tài)
store.commit('updateCarAddress',data.address)
})
}
}
通過dispatch()方法刀崖,調(diào)用actions里面定義的方法。
this.$store.dispatch('updateCarAddress','data/address.json')
5拍摇、modules
由于使用單一狀態(tài)樹亮钦,應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時(shí)充活,store 對象就有可能變得相當(dāng)臃腫或悲。
為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)堪唐。每個(gè)模塊擁有自己的 state巡语、mutation、action淮菠、getter男公、甚至是嵌套子模塊。
(1)定義模塊
namespaced屬性:默認(rèn)情況下,action枢赔、mutation 和 getter 是注冊在全局命名空間的澄阳。通過設(shè)置namespaced屬性為true,將action踏拜、mutation 和 getter全部注冊到私有命名空間中碎赢。
export default {
namespaced:true,
// 狀態(tài)
state:{
planeName:'空客404',
planePrice:'10Y',
planeAddress:'中國'
},
// 計(jì)算屬性
getters:{
planeInfo(state){
return `飛機(jī)名稱:${state.planeName},飛機(jī)價(jià)格:${state.planePrice}速梗,飛機(jī)產(chǎn)地:${state.planeAddress}`
}
},
// 同步方法
mutations:{
updatePlaneName(state,val){
state.planeName = val
},
updatePlanePrice(state,val){
state.planePrice = val
}
},
// 異步方法
actions:{
updatePlanePrice(store,val){
setTimeout(() => {
store.commit('updatePlanePrice',val)
}, 500);
}
}
}
(2)在全局狀態(tài)管理對象中導(dǎo)入模塊
// 導(dǎo)入飛機(jī)模塊
import plane from './modules/plane.js'
// 創(chuàng)建一個(gè)全局狀態(tài)管理對象并導(dǎo)出
export default new Vuex.Store({
// 模塊
modules:{
// 飛機(jī)模塊
plane
}
})
(3)使用模塊
① 獲取模塊中的state狀態(tài)
要從私有模塊中獲取數(shù)據(jù)肮塞,方式是:$store.state.模塊名稱.模塊的數(shù)據(jù) 。
<div>飛機(jī)名稱:{{ planeName }}</div>
planeName() {
return this.$store.state.plane.planeName;
}
② 獲取模塊中的getters計(jì)算屬性
要從私有模塊中獲取計(jì)算屬性姻锁,方式是:$store.getters['模塊名/計(jì)算屬性']枕赵。
<div>{{planeInfo}}</div>
planeInfo(){
return this.$store.getters['plane/planeInfo']
}
③ 調(diào)用模塊中的mutations定義的方法
調(diào)用私有模塊里面的mutations定義的方法,方式是:$store.commit('模塊名/方法名',新值)位隶。
<button @click="updatePlaneName">修改飛機(jī)名稱</button>
updatePlaneName(){
this.$store.commit('plane/updatePlaneName','波音747')
}
④ 調(diào)用模塊中的actions定義的方法
調(diào)用私有模塊里面的actions定義的方法拷窜,方式是:$store.dispatch('模塊名/方法名',新值)。
<button @click="updatePlanePrice">修改飛機(jī)價(jià)格</button>
updatePlanePrice(){
this.$store.dispatch('plane/updatePlanePrice','20Y')
}
三涧黄、Vuex使用
1篮昧、計(jì)算屬性中轉(zhuǎn)
直接在模板中使用全局狀態(tài)管理數(shù)據(jù),表達(dá)式會(huì)寫的很長笋妥。所以可以使用計(jì)算屬性懊昨。
// getters選項(xiàng)定義計(jì)算屬性
getters:{
carInfo(state){
return `汽車名稱:${state.carName},汽車價(jià)格:${state.carPrice}挽鞠,汽車產(chǎn)地:${state.carAddress}`
}
}
<!-- 直接在模板中使用全局狀態(tài)里面的計(jì)算屬性 -->
<div>{{$store.getters.carInfo}}</div>
<div>{{carInfo}}</div>
//計(jì)算屬性
computed:{
// 汽車信息
carInfo(){
// 返回全局狀態(tài)管理里面的計(jì)算屬性
return this.$store.getters.carInfo
}
}
2疚颊、映射函數(shù)
通過映射函數(shù)mapState、mapGetters信认、mapActions材义、mapMutations,可以將vuex.store中的屬性映射到vue實(shí)例身上嫁赏,這樣在vue實(shí)例中就能訪問vuex.store中的屬性了其掂,便于操作vuex.store。
(1)導(dǎo)入映射函數(shù)
// 從vuex中潦蝇,導(dǎo)入映射函數(shù)
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
(2)使用映射函數(shù)生成計(jì)算屬性
如果vuex里面state的數(shù)據(jù)名稱 跟 頁面中的計(jì)算屬性名稱相同款熬,就可以使用mapState映射函數(shù),自動(dòng)生成頁面中的計(jì)算屬性攘乒。
如果vuex里面getters的數(shù)據(jù)名稱 跟 頁面中的計(jì)算屬性名稱相同贤牛,就可以使用mapGetters映射函數(shù),自動(dòng)生成頁面中的計(jì)算屬性则酝。
注意:如果要映射模塊里面的state/getters殉簸,函數(shù)的第一個(gè)參數(shù)設(shè)置為模塊的名稱。
<div>汽車名稱:{{ carName }}</div>
<div>汽車價(jià)格:{{ carPrice }}</div>
<div>汽車產(chǎn)地:{{ carAddress }}</div>
<div>{{ carInfo }}</div>
<hr/>
<div>飛機(jī)名稱:{{ planeName }}</div>
<div>飛機(jī)價(jià)格:{{ planePrice }}</div>
<div>飛機(jī)產(chǎn)地:{{ planeAddress }}</div>
<div>{{ planeInfo }}</div>
computed: {
// mapState映射state
...mapState(["carName", "carPrice", "carAddress"]),
// mapGetters映射getters
...mapGetters(["carInfo"]),
// 映射私有模塊里面的數(shù)據(jù)
...mapState('plane',['planeName','planePrice','planeAddress']),
...mapGetters('plane',['planeInfo'])
}
(3)使用映射函數(shù)生成方法
如果定義的方法名跟全局管理對象中mutations里面的方法名相同,并且定義的方法會(huì)帶有一個(gè)參數(shù)般卑,通過參數(shù)傳遞數(shù)據(jù)武鲁。滿足該規(guī)則,就可以使用mapMutations映射函數(shù)生成方法蝠检。
如果定義的方法名跟全局管理對象中actions里面的方法名相同沐鼠,并且定義的方法會(huì)帶有一個(gè)參數(shù),通過參數(shù)傳遞數(shù)據(jù)叹谁。滿足該規(guī)則饲梭,就可以使用mapActions映射函數(shù)生成方法。
注意:如果要映射私有模塊中mutations/actions里面的方法本慕,函數(shù)的第一個(gè)參數(shù)設(shè)置為模塊的名稱排拷。
<button @click="updateCarName('賓利')">修改汽車名稱</button>
<button @click="updateCarPrice('300W')">修改汽車價(jià)格</button>
<button @click="updateCarAddress('data/address.json')">修改汽車產(chǎn)地</button>
<hr/>
<button @click="updatePlaneName('波音747')">修改飛機(jī)名稱</button>
<button @click="updatePlanePrice('20Y')">修改飛機(jī)價(jià)格</button>
methods: {
// 映射全局管理對象中mutations里面的方法
...mapMutations(["updateCarName", "updateCarPrice"]),
// 映射全局管理對象中actions里面的方法
...mapActions(["updateCarAddress"]),
// 映射私有模塊里面的方法
...mapMutations('plane',['updatePlaneName']),
...mapActions('plane',['updatePlanePrice'])
}
3侧漓、購物車模塊
import axios from "axios";
export default {
namespaced: true,
state: {
//商品數(shù)組
goodses: [],
},
getters: {
//總價(jià)
totalPrice(state) {
return state.goodses.filter((r) => r.ck).map((r) => r.price * r.count).reduce((a, b) => a + b, 0);
},
// 是否全選
isCkAll(state) {
return state.goodses.length>0 & state.goodses.every((r) => r.ck);
},
},
mutations: {
// 加載數(shù)據(jù)的同步方法
loadGoodses(state, val) {
state.goodses = val;
},
// 設(shè)置所有商品的狀態(tài)
ckAll(state, val) {
state.goodses.forEach((r) => {
r.ck = val;
});
},
// 根據(jù)id刪除商品
delGoodsById(state, id) {
let index = state.goodses.findIndex((r) => r.id == id);
state.goodses.splice(index, 1);
},
},
actions: {
// 加載數(shù)據(jù)的異步方法
loadGoodses(store, val) {
axios.get(val).then(({ data }) => {
store.commit("loadGoodses", data);
});
},
// 設(shè)置所有商品的狀態(tài)
ckAll(store, val) {
store.commit("ckAll", val);
},
// 根據(jù)id刪除商品
delGoodsById(store, id) {
store.commit("delGoodsById", id);
},
},
};
<template>
<div class="shopcart">
<h4>購物車</h4>
<table>
<thead>
<tr>
<th>
<input type="checkbox" v-model="isCkAll" />
</th>
<th>名稱</th>
<th>圖片</th>
<th>單價(jià)</th>
<th>數(shù)量</th>
<th>小計(jì)</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in goodses" :key="item.id">
<td>
<input type="checkbox" v-model="item.ck" />
</td>
<td>{{ item.name }}</td>
<td>
<img :src="item.img" />
</td>
<td>{{ item.price }}</td>
<td>
<button @click="item.count--" :disabled="item.count === 1">
-
</button>
<input type="text" v-model="item.count" />
<button @click="item.count++" :disabled="item.count === 10">
+
</button>
</td>
<td>{{ item.price * item.count }}</td>
<td>
<button @click="delGoodsById(item.id)">刪除</button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="7">
<span>總價(jià):</span>
<span>{{ totalPrice }}</span>
</td>
</tr>
</tfoot>
</table>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from "vuex";
export default {
name: "Shopcart",
computed: {
...mapState("shopcart", ["goodses"]),
...mapGetters("shopcart", ["totalPrice"]),
// 定義可以修改的計(jì)算屬性
isCkAll: {
set(val) {
this.ckAll(val);
},
get() {
return this.$store.getters["shopcart/isCkAll"];
},
},
},
methods: {
// 映射actions對應(yīng)的方法
...mapActions("shopcart", ["ckAll", "delGoodsById"]),
},
// 頁面掛載完成
mounted() {
this.$store.dispatch("shopcart/loadGoodses", "data/shopcart.json");
},
};
</script>