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