? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Vuex的知識點
1.vuex是什么?
Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式紧卒。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化亲澡。Vuex 也集成到 Vue 的官方調(diào)試工具?devtools extension唉地,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級調(diào)試功能岳守。
1.2 什么是“狀態(tài)管理模式”?
這個狀態(tài)自管理應(yīng)用包含以下幾個部分:
state碌冶,驅(qū)動應(yīng)用的數(shù)據(jù)源湿痢;
view,以聲明方式將?state?映射到視圖扑庞;
actions譬重,響應(yīng)在?view?上的用戶輸入導(dǎo)致的狀態(tài)變化。
2.State
2.1單一狀態(tài)樹
Vuex 使用單一狀態(tài)樹——是的罐氨,用一個對象就包含了全部的應(yīng)用層級狀態(tài)臀规。至此它便作為一個“唯一數(shù)據(jù)源 (SSOT)”而存在。這也意味著栅隐,每個應(yīng)用將僅僅包含一個 store 實例塔嬉。單一狀態(tài)樹讓我們能夠直接地定位任一特定的狀態(tài)片段,在調(diào)試的過程中也能輕易地取得整個當前應(yīng)用狀態(tài)的快照租悄。
單狀態(tài)樹和模塊化并不沖突——在后面的章節(jié)里我們會討論如何將狀態(tài)和狀態(tài)變更事件分布到各個子模塊中谨究。
存儲在 Vuex 中的數(shù)據(jù)和 Vue 實例中的?data?遵循相同的規(guī)則,例如狀態(tài)對象必須是純粹 (plain) 的
2.2在 Vue 組件中獲得 Vuex 狀態(tài)
那么我們?nèi)绾卧?Vue 組件中展示狀態(tài)呢恰矩?由于 Vuex 的狀態(tài)存儲是響應(yīng)式的记盒,從 store 實例中讀取狀態(tài)最簡單的方法就是在計算屬性中返回某個狀態(tài):
創(chuàng)建一個 Counter 組件constCounter={template:`
<div>{{ count }}</div>`,
computed:{count(){returnstore.state.count}}}
2.3mapState?輔助函數(shù)
當一個組件需要獲取多個狀態(tài)時候,將這些狀態(tài)都聲明為計算屬性會有些重復(fù)和冗余外傅。為了解決這個問題纪吮,我們可以使用?mapState?輔助函數(shù)幫助我們生成計算屬性,讓你少按幾次鍵:
對象展開運算符
mapState?函數(shù)返回的是一個對象萎胰。我們?nèi)绾螌⑺c局部計算屬性混合使用呢碾盟?通常,我們需要使用一個工具函數(shù)將多個對象合并為一個技竟,以使我們可以將最終對象傳給?computed?屬性冰肴。但是自從有了對象展開運算符,我們可以極大地簡化寫法:
computed:{localComputed(){/* ... */},
// 使用對象展開運算符將此對象混入到外部對象中...mapState({// ...})}
3Getter
3.1通過屬性訪問
Getter 會暴露為?store.getters?對象,你可以以屬性的形式訪問這些值:
store.getters.doneTodos// -> [{ id: 1, text: '...', done: true }]
2通過方法訪問
你也可以通過讓 getter 返回一個函數(shù)熙尉,來實現(xiàn)給 getter 傳參联逻。在你對 store 里的數(shù)組進行查詢時非常有用。
getters:{// ...getTodoById:(state)=>(id)=>{returnstate.todos.find(todo=>todo.id===id)}}
4 Mutation
mutation:更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation检痰。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的?事件類型 (type)?和 一個?回調(diào)函數(shù) (handler)包归。這個回調(diào)函數(shù)就是我們實際進行狀態(tài)更改的地方,
4.1對象風(fēng)格的提交方式
提交 mutation 的另一種方式是直接使用包含?type?屬性的對象:
store.commit({type:'increment',amount:10})
Mutation 需遵守 Vue 的響應(yīng)規(guī)則
既然 Vuex 的 store 中的狀態(tài)是響應(yīng)式的铅歼,那么當我們變更狀態(tài)時公壤,監(jiān)視狀態(tài)的 Vue 組件也會自動更新。這也意味著 Vuex 中的 mutation 也需要與使用 Vue 一樣遵守一些注意事項:
最好提前在你的 store 中初始化好所有所需屬性
5.Action
Action 類似于 mutation椎椰,不同在于:
Action 提交的是 mutation厦幅,而不是直接變更狀態(tài)。
Action 可以包含任意異步操作慨飘。
分發(fā) Action
Action 通過?store.dispatch?方法觸發(fā):
store.dispatch('increment')
乍一眼看上去感覺多此一舉确憨,我們直接分發(fā) mutation 豈不更方便?實際上并非如此套媚,還記得?mutation 必須同步執(zhí)行這個限制么缚态?Action 就不受約束磁椒!我們可以在 action 內(nèi)部執(zhí)行異步操作:
actions:{incrementAsync({commit}){setTimeout(()=>{commit('increment')},1000)}}
6.Module
Vuex 允許我們將 store 分割成模塊(module)堤瘤。每個模塊擁有自己的 state、mutation浆熔、action本辐、getter、甚至是嵌套子模塊
constmoduleA={state:{...},mutations:{...},actions:{...},getters:{...}}
constmoduleB={state:{...},mutations:{...},actions:{...}}
conststore=newVuex.Store({modules:{a:moduleA,b:moduleB}})
7.項目結(jié)構(gòu)
Vuex 并不限制你的代碼結(jié)構(gòu)医增。但是慎皱,它規(guī)定了一些需要遵守的規(guī)則:
應(yīng)用層級的狀態(tài)應(yīng)該集中到單個 store 對象中。
提交?mutation?是更改狀態(tài)的唯一方法叶骨,并且這個過程是同步的茫多。
異步邏輯都應(yīng)該封裝到?action?里面。
8.插件
Vuex 的 store 接受?plugins?選項忽刽,這個選項暴露出每次 mutation 的鉤子天揖。Vuex 插件就是一個函數(shù),它接收 store 作為唯一參數(shù):
constmyPlugin=store=>{// 當 store 初始化后調(diào)用store.subscribe((mutation,state)=>{// 每次 mutation 之后調(diào)用// mutation 的格式為 { type, payload }})}
8.1在插件內(nèi)提交 Mutation
在插件中不允許直接修改狀態(tài)——類似于組件跪帝,只能通過提交 mutation 來觸發(fā)變化今膊。
9.嚴格模式
開啟嚴格模式,僅需在創(chuàng)建 store 的時候傳入?strict: true:
conststore=newVuex.Store({// ...strict:true})
在嚴格模式下伞剑,無論何時發(fā)生了狀態(tài)變更且不是由 mutation 函數(shù)引起的斑唬,將會拋出錯誤。這能保證所有的狀態(tài)變更都能被調(diào)試工具跟蹤到。
9.2開發(fā)環(huán)境與發(fā)布環(huán)境
不要在發(fā)布環(huán)境下啟用嚴格模式恕刘!嚴格模式會深度監(jiān)測狀態(tài)樹來檢測不合規(guī)的狀態(tài)變更——請確保在發(fā)布環(huán)境下關(guān)閉嚴格模式缤谎,以避免性能損失。
類似于插件褐着,我們可以讓構(gòu)建工具來處理這種情況:
conststore=newVuex.Store({// ...strict:process.env.NODE_ENV!=='production'})
10表單處理
當在嚴格模式中使用 Vuex 時弓千,在屬于 Vuex 的 state 上使用?v-model?會比較棘手:
<inputv-model="obj.message">
假設(shè)這里的?obj?是在計算屬性中返回的一個屬于 Vuex store 的對象,在用戶輸入時献起,v-model?會試圖直接修改?obj.message洋访。在嚴格模式中,由于這個修改不是在 mutation 函數(shù)中執(zhí)行的, 這里會拋出一個錯誤谴餐。
用“Vuex 的思維”去解決這個問題的方法是:給?<input>?中綁定 value姻政,然后偵聽?input?或者?change?事件,在事件回調(diào)中調(diào)用一個方法:
<input:value="message"@input="updateMessage">
...computed:{...mapState({message:state=>state.obj.message})},methods:{updateMessage(e){this.$store.commit('updateMessage',e.target.value)}}
下面是 mutation 函數(shù):
...mutations:{updateMessage(state,message){state.obj.message=message}}
雙向綁定的計算屬性
必須承認岂嗓,這樣做比簡單地使用“v-model?+ 局部狀態(tài)”要啰嗦得多汁展,并且也損失了一些?v-model?中很有用的特性。另一個方法是使用帶有 setter 的雙向綁定計算屬性:
<inputv-model="message">
...computed:{message:{get(){returnthis.$store.state.obj.message},set(value){this.$store.commit('updateMessage',value)}}}
11測試
1測試 Mutation
Mutation 很容易被測試厌殉,因為它們僅僅是一些完全依賴參數(shù)的函數(shù)食绿。這里有一個小技巧,如果你在?store.js?文件中定義了 mutation公罕,并且使用 ES2015 模塊功能默認輸出了 Vuex.Store 的實例器紧,那么你仍然可以給 mutation 取個變量名然后把它輸出去:
conststate={...}// `mutations` 作為命名輸出對象exportconstmutations={...}exportdefaultnewVuex.Store({state,mutations})
2測試 Action
Action 應(yīng)對起來略微棘手,因為它們可能需要調(diào)用外部的 API楼眷。當測試 action 的時候铲汪,我們需要增加一個 mocking 服務(wù)層——例如,我們可以把 API 調(diào)用抽象成服務(wù)罐柳,然后在測試文件中用 mock 服務(wù)回應(yīng) API 調(diào)用掌腰。
3測試 Getter
如果你的 getter 包含很復(fù)雜的計算過程,很有必要測試它們张吉。Getter 的測試與 mutation 一樣直截了當齿梁。
測試一個 getter 的示例:
// getters.jsexportconstgetters={filteredProducts(state,{filterCategory}){returnstate.products.filter(product=>{returnproduct.category===filterCategory})}}
12熱重載
1使用 webpack 的?,Vuex 支持在開發(fā)過程中熱重載 mutation肮蛹、module勺择、action 和 getter。你也可以在 Browserify 中使用?插件蔗崎。
importVuefrom'vue'importVuexfrom'vuex'importmutationsfrom'./mutations'importmoduleAfrom'./modules/a'Vue.use(Vuex)conststate={...}conststore=newVuex.Store({state,mutations,modules:{a:moduleA}})if(module.hot){// 使 action 和 mutation 成為可熱重載模塊module.hot.accept(['./mutations','./modules/a'],()=>{// 獲取更新后的模塊// 因為 babel 6 的模塊編譯格式問題酵幕,這里需要加上 `.default`constnewMutations=require('./mutations').defaultconstnewModuleA=require('./modules/a').default// 加載新模塊store.hotUpdate({mutations:newMutations,modules:{a:newModuleA}})})}