如何使用 Vuex的入門(mén)教程
前言
本人曾對(duì) Vuex 作過(guò)詳細(xì)介紹,但是今天去回顧的時(shí)候發(fā)現(xiàn)文章思路有些繁瑣,不容易找到重點(diǎn)。于是,在下班前幾分鐘,我對(duì)其重新梳理了一遍。
tips:本文的案例均為Vue2版本。
一、基本概念
Vuex 是一個(gè)專(zhuān)為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
初識(shí)vuex
vuex有什么用?
比如, vue做了一個(gè)音樂(lè)app, 里面的播放組件, 這組件應(yīng)該是在所有的頁(yè)面都有用到的.
希望在所有的頁(yè)面都能控制播放器的 暫停/播放, 或者說(shuō)都可以選擇是否 隨便播放/單曲循環(huán).
這就涉及到了多組件之間的傳參,而且是非常復(fù)雜的傳參.這時(shí)候使用vuex是合適的.
二、項(xiàng)目場(chǎng)景
如果你的項(xiàng)目里有很多頁(yè)面(組件/視圖),頁(yè)面之間存在多級(jí)的嵌套關(guān)系,此時(shí),如果這些頁(yè)面都需要共享一個(gè)狀態(tài)的時(shí)候,此時(shí)就會(huì)產(chǎn)生以下兩個(gè)問(wèn)題:
- 多個(gè)視圖依賴(lài)同一個(gè)狀態(tài)
- 來(lái)自不同視圖的行為需要變更同一個(gè)狀態(tài)
解決方案(初版):
- 對(duì)于第一個(gè)問(wèn)題,假如是多級(jí)嵌套關(guān)系,你可以使用父子組件傳參進(jìn)行解決,雖有些麻煩,但好在可以解決;對(duì)于兄弟組件或者關(guān)系更復(fù)雜組件之間,雖然可以通過(guò)各種各樣的辦法解決,可實(shí)在很不優(yōu)雅,而且等項(xiàng)目做大了,代碼量愈發(fā)巨大,實(shí)在令人心煩。
- 對(duì)于第二個(gè)問(wèn)題,你可以通過(guò)父子組件直接引用,或者通過(guò)事件來(lái)變更或者同步狀態(tài)的多份拷貝,但是這種模式很脆弱,使得代碼難以維護(hù),而且同樣會(huì)讓代碼量劇增。
思路:
- 把各個(gè)組件都需要依賴(lài)的同一個(gè)狀態(tài)抽取出來(lái),全局統(tǒng)一管理。
- 在這種模式下,任何組件都可以直接訪問(wèn)到這個(gè)狀態(tài),或者當(dāng)狀態(tài)發(fā)生改變時(shí),所有的組件都相應(yīng)更新。
此時(shí),Vuex 就誕生了。 這就是它背后的基本思想,借鑒了 Flux、Redux。與其他模式不同的是,Vuex 是專(zhuān)門(mén)為 Vue 設(shè)計(jì)的狀態(tài)管理庫(kù),以利用 Vue.js 的細(xì)粒度數(shù)據(jù)響應(yīng)機(jī)制來(lái)進(jìn)行高效的狀態(tài)更新。
三、如何使用
1、安裝
進(jìn)入項(xiàng)目,鍵入以下命令:
npm install vuex --save
2、State 初始值
在 src 路徑下創(chuàng)建 store 文件夾,并在該文件夾下新增 index.js 文件:
import Vue from 'vue';
import Vuex from 'vuex';
?
Vue.use(Vuex);
?
const store = new Vuex.Store({
? ? state: {
? ? ? ? // 定義一個(gè)name,以供全局使用
? ? ? ? name: 'leo'
? ? }
});
?
export default store;修改 main.js:
import Vue from 'vue';
import App from './App';
import store from './store'; ? // 引入我們前面導(dǎo)出的store對(duì)象
?
Vue.config.productionTip = false;
?
new Vue({
? ? el: '#app',
? ? store, ? // 把store對(duì)象添加到vue實(shí)例上
? ? components: { App },
? ? template: '<App/>'
});最后修改 App.vue:
<template>
? ? <div></div>
</template>
?
<script>
? ? export default {
? ? ? ? mounted() {
? ? ? ? ? ? // 使用this.$store.state.xxx可以直接訪問(wèn)到倉(cāng)庫(kù)中的狀態(tài)
? ? ? ? ? ? console.log(this.$store.state.name); ? // 'leo'
? ? ? ? }
? ? }
</script>建議一:this.$store.state.xxx 放在計(jì)算屬性中,這樣可以讓你的代碼看起來(lái)更優(yōu)雅一些。
export default {
? ? mounted() {
? ? ? ? console.log(this.getName); ? // 'leo'
? ? },
? ? computed:{
? ? ? ? getName() {
? ? ? ? ? ? return this.$store.state.name;
? ? ? ? }
? ? }
}建議二:每次都寫(xiě) this.$store.state.xxx 讓你感到厭煩,造成代碼冗余,可以使用 mapState。
<script>
import { mapState } from 'vuex'; ? // 從vuex中導(dǎo)入mapState
export default {
? ? mounted() {
? ? ? ? console.log(this.name); ? // 'leo'
? ? },
? ? computed: {
? ? ? ? ...mapState(['name']) ? // 經(jīng)過(guò)解構(gòu)后,自動(dòng)就添加到了計(jì)算屬性中,此時(shí)就可以直接像訪問(wèn)計(jì)算屬性一樣訪問(wèn)它
? ? }
}
</script>你甚至可以給它取別名:
...mapState({ userName: 'name' }) ? // 賦別名的話,這里接收對(duì)象,而不是數(shù)組3、Getters 修飾值
設(shè)想一個(gè)場(chǎng)景,你已經(jīng)將 store 中的 name 展示到頁(yè)面上了,而且是很多頁(yè)面都展示了,此時(shí)客戶(hù)要求在每個(gè) name 前都加上“hello”。
最容易想到的辦法就是去每個(gè)用到 name 的頁(yè)面一個(gè)一個(gè)加上“hello”,顯然這種方式并不友好,原因如下:
第一,假如你在A、B、C三個(gè)頁(yè)面都用到了 name,那么你要在這A、B、C三個(gè)頁(yè)面都修改一遍,多個(gè)頁(yè)面你就要加很多遍這個(gè)方法,造成代碼冗余,很不好。
第二,假如下次客戶(hù)讓你把“hello”改成“greet”的時(shí)候,你又得把三個(gè)頁(yè)面(甚至更多)都改一遍,這時(shí)候你是拿石頭砸自己的腳。
吸取上面的教訓(xùn),我們有一個(gè)新的思路:直接在 store 中對(duì) name 進(jìn)行一些操作或者加工,從源頭解決問(wèn)題!這時(shí)候,Getter 閃亮登場(chǎng)!
首先,在 store 對(duì)象中增加 getters 屬性:
const store = new Vuex.Store({
? ? state: {
? ? ? ? name: 'leo'
? ? },
? ? // 在store對(duì)象中增加getters屬性
? ? getters: {
? ? ? ? getMessage(state) { ? // 獲取修飾后的name,第一個(gè)參數(shù)state為必要參數(shù),必須寫(xiě)在形參上
? ? ? ? ? ? return `hello ${state.name}`;
? ? ? ? }
? ? }
});在組件中使用:
export default {
? ? mounted() {
? ? ? ? console.log(this.$store.state.name);
? ? ? ? console.log(this.$store.getters.getMessage); ? // 注意不是$store.state了,而是$store.getters
? ? }
}建議使用 mapGetters:
<script>
import { mapState, mapGetters } from 'vuex';
export default {
? ? mounted() {
? ? ? ? console.log(this.name); ? // 'leo'
? ? ? ? console.log(this.getMessage); ? // 'hello leo'?
? ? },
? ? computed: {
? ? ? ? ...mapState(['name']),
? ? ? ? ...mapGetters(['getMessage'])
? ? },
}
</script>你也可以給它取別名:
...mapGetters({ greetMsg: 'getMessage' }) ? // 賦別名的話,這里接收對(duì)象,而不是數(shù)組4、Mutations 修改值
說(shuō)到修改值,有的人可能會(huì)想到這樣寫(xiě):
this.$store.state.xxx = XXX;
首先,這是一種錯(cuò)誤的寫(xiě)法。為什么說(shuō)是錯(cuò)誤的寫(xiě)法?因?yàn)檫@個(gè) store 倉(cāng)庫(kù)比較奇怪,你可以隨便拿,但是你不能隨便改,舉個(gè)例子:
假如你打開(kāi)朋友圈,看到你的好友發(fā)了條動(dòng)態(tài),但是動(dòng)態(tài)里有個(gè)錯(cuò)別字,你要怎么辦呢?你可以幫他改掉嗎?當(dāng)然不可以!我們只能通知他本人去修改,因?yàn)槭莿e人的朋友圈,你是無(wú)權(quán)操作的,只有他自己才能操作。同理,在 Vuex 中,我們不能直接修改倉(cāng)庫(kù)里的值,必須用 Vuex 自帶的方法去修改。這個(gè)時(shí)候,Mutation 閃亮登場(chǎng)!
首先,修改 store/index.js:
const store = new Vuex.Store({
? ? state: {
? ? ? ? name: 'leo',
? ? ? ? number: 0,
? ? },
? ? mutations: { ? // 增加nutations屬性
? ? ? ? setNumber(state) { ? // 增加一個(gè)mutations的方法,方法的作用是讓number從0變成5,state是必須默認(rèn)參數(shù)
? ? ? ? ? ? state.number = 5;
? ? ? ? }
? ? }
});然后,修改 App.vue:
<script>
export default {
? ? mounted() {
? ? ? ? console.log(this.$store.state.number); ? // 舊值,0
? ? ? ? this.$store.commit('setNumber');
? ? ? ? console.log(this.$store.state.number); ? // 新值,5
? ? },
}
</script>如果我們想傳動(dòng)態(tài)參數(shù)怎么辦?
首先,修改 store/index.js:
const store = new Vuex.Store({
? ? state: {
? ? ? ? name: 'leo',
? ? ? ? number: 0,
? ? },
? ? mutations: {
? ? ? ? setNumber(state, num) { ? // 增加一個(gè)帶參數(shù)的mutations方法
? ? ? ? ? ? state.number = num;
? ? ? ? },
? ? },
});相應(yīng)地,修改 App.vue:
<script>
export default {
? ? mounted() {
? ? ? ? console.log(this.$store.state.number); ? // 舊值,0
? ? ? ? this.$store.commit('setNumber', 666);
? ? ? ? console.log(this.$store.state.number); ? // 新值,666
? ? },
}
</script>上面的這種傳參的方式雖然可以達(dá)到目的,但是并不推薦。建議傳遞一個(gè)對(duì)象,看起來(lái)更美觀,對(duì)象的名字你可以隨意命名,一般命名為 payload:
mutations: {
? ? setNumber(state, payload) { ? // 增加一個(gè)帶參數(shù)的mutations方法,并且官方建議payload為一個(gè)對(duì)象
? ? ? ? state.number = payload.number;
? ? },
},相應(yīng)地,修改 App.vue:
<script>
export default {
? ? mounted() {
? ? ? ? console.log(this.$store.state.number); ? // 舊值,0
? ? ? ? this.$store.commit('setNumber', { number: 666 }); ?// 調(diào)用的時(shí)候需要傳遞一個(gè)對(duì)象
? ? ? ? console.log(this.$store.state.number); ? // 新值,666
? ? },
}
</script>建議使用 mapMutations:
<script>
import { mapMutations } from 'vuex';
export default {
? ? mounted() {
? ? ? ? this.setNumber({ number: 666 });
? ? },
? ? methods: { ? // 注意,mapMutations是解構(gòu)到methods里面的,而不是計(jì)算屬性了
? ? ? ? ...mapMutations(['setNumber'])
? ? }
}
</script>你也可以給它取別名:
methods:{
? ? ...mapMutations({ changeNumber: 'setNumber' }) ? // 賦別名的話,這里接收對(duì)象,而不是數(shù)組
}注意:Mutations 里面的函數(shù)必須是同步操作,不能包含異步操作。
Mutations 只能進(jìn)行同步操作,所以,我們馬上開(kāi)始下一節(jié),看看使用 Actions 進(jìn)行異步操作的時(shí)候應(yīng)該注意什么!
5、Actions 異步修改值
Vuex 不希望你將異步操作放在 Mutations 中,所以就給你設(shè)置了一個(gè)區(qū)域,讓你進(jìn)行異步操作,這就是 Actions。
首先,修改 store/index.js:
const store = new Vuex.Store({
? ? state: {
? ? ? ? name: 'leo',
? ? ? ? number: 0,
? ? },
? ? mutations: {
? ? ? ? setNumber(state, payload) {
? ? ? ? ? ? state.number = payload.number;
? ? ? ? },
? ? },
? ? actions: { ? // 增加actions屬性
? ? ? ? setNum(content) { ? // 增加setNum方法,默認(rèn)第一個(gè)參數(shù)是content,其值是復(fù)制的一份store
? ? ? ? ? ? return new Promise(resolve => { ?// 模擬異步操作,1秒后修改number為666
? ? ? ? ? ? ? ? setTimeout(() => {
? ? ? ? ? ? ? ? ? ? content.commit('setNumber', { number: 666 });
? ? ? ? ? ? ? ? ? ? resolve();
? ? ? ? ? ? ? ? }, 1000);
? ? ? ? ? ? });
? ? ? ? }
? ? }
});然后,修改 App.vue:
async mounted() {
? ? console.log(this.$store.state.number); ? // 舊值,0
? ? await this.$store.dispatch('setNum'); ? // 等待異步操作完成
? ? console.log(this.$store.state.number); ? // 新值,666
}其實(shí) action 就是去提交 mutation 的。異步操作都在 action 中消化了,最后再去提交 mutation。
當(dāng)然,你也可以進(jìn)行動(dòng)態(tài)傳參。
首先,修改 store/index.js:
actions: {
? ? setNum(content, payload) { ? // 增加payload參數(shù)
? ? ? ? return new Promise(resolve => {
? ? ? ? ? ? setTimeout(() => {
? ? ? ? ? ? ? ? content.commit('setNumber', { number: payload.number });
? ? ? ? ? ? ? ? resolve();
? ? ? ? ? ? }, 1000);
? ? ? ? });
? ? },
}相應(yīng)地,修改 App.vue:
async mounted() {
? ? console.log(this.$store.state.number); ? // 舊值,0
? ? await this.$store.dispatch('setNum', { number: 888 });
? ? console.log(this.$store.state.number); ? // 新值,888
}建議使用 mapActions:
<script>
import { mapActions } from 'vuex';
export default {
? ? methods: {
? ? ? ? ...mapActions(['setNum']), ? // 解構(gòu)到methods中
? ? },
? ? async mounted() {
? ? ? ? await this.setNum({ number: 888 }); ?// 直接調(diào)用即可
? ? }
}
</script>你也可以給它取別名:
...mapActions({ changeNumber: 'setNum' }) ? // 賦別名的話,這里接收對(duì)象,而不是數(shù)組tips:在 actions 里面,方法的形參可以直接將 commit 解構(gòu)出來(lái),方便后續(xù)操作。
actions: {
? ? setNum({ commit }) { ? // 直接將content結(jié)構(gòu)掉,解構(gòu)出commit,下面就可以直接調(diào)用了
? ? ? ? return new Promise(resolve => {
? ? ? ? ? ? setTimeout(() => {
? ? ? ? ? ? ? ? commit('xxx'); ?// 直接調(diào)用
? ? ? ? ? ? ? ? resolve();
? ? ? ? ? ? }, 1000);
? ? ? ? });
? ? },
},四、總結(jié)
下班前這幾分鐘,你知道了如何使用 Vuex。你會(huì)安裝它,配置它,讀取 state 的值,甚至修飾讀(Getters),然后你會(huì)修改里面的值(Mutations),如果有異步操作并且需要修改 state,那你就要使用 Actions。
一張神奇的圖。

