寫在前面
此系列來源于開源項目:前端 100 問:能搞懂 80%的請把簡歷給我
為了備戰(zhàn) 2021 春招
每天一題嗅钻,督促自己
從多方面多角度總結答案皂冰,豐富知識
聊聊 Redux 和 Vuex 的設計思想
簡書整合地址:前端 100 問
正文回答
在軟件開發(fā)里店展,有些通用的思想,比如隔離變化秃流,約定優(yōu)于配置等赂蕴,隔離變化就是說做好抽象,把一些容易變化的地方找到共性舶胀,隔離出來概说,不要去影響其他的代碼。約定優(yōu)于配置就是很多東西我們不一定要寫一大堆的配置嚣伐,比如我們幾個人約定糖赔,view 文件夾里只能放視圖,不能放過濾器轩端,過濾器必須放到 filter 文件夾里放典,那這就是一種約定,約定好之后船万,我們就不用寫一大堆配置文件了刻撒,我們要找所有的視圖,直接從 view 文件夾里找就行耿导。
根據(jù)這些思想声怔,對于狀態(tài)管理的解決思路就是:把組件之間需要共享的狀態(tài)抽取出來,遵循特定的約定舱呻,統(tǒng)一來管理醋火,讓狀態(tài)的變化可以預測。
Store 模式
最簡單的處理就是把狀態(tài)存到一個外部變量里面箱吕,比如:this.$root.$data
芥驳,當然也可以是一個全局變量。但是這樣有一個問題茬高,就是數(shù)據(jù)改變后兆旬,不會留下變更過的記錄,這樣不利于調(diào)試
所以我們稍微搞得復雜一點怎栽,用一個簡單的 Store 模式
var store = {
state: {
message: "Hello!",
},
setMessageAction(newValue) {
// 發(fā)生改變記錄點日志啥的
this.state.message = newValue;
},
clearMessageAction() {
this.state.message = "";
},
};
store 的 state 來存數(shù)據(jù)丽猬,store 里面有一堆的 action,這些 action 來控制 state 的改變熏瞄,也就是不直接去對 state 做改變脚祟,而是通過 action 來改變,因為都走 action强饮,我們就可以知道到底改變(mutation)是如何被觸發(fā)的由桌,出現(xiàn)錯誤,也可以記錄記錄日志啥的
不過這里沒有限制組件里面不能修改 store 里面的 state,萬一組件瞎胡修改行您,不通過 action铭乾,那我們也沒法跟蹤這些修改是怎么發(fā)生的。所以就需要規(guī)定一下邑雅,組件不允許直接修改屬于 store 實例的 state片橡,組件必須通過 action 來改變 state妈经,也就是說淮野,組件里面應該執(zhí)行 action 來分發(fā) (dispatch) 事件通知 store 去改變。這樣約定的好處是吹泡,我們能夠記錄所有 store 中發(fā)生的 state 改變骤星,同時實現(xiàn)能做到記錄變更 (mutation)、保存狀態(tài)快照爆哑、歷史回滾/時光旅行的先進的調(diào)試工具洞难。
vuex
我的主要技術棧是 vuex,所以這里就寫寫 vuex揭朝,其他的參考Vuex队贱、Flux、Redux潭袱、Redux-saga柱嫌、Dva、MobX
Store
每一個 Vuex 里面有一個全局的 Store屯换,包含著應用中的狀態(tài) State编丘,這個 State 只是需要在組件中共享的數(shù)據(jù),不用放所有的 State彤悔,沒必要嘉抓。這個 State 是單一的,和 Redux 類似晕窑,所以抑片,一個應用僅會包含一個 Store 實例。單一狀態(tài)樹的好處是能夠直接地定位任一特定的狀態(tài)片段杨赤,在調(diào)試的過程中也能輕易地取得整個當前應用狀態(tài)的快照敞斋。
Vuex 通過 store 選項,把 state 注入到了整個應用中望拖,這樣子組件能通過 this.$store
訪問到 state 了渺尘。
const app = new Vue({
el: "#app",
// 把 store 對象提供給 “store” 選項,這可以把 store 的實例注入所有的子組件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`,
});
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count() {
return this.$store.state.count;
},
},
};
State 改變说敏,View 就會跟著改變鸥跟,這個改變利用的是 Vue 的響應式機制。
Mutation
顯而易見,State 不能直接改医咨,需要通過一個約定的方式枫匾,這個方式在 Vuex 里面叫做 mutation,更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation拟淮。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)干茉。
const store = new Vuex.Store({
state: {
count: 1,
},
mutations: {
increment(state) {
// 變更狀態(tài)
state.count++;
},
},
});
注意:mutation 都是同步事務。
總的來說都是讓 View 通過某種方式觸發(fā) Store 的事件或方法很泊,Store 的事件或方法對 State 進行修改或返回一個新的 State角虫,State 改變之后,View 發(fā)生響應式改變
Action
到這里又該處理異步這塊兒了委造。mutation 是必須同步的戳鹅,這個很好理解,和之前的 reducer 類似昏兆,不同步修改的話枫虏,會很難調(diào)試,不知道改變什么時候發(fā)生爬虱,也很難確定先后順序隶债,A、B 兩個 mutation跑筝,調(diào)用順序可能是 A -> B死讹,但是最終改變 State 的結果可能是 B -> A。
對比 Redux 的中間件继蜡,Vuex 加入了 Action 這個東西來處理異步回俐,Vuex 的想法是把同步和異步拆分開,異步操作想咋搞咋搞稀并,但是不要干擾了同步操作仅颇。View 通過 store.dispatch('increment') 來觸發(fā)某個 Action,Action 里面不管執(zhí)行多少異步操作碘举,完事之后都通過 store.commit('increment') 來觸發(fā) mutation忘瓦,一個 Action 里面可以觸發(fā)多個 mutation。所以 Vuex 的 Action 類似于一個靈活好用的中間件引颈。
Vuex 把同步和異步操作通過 mutation 和 Action 來分開處理耕皮,是一種方式。但不代表是唯一的方式蝙场,還有很多方式凌停,比如就不用 Action,而是在應用內(nèi)部調(diào)用異步請求售滤,請求完畢直接 commit mutation罚拟,當然也可以台诗。
Vuex 還引入了 Getter,這個可有可無赐俗,只不過是方便計算屬性的復用拉队。
Vuex 單一狀態(tài)樹并不影響模塊化,把 State 拆了阻逮,最后組合在一起就行粱快。Vuex 引入了 Module 的概念,每個 Module 有自己的 state叔扼、mutation事哭、action、getter币励,其實就是把一個大的 Store 拆開慷蠕。
總的來看珊拼,Vuex 的方式比較清晰食呻,適合 Vue 的思想,在實際開發(fā)中也比較方便澎现。
對比 Redux
Redux: view——>actions——>reducer——>state 變化——>view 變化(同步異步一樣)
Vuex:
- view——>commit——>mutations——>state 變化——>view 變化(同步操作)
- view——>dispatch——>actions——>mutations——>state 變化——>view 變化(異步操作)