由于多個(gè)狀態(tài)分散的跨越在許多組件和交互間各個(gè)角落震叮,大型應(yīng)用復(fù)雜度也經(jīng)常逐漸增長知举。為了解決這個(gè)問題金顿,Vue 提供 vuex:我們有受到 Elm 啟發(fā)的狀態(tài)管理庫日丹。vuex 甚至集成到 vue-devtools,無需配置即可訪問時(shí)光旅行怎抛。
狀態(tài)管理
狀態(tài)的初始化
狀態(tài)管理仰税,我們應(yīng)該并不陌生。
舉個(gè)例子抽诉,超市里新進(jìn)了一批商品,管理員給這些商品分類吐绵,建立索引迹淌,然后按照順序放入貨架的過程就是最簡單的狀態(tài)管理。
let goods1 = {
category: 'fruit',
name: 'apple',
quantity: 5
}
let goods2 = {
category: 'supplies',
name: 'toothbrush',
quantity: 5
}
let goods3 = {
category: 'clothes',
name: 'sweater',
quantity: 5
}
簡單歸類后 :
let shop = {
goods: {
fruit: [{ name: 'apple', quantity: 5 }],
supplies: [{ name: 'toothbrush', quantity: 5 }],
clothes: [{ name: 'sweater', quantity: 5 }]
}
}
這樣己单,當(dāng)我們需要某一商品時(shí)唉窃,很容易根據(jù)類目檢索到這個(gè)商品 :
console.log(shop.goods.fruit.find(f => f.name === 'apple'))
//-> { name: 'apple', quantity: 5 }
狀態(tài)的改變
當(dāng)有顧客前來購買商品時(shí),我們需要類似的操作來減少被購買商品的數(shù)量 :
shop.goods.fruit.find(f => f.name === 'apple').quantity --
然而在成千上萬的交易量背后纹笼,你不知道這些商品被購買的詳細(xì)情況纹份,你甚至不知道上周賣出了多少蘋果,你也就無從得知下周該進(jìn)多少廷痘。
所以你需要一個(gè)賬目來記錄商品購買明細(xì) :
let account = {
appleSold (value) {
console.log("apple sold " + value)
shop.goods.fruit.find(f => f.name === 'apple').quantity -= value
}
}
當(dāng)賣出蘋果時(shí)蔓涧,POS機(jī)“滴”一聲,記錄生成了 :
account.appleSold (5)
//-> apple sold 5
最簡單的store
于是笋额,我們得到了一個(gè)最簡單的store :
let shop = {
goods: {
fruit: [{ name: 'apple', quantity: 5 }],
supplies: [{ name: 'toothbrush', quantity: 5 }],
clothes: [{ name: 'sweater', quantity: 5 }]
},
account: {
appleSold (value) {
console.log("apple sold " + value)
shop.goods.fruit.find(f => f.name === 'apple').quantity -= value
},
funcN () { }
}
}
由此可知元暴,狀態(tài)管理可以幫助我們更友好的改變狀態(tài),同時(shí)兄猩,跟蹤狀態(tài)變化的軌跡茉盏。
Vue(x) er 須知
開始
Vuex 官方文檔:
https://vuex.vuejs.org/zh-cn/getting-started.html
Vuex最核心的概念 :
- 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)用趟咆。
下面對(duì)此拓展:
對(duì)象引用
下面這兩段代碼將輸出什么? 先不要往下看, 自己寫一下
let store = {
state: {
msg: "welcome"
}
}, copy = store.state;
store.state = {
hello: "world"
};
console.log(Object.keys(copy));
let store = {
state: {
msg: 'welcome'
}
}, copy = store.state;
store.state.hello = "world";
console.log(Object.keys(copy))
結(jié)果如下(如果你都答對(duì)了添瓷,那么理解和上手Vuex將會(huì)很輕松) :
//-> ["msg"]
//-> ["msg", "hello"]
提交和分發(fā)
vuex 只是一個(gè)工具梅屉,或許過了這段時(shí)間,過了這個(gè)項(xiàng)目鳞贷,你就不會(huì)再用它坯汤。
我們要記住的是它留給我們的啟示:
不要直接更改狀態(tài), 而是通過提交(commit)和分發(fā)(dispatch)的方法通知管理者改變對(duì)象狀態(tài),這是大型項(xiàng)目和復(fù)雜狀態(tài)管理的最佳實(shí)踐搀愧。
Vuex 核心概念
一個(gè)完整的 Vuex Store
/**
* index.js
*/
import axios from 'axios'
const store = new Vuex.Store({
state: {
counter: 0
},
getters: {
counter: state => state.counter
},
// 可處理異步請(qǐng)求惰聂, dispatch 觸發(fā)
actions: {
askPermission ({commit}) {
axios.get('/url').then((res) => {
if(res.data.permission)
commit('addCounter')
}).catch((err) => {
console.log('Error: in process "Ask permission".\n Detailed: ' + err)
})
}
},
// 同步, commit 觸發(fā)
mutations: {
addCounter (state) {
state.counter ++
}
}
})
PS: 仔細(xì)研究一下 dispatch & actions, commit & mutations, 是否有一種似曾相識(shí)的感覺?
Look, 看這對(duì) emit & on (事件機(jī)制),同樣的事件類型咱筛,同樣的回調(diào)函數(shù)搓幌。
State
單一狀態(tài)樹
Vuex使用單一狀態(tài)樹,一個(gè)state對(duì)象包含全部應(yīng)用層狀態(tài)迅箩,使得一個(gè)應(yīng)用只有唯一數(shù)據(jù)源(SSOT, Single Source of Truth)
這對(duì)模塊化并不造成影響
state: {
moduleA: {
},
moduleB: {
}
}
Getter
state: {
prop: ''
}
你可以使用store.state.prop直接讀取狀態(tài)的值, 當(dāng)然也可以使用Getter :
getters: {
prop = state => state.prop
}
使用Getter的好處在于溉愁,你可以從state中派生出一些狀態(tài) :
getters: {
prop = state => state.prop,
fixedProp = state => state.prop || '暫無'
}
Mutation
Vuex 中的 mutation 類似于事件,有一個(gè)字符串的 事件類型 (type) 和 一個(gè) 回調(diào)函數(shù) (handler)饲趋,回調(diào)函數(shù)的接受state作為第一個(gè)參數(shù)拐揭,我們?cè)谶@里修改狀態(tài)(state)
state: {
counter: 0
},
mutations: {
addCounter (state) {
state.counter ++
},
addCounter (state, payload) {
state.counter += payload.value
}
}
通過 commit 通知狀態(tài)變化
store.commit('addCounter')
store.commit('addCounter', {value: 1})
Action
類似于mutation,不同在于
- 只能通過 commit mutation 通知狀態(tài)變化
- mutation 只能包含同步操作,而 action 可以包含異步操作(比如, 在這里可以執(zhí)行ajax請(qǐng)求)
actions: {
askPermission ({commit}) {
axios.get('/url').then((res) => {
if(res.data.permission)
commit('addCounter')
}).catch((err) => {
console.log('Error: in process "Ask permission".\n Detailed: ' + err)
})
},
askPermission ({commit}, payload) {
axios.get('/url', { params:payload }).then((res) => {
if(res.data.permission)
commit('addCounter')
}).catch((err) => {
console.log('Error: in process "Ask permission".\n Detailed: ' + err)
})
}
}
通過 dispatch 通知狀態(tài)變化
store.dispatch('askPermission')
store.dispatch('askPermission', { author: "lonelydawn" })
Module
Vuex 允許我們將store分割成模塊奕塑,每個(gè)模塊擁有自己的state, mutation, action, getter, 甚至是嵌套子模塊 :
const store = new Vuex.Store({
modules: {
a: {
state: {},
mutations: {
addCounter(state) {}
},
actions: {},
getters: {}
},
b: {
namespaced: true, // 建立命名空間
state: {},
mutations: {
addCounter(state) {}
},
actions: {}
}
}
})
store.state.a
store.state.b
// 提交 給 模塊 a 的 mutations
store.commit('addCounter')
// 提交 給 模塊 b 的 mutations
store.commit('b/addCounter')
最后
Vuex 的基本用法已經(jīng)介紹完了堂污。
相關(guān)內(nèi)容 :
官方文檔: https://vuex.vuejs.org/zh-cn/
官方實(shí)例: https://github.com/vuejs/vuex/tree/dev/examples
在下列內(nèi)容中, 我將 演示如何使用 vue + vuex 以及其他常用組件從入門到實(shí)戰(zhàn)龄砰。