五、建議
何時(shí)使用 Vuex ?
這個(gè)問(wèn)題因人而異,如果你不需要開(kāi)發(fā)大型的單頁(yè)應(yīng)用,此時(shí)你完全沒(méi)有必要使用 Vuex,比如你的頁(yè)面就兩三個(gè),使用 Vuex 后增加的文件比你現(xiàn)在的頁(yè)面還要多,完全沒(méi)這個(gè)必要。
假如你的項(xiàng)目達(dá)到了中大型應(yīng)用的規(guī)模,此時(shí)你很可能會(huì)考慮如何更好地在組件外部對(duì)狀態(tài)進(jìn)行統(tǒng)一管理,甚至?xí)帜K(Vuex 中的 module,不展開(kāi)),它將會(huì)是一個(gè)不錯(cuò)的選擇。
到此這篇關(guān)于如何使用 Vuex的入門(mén)教程的文章就介紹到這了,更多相關(guān)Vuex 入門(mén)教程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue多布局模式實(shí)現(xiàn)方法詳細(xì)講解
這篇文章主要介紹了Vue多布局模式實(shí)現(xiàn)方法,多布局模式可以根據(jù)用戶(hù)角色所在場(chǎng)景切換頁(yè)面布局,是非常常見(jiàn)的基礎(chǔ)功能,感興趣的同學(xué)可以參考下文2023-05-05
Vue中使用debugger在瀏覽器中不起作用的問(wèn)題及解決
這篇文章主要介紹了Vue中使用debugger在瀏覽器中不起作用的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
elementui中el-input回車(chē)搜索實(shí)現(xiàn)示例
這篇文章主要介紹了elementui中el-input回車(chē)搜索實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
Vue-CLI 3.X 部署項(xiàng)目至生產(chǎn)服務(wù)器的方法
這篇文章主要介紹了Vue-CLI 3.X 部署項(xiàng)目至生產(chǎn)服務(wù)器的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
vue實(shí)現(xiàn)圖片滑動(dòng)驗(yàn)證
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)圖片滑動(dòng)驗(yàn)證,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
深入探索VueJS Scoped CSS 實(shí)現(xiàn)原理
這篇文章主要介紹了深入探索VueJS Scoped CSS 實(shí)現(xiàn)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09

