详解Vuex常见问题、深入理解Vuex
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
狀態(tài)?我把它理解為在data中的屬性需要共享給其他vue組件使用的部分,就叫做狀態(tài)。簡(jiǎn)單的說(shuō)就是data中需要共用的屬性。
使用Vue開發(fā)項(xiàng)目時(shí),通常我們就會(huì)遇到如下幾種棘手的問(wèn)題:
問(wèn)題1:通過(guò)路由傳遞參數(shù),我們會(huì)采用params或者query形式,但這兩種方式都會(huì)在URL上做手腳,如果傳遞的參數(shù)過(guò)多,會(huì)導(dǎo)致400 Bad Request(如:點(diǎn)擊表格某行,攜帶行數(shù)據(jù)跳轉(zhuǎn)到新頁(yè)面進(jìn)行查看)。?
問(wèn)題2:兄弟組件傳值?
問(wèn)題3:多處地方使用同一數(shù)據(jù),為節(jié)省重復(fù)請(qǐng)求(最為常見(jiàn))
業(yè)務(wù)場(chǎng)景:
從A頁(yè)面攜帶下鉆參數(shù)(ID)到B頁(yè)面,然后B頁(yè)面獲取參數(shù)(ID)進(jìn)行數(shù)據(jù)請(qǐng)求。由于要下鉆的ID過(guò)長(zhǎng),受瀏覽器的URL長(zhǎng)度限制問(wèn)題,我們不能采用常規(guī)的prams(xxx.com/B/:ID)或query(xxx.com/B?ID=…)形式傳遞。可以使用Vuex做中間過(guò)渡,跳轉(zhuǎn)前存儲(chǔ)ID信息,進(jìn)入B頁(yè)面后從Vuex獲取ID信息。
如果用戶在B頁(yè)面刷新數(shù)據(jù),則Vuex的ID狀態(tài)值會(huì)被清空無(wú)法獲取,這里只能借助localStorage進(jìn)行持久化進(jìn)行處理(當(dāng)然,如果直接使用localstorage進(jìn)行持久化存儲(chǔ),而不借助Vuex也是可行的,但就是沒(méi)了響應(yīng)式的效果了)
一、常見(jiàn)使用問(wèn)題
1、Vuex,每次調(diào)用mutation之后向localstorage存值,防止刷新丟失
export default {state:{reportInfo:null},getters:{reportInfo(state){if(!state.reportInfo){state.reportInfo = JSON.parse(sessionStorage.getItem('reportInfo'))}return state.reportInfo}},mutations:{initReport:(state,data) => {state.reportInfo = datasessionStorage.setItem('reportInfo',JSON.stringify(data))},changeReportReview:(state,data) => {state.reportInfo.is_review = data;//改state的reportInfo里的某個(gè)值, 同時(shí)修改sessionStorage的值,以保證一樣sessionStorage.setItem('reportInfo',JSON.stringify(state.reportInfo))}} }?
2、調(diào)用時(shí)需要追加模塊名稱
如上的getters,首先從store中獲取,如果store中不存在則從localstorage中獲取(刷新)
3、頁(yè)面中如果有用戶登出操作,此時(shí)一般需要removeItem,如果頁(yè)面中有userInfo的判斷信息,如下一般需要先做個(gè)判斷userInfo存在才會(huì)去取userInfo.roleName的值,這樣就會(huì)防止頁(yè)面報(bào)錯(cuò),類似于后臺(tái)語(yǔ)言空指針的錯(cuò)誤
<span class="el-dropdown-link">{{userInfo ? (userInfo.account ? userInfo.account : userInfo.phoneNum) : ""}}<i class="iconfont icon-user"></i> </span> <router-link :to="'/account'" v-if="userInfo && userInfo.roleName === 'sys'"><el-dropdown-item>添加賬戶</el-dropdown-item> </router-link>如果沒(méi)有removeItem的操作的話,則是不需要這樣的。如下面的reportInfo,因?yàn)橛脩舻浅龅臅r(shí)候,不會(huì)影響到它
<div class="suggess_requert" v-if="suggess.suggestion_level === 3"><div v-if="reportInfo.is_review === 1"><router-link v-if="userInfo && userInfo.roleName === 'dba'" :to="'/list/review'"><h3>提交審核意見(jiàn)</h3></router-link><h3 v-else>專家正在審核,請(qǐng)稍后</h3></div><router-link v-else-if="reportInfo.is_review === 2" :to="'/list/reviewInfo/' + reportInfo.report_id"><h3>查看云和恩墨專家團(tuán)隊(duì)審核意見(jiàn)</h3></router-link><div v-else><h3 v-if="userInfo && userInfo.roleName === 'user'" @click="reviewRequest()">一鍵請(qǐng)求云和恩墨專家團(tuán)隊(duì)幫您二次審核</h3></div> </div>?
?4、state訪問(wèn)狀態(tài)對(duì)象
const state ,這個(gè)就是我們說(shuō)的訪問(wèn)狀態(tài)對(duì)象,它就是我們SPA(單頁(yè)應(yīng)用程序)中的共享值。
學(xué)習(xí)狀態(tài)對(duì)象賦值給內(nèi)部對(duì)象,也就是把stroe.js中的值,賦值給我們模板里data中的值。有三種賦值方式:
(1)通過(guò)computed的計(jì)算屬性直接賦值
computed屬性可以在輸出前,對(duì)data中的值進(jìn)行改變,我們就利用這種特性把store.js中的state值賦值給我們模板中的data值。
computed:{count(){return this.$store.state.count;} }這里需要注意的是return this.$store.state.count這一句,一定要寫this,要不你會(huì)找不到$store的。這種寫法很好理解,但是寫起來(lái)是比較麻煩的,那我們來(lái)看看第二種寫法。
(2)通過(guò)mapState的對(duì)象來(lái)賦值
我們首先要用import引入mapState。
import {mapState} from 'vuex';//然后還在computed計(jì)算屬性里寫如下代碼: computed:mapState({count:state=>state.count //理解為傳入state對(duì)象,修改為state.count屬性 })這里我們使用ES6的箭頭函數(shù)來(lái)給count賦值。
(3)通過(guò)mapState的數(shù)組來(lái)賦值
computed:mapState(["count"])這個(gè)算是最簡(jiǎn)單的寫法了,在實(shí)際項(xiàng)目開發(fā)當(dāng)中也經(jīng)常這樣使用
5、模板獲取Mutations方法
實(shí)際開發(fā)中我們也不喜歡看到$store.commit( )這樣的方法出現(xiàn),我們希望跟調(diào)用模板里的方法一樣調(diào)用。 例如:@click=”reduce” 就和沒(méi)引用vuex插件一樣。要達(dá)到這種寫法,只需要簡(jiǎn)單的兩部就可以了:
//1、用import 引入我們的mapMutations: import {mapGetters,mapMutations} from 'vuex' //2、添加methods屬性,并加入mapMutations ...mapMutations(['changeReportReview']),mapGetters、mapActions,都是一樣的用法
methods:{...mapMutations([ 'add','reduce']),...mapActions(['addAction','reduceAction']) },需要注意的是,(1)調(diào)用的時(shí)候都是需要加上this才能訪問(wèn)到的;(2)getters/mutations/actions這些如果沒(méi)帶命名空間的話,就算是寫在module模塊里的這些方法,但是在使用的時(shí)候,是不需要加上模塊名稱的,而state是要加上模塊名稱的
二、狀態(tài)管理模式
“單向數(shù)據(jù)流”理念的極簡(jiǎn)示意:
- state,驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源;
- view,以聲明方式將?state?映射到視圖;
- actions,響應(yīng)在?view?上的用戶輸入導(dǎo)致的狀態(tài)變化。
我們?cè)陂_發(fā)中會(huì)遇到多個(gè)組件共享狀態(tài)時(shí),單向數(shù)據(jù)流的簡(jiǎn)潔性很容易被破壞。
- 多個(gè)視圖依賴于同一狀態(tài)。
- 來(lái)自不同視圖的行為需要變更同一狀態(tài)。
對(duì)于問(wèn)題一,傳參的方法對(duì)于多層嵌套的組件將會(huì)非常繁瑣,并且對(duì)于兄弟組件(非父子組件)間的狀態(tài)傳遞無(wú)能為力;
對(duì)于問(wèn)題二,我們經(jīng)常會(huì)采用父子組件直接引用或者通過(guò)事件來(lái)變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會(huì)導(dǎo)致無(wú)法維護(hù)的代碼。
三、Vuex簡(jiǎn)介
Vuex 和單純的全局對(duì)象有以下兩點(diǎn)不同:
- Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新。
- 不能直接改變 store 中的狀態(tài)。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用。
四、State
Vuex 使用單一狀態(tài)樹,這可以讓我們能夠直接地定位任一特定的狀態(tài)片段,在調(diào)試的過(guò)程中也能輕易地取得整個(gè)當(dāng)前應(yīng)用狀態(tài)的快照。需要注意,單狀態(tài)樹和模塊化并不沖突!
由于 store 中的狀態(tài)是響應(yīng)式的,在組件中調(diào)用 store 中的狀態(tài)簡(jiǎn)單到僅需要在計(jì)算屬性中返回即可。
computed: {count () {return store.state.count // this.$store.state.count} }mapState 輔助函數(shù)
當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)時(shí)候,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余。為了解決這個(gè)問(wèn)題,我們可以使用?mapState?輔助函數(shù)幫助我們生成計(jì)算屬性。
import { mapState } from 'vuex'computed: mapState({// 映射 this.count 為 store.state.count'count',// 箭頭函數(shù)可使代碼更簡(jiǎn)練count: state => state.count,// 傳字符串參數(shù) 'count' 等同于 `state => state.count`countAlias: 'count',// 為了能夠使用 `this` 獲取局部狀態(tài),必須使用常規(guī)函數(shù)countPlusLocalState (state) {return state.count + this.localCount},// 使用對(duì)象展開運(yùn)算符將此對(duì)象混入到外部對(duì)象中...mapState({// ...}) }) ?五、Getter
Getter(state, getters)可以從 store 中的 state 中派生出一些狀態(tài)(如,對(duì)數(shù)據(jù)進(jìn)行過(guò)濾操作)。對(duì)于多個(gè)組件需要用同一屬性時(shí),意義重大!類似于計(jì)算屬性,getter 的返回值會(huì)根據(jù)它的依賴被緩存起來(lái),且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算。
完整請(qǐng)參照?https://vuex.vuejs.org/zh-cn/getters.html
六、Mutation
mutation 必須是同步函數(shù)!!!
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。
Vuex 中的 mutation 非常類似于事件:每個(gè) mutation 都有一個(gè)字符串的?事件類型 (type)?和 一個(gè)?回調(diào)函數(shù) (handler)。調(diào)用?store.commit(type, payload)?方法來(lái)觸發(fā)mutations中的相關(guān)方法。
mutations: {increment (state, n) {state.count += n} }store.commit('increment', 10) ?注意:Mutation 需遵守 Vue 的響應(yīng)規(guī)則
-
最好提前在你的 store 中初始化好所有所需屬性
-
當(dāng)需要在對(duì)象上添加新屬性時(shí),你應(yīng)該
-
使用?Vue.set(obj, 'newProp', 123), 或者
-
以新對(duì)象替換老對(duì)象。例如,利用 stage-3 的對(duì)象展開運(yùn)算符我們可以這樣寫
-
完整請(qǐng)參照:https://vuex.vuejs.org/zh-cn/mutations.html
七、Action
Action 類似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接變更狀態(tài)。
- Action 可以包含任意異步操作。
- 通過(guò)?store.dispatch?方法觸發(fā)
組合 Action:store.dispatch?可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且?store.dispatch?仍舊返回 Promise。
actions: {actionA ({ commit }) {return new Promise((resolve, reject) => {setTimeout(() => {commit('someMutation')resolve()}, 1000)})} } //現(xiàn)在你可以: store.dispatch('actionA').then(() => {// ... })?
?在另外一個(gè) action 中也可以:
// 假設(shè) getData() 和 getOtherData() 返回的是 Promise actions: {async actionA ({ commit }) {commit('gotData', await getData())},async actionB ({ dispatch, commit }) {await dispatch('actionA') // 等待 actionA 完成commit('gotOtherData', await getOtherData())} } ?一個(gè)?store.dispatch?在不同模塊中可以觸發(fā)多個(gè) action 函數(shù)。在這種情況下,只有當(dāng)所有觸發(fā)函數(shù)完成后,返回的 Promise 才會(huì)執(zhí)行。
完整請(qǐng)參照:https://vuex.vuejs.org/zh-cn/actions.html
八、Module
由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對(duì)象。為了解決以上問(wèn)題,Vuex 允許我們將 store 分割成模塊(module)。
默認(rèn)情況下,模塊內(nèi)部的 action、mutation 和 getter 是注冊(cè)在全局命名空間的——這樣使得多個(gè)模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)。如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過(guò)添加?namespaced: true?的方式使其成為命名空間模塊。當(dāng)模塊被注冊(cè)后,它的所有 getter、action 及 mutation 都會(huì)自動(dòng)根據(jù)模塊注冊(cè)的路徑調(diào)整命名。
九、插件
Vuex 的 store 接受?plugins?選項(xiàng),這個(gè)選項(xiàng)暴露出每次 mutation 的鉤子。Vuex 插件就是一個(gè)函數(shù),它接收 store 作為唯一參數(shù)。
const myPlugin = store => {// 當(dāng) store 初始化后調(diào)用store.subscribe((mutation, state) => {// 每次 mutation 之后調(diào)用// mutation 的格式為 { type, payload }}) } ?然后像這樣使用:
const store = new Vuex.Store({// ...plugins: [myPlugin] })項(xiàng)目中我們會(huì)使用plugin來(lái)初始化一些數(shù)據(jù)
const initActionList = ['base/' + INIT_BUSINESS_SYSTEM_LIST,'threat/' + INIT_STD_COEFFICIENT_LIST ] export default function (store) {for (let action of initActionList) {Bus.$once(action, () => {store.dispatch(action)})} } ?但是 ,使用plugin的Bus.$once去初始化請(qǐng)求,而不再每個(gè)使用模塊自身dispatch。會(huì)有解決不掉的兩個(gè)問(wèn)題:
- 點(diǎn)擊某個(gè)按鈕觸發(fā)相關(guān)數(shù)據(jù)($once只適合初始化時(shí)請(qǐng)求)
- 某請(qǐng)求依賴store中的情況(刷新)await dispatch('actionA') // 等待 actionA 完成
十、表單處理
當(dāng)在嚴(yán)格模式中使用 Vuex 時(shí),在屬于 Vuex 的 state 上使用?v-model?會(huì)比較棘手
<input v-model="obj.message">在用戶輸入時(shí),v-model?會(huì)試圖直接修改?obj.message。在嚴(yán)格模式中,由于這個(gè)修改不是在 mutation 函數(shù)中執(zhí)行的, 這里會(huì)拋出一個(gè)錯(cuò)誤。使用傳統(tǒng)的value+input事件實(shí)現(xiàn),但是比較啰嗦。
<input :value="message" @input="updateMessage">computed: {...mapState({message: state => state.obj.message}) }, methods: {updateMessage (e) {this.$store.commit('updateMessage', e.target.value)} }這里,可以使用雙向綁定的計(jì)算屬性:
computed: {message: {get () {return this.$store.state.obj.message},set (value) {this.$store.commit('updateMessage', value)}} }總結(jié):使用 Vuex 并不意味著你需要將所有的狀態(tài)放入 Vuex。雖然將所有的狀態(tài)放到 Vuex 會(huì)使?fàn)顟B(tài)變化更顯式和易調(diào)試,但也會(huì)使代碼變得冗長(zhǎng)和不直觀。如果有些狀態(tài)嚴(yán)格屬于單個(gè)組件,最好還是作為組件的局部狀態(tài)。你應(yīng)該根據(jù)你的應(yīng)用開發(fā)需要進(jìn)行權(quán)衡和確定。
總結(jié)
以上是生活随笔為你收集整理的详解Vuex常见问题、深入理解Vuex的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: MySQL
- 下一篇: armv8 汇编 绝对地址赋值_详解汇编