直接下載 / CDN 引用
https://unpkg.com/vuex
在 Vue 以後引入 vuex 會進行自動安裝:
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>html
NPM
npm install vuex --savevue
單一狀態樹
Vuex 使用 單一狀態樹 —— 是的,用一個對象就包含了所有的應用層級狀態。至此它便做爲一個『惟一數據源(SSOT)』而存在。這也意味着,每一個應用將僅僅包含一個 store 實例。單一狀態樹讓咱們可以直接地定位任一特定的狀態片斷,在調試的過程當中也能輕易地取得整個當前應用狀態的快照web
使用
store.jsvuex
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import state from './state'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
actions,
getters,
state,
mutations
})npm
main.jsapi
import store from './store'數組
new Vue({
el: '#app',
store,
...............
})websocket// store 實例會注入到根組件下的全部子組件中,且子組件能經過
this.$store
訪問到app
mapState
輔助函數須要獲取多個狀態時候,將這些狀態都聲明爲計算屬性會有些重複和冗餘異步
mapState
輔助函數幫助咱們生成計算屬性// 引入
import { mapState } from 'vuex'computed: mapState({
// 箭頭函數可以使代碼更簡練
count: state => state.count,// 等同於 `state => state.count`
countAlias: 'count',// 等同於 `name => state.name`
name,// 爲了可以使用 `this` 獲取局部狀態,必須使用常規函數
countPlusLocalState (state) {
return state.count + this.localCount
}
})
//自己又有單獨的屬性的狀況
computed: {
//內部的計算屬性
selfattr() {},
...mapState({
count,
name
})
}
有時候咱們須要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數
return this.$store.state.todos.filter(todo => todo.done).length
多個組件須要用到此屬性,咱們要麼複製這個函數,或者抽取到一個共享函數而後在多處導入它 —— 不管哪一種方式都不是很理想
Vuex 容許咱們在 store 中定義『getters』(能夠認爲是 store 的計算屬性)。Getters 接受 state 做爲其第一個參數
例子:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})Getters 會暴露爲
store.getters
對象store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getters 也能夠接受其餘 getters 做爲第二個參數:
getters: {
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
咱們能夠很容易地在任何組件中使用它:
this.$store.getters.doneTodosCount
mapGetters
輔助函數
mapGetters
輔助函數僅僅是將 store 中的 getters 映射到局部計算屬性:語法參照state
computed: {
...mapGetters([
'doneTodosCount',
'anotherGetter'
])
}
更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation。Vuex 中的 mutations 很是相似於事件:每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數
例子:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 變動狀態
state.count++
}
}
})//不能直接調用一個 mutation handler , 須要調用 store.commit 方法
store.commit('increment')
store.commit
傳入額外的參數mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
對象風格的提交方式
store.commit({ type: 'increment', amount: 10 })
mutations: { increment (state, payload) { state.count += payload.amount } }
Mutations 需遵照 Vue 的響應規則
- 最好提早在你的 store 中初始化好全部所需屬性。
- 當須要在對象上添加新屬性時,你應該 使用 Vue.set(obj, 'newProp', 123), 或者 -
- 以新對象替換老對象。例如,利用 stage-3 的對象展開運算符咱們能夠這樣寫:state.obj = { ...state.obj, newProp: 123 }
使用常量替代 Mutation 事件類型(規範)
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'// store.js
import { SOME_MUTATION } from './mutation-types'mutations: {
// 咱們可使用 ES2015 風格的計算屬性命名功能來使用一個常量做爲函數名
[SOME_MUTATION] (state) {.........
}
}
mutation 必須是同步函數
提交 Mutations
普通提交:
this.$store.commit('xxx')
提交 mutation使用
mapMutations 提交:
import { mapMutations } from 'vuex'
export default {
methods: {
...mapMutations([
'increment' // 映射 this.increment() 爲 this.$store.commit('increment')
]),
...mapMutations({
add: 'increment' // 映射 this.add() 爲 this.$store.commit('increment')
})
}
}
Action 相似於 mutation,不一樣在於:
Action 提交的是 mutation,而不是直接變動狀態。
Action 能夠包含任意異步操做例子:
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}Action 函數接受一個與 store 實例具備相同方法和屬性的 context 對象,所以你能夠調用
context.commit
提交一個 mutation,或者經過context.state
和context.getters
來獲取 state 和 getters。當咱們在以後介紹到 Modules時,你就知道 context 對象爲何不是 store 實例自己了
參數解構 來簡化代碼
actions: {
increment ({ commit }) {
commit('increment')
}
}
分發 Action
Action 經過
store.dispatch
方法觸發:store.dispatch('increment') ; 咱們能夠在 action 內部執行異步操做Actions 支持一樣的載荷方式和對象方式進行分發:
// 以載荷形式分發
store.dispatch('incrementAsync', {
amount: 10
})// 以對象形式分發
store.dispatch({
type: 'incrementAsync',
amount: 10
})
mapActions
輔助函數import { mapActions } from 'vuex'
methods: {
...mapActions([
'increment' // 映射 this.increment() 爲 this.$store.dispatch('increment')
]),
...mapActions({
add: 'increment' // 映射 this.add() 爲 this.$store.dispatch('increment')
})
}
組合 Actions
Action 一般是異步的,那麼如何知道 action 何時結束呢?更重要的是,咱們如何才能組合多個 action,以處理更加複雜的異步流程?
store.dispatch
能夠處理被觸發的action的回調函數返回的Promise,而且store.dispatch仍舊返回Promise例子:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}如今你能夠:
store.dispatch('actionA').then(() => {
........
})在另一個 action 中也能夠:
actions: {
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
async / await 組合
// 假設 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
應用的全部狀態會集中到一個比較大的對象。當應用變得很是複雜時,store 對象就有可能變得至關臃腫
以上問題,Vuex 容許咱們將 store 分割成模塊(module)。每一個模塊擁有本身的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行一樣方式的分割
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
模塊的局部狀態
對於模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象。
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// 這裏的 `state` 對象是模塊的局部狀態
state.count++
}
},getters: {
doubleCount (state) {
return state.count * 2
}
}
}
一樣,對於模塊內部的 action,局部狀態經過 context.state 暴露出來, 根節點狀態則爲 context.rootState:const moduleA = {
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
對於模塊內部的 getter,根節點狀態會做爲第三個參數暴露出來:const moduleA = {
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
命名空間
默認狀況下,模塊內部的 action、mutation 和 getter 是註冊在全局命名空間的——這樣使得多個模塊可以對同一 mutation 或 action 做出響應。若是但願你的模塊更加自包含或提升可重用性,你能夠經過添加
namespaced: true
的方式使其成爲命名空間模塊。當模塊被註冊後,它的全部 getter、action 及 mutation 都會自動根據模塊註冊的路徑調整命名 , 例如:const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
state: { ... }, // 模塊內的狀態已是嵌套的了,使用 `namespaced` 屬性不會對其產生影響
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
modules: { // 嵌套模塊
myPage: { // 繼承父模塊的命名空間
getters: {
profile () { ... } // -> getters['account/profile']
}
},
posts: { // 進一步嵌套命名空間
namespaced: true,
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
在命名空間模塊內訪問全局內容(Global Assets)
若是你但願使用全局 state 和 getter,
rootState
和rootGetter
會做爲第三和第四參數傳入 getter,也會經過context
對象的屬性傳入 action。若須要在全局命名空間內分發 action 或提交 mutation,將
{ root: true }
做爲第三參數傳給dispatch
或commit
便可。modules: {
foo: {
namespaced: true,
getters: {
// 在這個模塊的 getter 中,`getters` 被局部化了
// 你可使用 getter 的第四個參數來調用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// 在這個模塊中, dispatch 和 commit 也被局部化了
// 他們能夠接受 `root` 屬性以訪問根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
帶命名空間的綁定函數
當使用
mapState
,mapGetters
,mapActions
和mapMutations
這些函數來綁定命名空間模塊時,寫起來可能比較繁瑣:computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
})
},
methods: {
...mapActions([
'some/nested/module/foo',
'some/nested/module/bar'
])
}對於這種狀況,你能夠將模塊的空間名稱字符串做爲第一個參數傳遞給上述函數,上面的例子能夠簡化爲:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo',
'bar'
])
}
Vuex 的 store 接受
plugins
選項,這個選項暴露出每次 mutation 的鉤子。Vuex 插件就是一個函數,它接收 store 做爲惟一參數:const myPlugin = store => {
// 當 store 初始化後調用
store.subscribe((mutation, state) => {
// 每次 mutation 以後調用
// mutation 的格式爲 { type, payload }
})
}而後像這樣使用:
const store = new Vuex.Store({
plugins: [myPlugin]
})
下面是個大概例子
export default function createWebSocketPlugin (socket) {
return store => {
socket.on('data', data => {
store.commit('receiveData', data)
})
store.subscribe(mutation => {
if (mutation.type === 'UPDATE_DATA') {
socket.emit('update', mutation.payload)
}
})
}
}
const plugin = createWebSocketPlugin(socket)const store = new Vuex.Store({
state,
mutations,
plugins: [plugin]
})
開啓嚴格模式,僅需在建立 store 的時候傳入
strict: true
:在嚴格模式下,不管什麼時候發生了狀態變動且不是由 mutation 函數引發的,將會拋出錯誤。這能保證全部的狀態變動都能被調試工具跟蹤到
不要在發佈環境下啓用嚴格模式!
構建工具來處理這種狀況
const store = new Vuex.Store({
strict: process.env.NODE_ENV !== 'production'
})
當在嚴格模式中使用 Vuex 時,在屬於 Vuex 的 state 上使用
v-model
會比較棘手:<input v-model="obj.message">
假設這裏的
obj
是在計算屬性中返回的一個屬於 Vuex store 的對象,在用戶輸入時,v-model
會試圖直接修改obj.message
。在嚴格模式中,因爲這個修改不是在 mutation 函數中執行的, 這裏會拋出一個錯誤
方法一:
用『Vuex 的思惟』去解決這個問題的方法是:給
<input>
中綁定 value,而後偵聽input
或者change
事件,在事件回調中調用 action:<input :value="message" @input="updateMessage">
computed: {
...mapState({ message: state => state.obj.message })
},
methods: { updateMessage (e) {
this.$store.commit('updateMessage', e.target.value)
} }
下面是 mutation 函數:
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
方法二:
使用帶有 setter 的雙向綁定計算屬性
<input v-model="message">
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
Vuex.Store 構造器選項
- state
類型:
Object
Vuex store 實例的根 state 對象
- mutations
類型:
{ [type: string]: Function }
函數老是接受
state
做爲第一個參數(若是定義在模塊中,則爲模塊的局部狀態),payload
做爲第二個參數(可選)
- actions
類型:
{ [type: string]: Function }
在 store 上註冊 action。處理函數接受一個
context
對象,包含如下屬性:{
state, // 等同於 store.state, 若在模塊中則爲局部狀態
rootState, // 等同於 store.state, 只存在於模塊中
commit, // 等同於 store.commit
dispatch, // 等同於 store.dispatch
getters // 等同於 store.getters
}
- getters
類型: { [key: string]: Function }
在 store 上註冊 getter,getter 方法接受如下參數:
state, // 若是在模塊中定義則爲模塊的局部狀態
getters, // 等同於 store.getters
rootState // 等同於 store.state
註冊的 getter 暴露爲 store.getters。
- modules
類型: Object
包含了子模塊的對象,會被合併到 store,大概長這樣:
{
key: {
state,
mutations,
actions?,
getters?,
modules?
},
...
}
與根模塊的選項同樣,每一個模塊也包含 state 和 mutations 選項。模塊的狀態使用 key 關聯到 store 的根狀態。模塊的 mutation 和 getter 只會接收 module 的局部狀態做爲第一個參數,而不是根狀態,而且模塊 action 的 context.state 一樣指向局部狀態。
- plugins
類型: Array<Function>
一個數組,包含應用在 store 上的插件方法。這些插件直接接收 store 做爲惟一參數,能夠監聽 mutation(用於外部地數據持久化、記錄或調試)或者提交 mutation (用於內部數據,例如 websocket 或 某些觀察者)
Vuex.Store 實例屬性
- state
類型: Object
根狀態,只讀。
- getters
類型: Object
暴露出註冊的 getter,只讀。
Vuex.Store 實例方法
- commit(type: string, payload?: any) | commit(mutation: Object)
提交 mutation。
- dispatch(type: string, payload?: any) | dispatch(action: Object)
分發 action。返回 action 方法的返回值,若是多個處理函數被觸發,那麼返回一個 Pormise。
- replaceState(state: Object)
替換 store 的根狀態,僅用狀態合併或 time-travel 調試。
- watch(getter: Function, cb: Function, options?: Object)
響應式地監測一個 getter 方法的返回值,當值改變時調用回調函數。getter 接收 store 的狀態做爲惟一參數。接收一個可選的對象參數表示 Vue 的 vm.$watch 方法的參數。
- subscribe(handler: Function)
註冊監聽 store 的 mutation。handler 會在每一個 mutation 完成後調用,接收 mutation 和通過 mutation 後的狀態做爲參數:
store.subscribe((mutation, state) => {
console.log(mutation.type)
console.log(mutation.payload)
})
一般用於插件
- registerModule(path: string | Array<string>, module: Module)
註冊一個動態模塊。 詳細介紹
- unregisterModule(path: string | Array<string>)
卸載一個動態模塊。 詳細介紹