Vuex 是一個爲 Vue 應用程序開發的狀態管理模式,它用集中式存儲來管理應用全部組件的狀態html
簡單來講,它的做用就是把全部組件的共享狀態抽取出來,以一個全局單例的模式進行管理vue
咱們能夠把 Vuex 理解成一個 store,裏面存儲着全部組件共享的 state(數據)和 mutations(操做)java
這裏仍是先附上官方文檔的連接:https://vuex.vuejs.org/zh/,有興趣的朋友能夠去看看vuex
(1)經過 CDN 引用shell
<script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script>
(2)經過 NPM 安裝與使用npm
> npm install vuex
在項目中須要經過 Vue.use()
明確安裝 Vuex緩存
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)
Vuex 中的 state 用於集中存儲數據,當咱們須要訪問 state 時,能夠先將其映射爲計算屬性app
因爲 state 是響應式的,因此當 state 發生變化時,它會從新求取計算屬性,並自動更新相應的 DOM異步
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> // 一、定義 store const store = new Vuex.Store({ state: { // 定義 state count: 0 } }) // 二、定義組件 const Counter = { template: ` <div> <p>{{ count }}</p> </div> `, // 若是須要訪問 state,能夠在計算屬性中經過 this.$store.state 返回數據 computed: { count() { return this.$store.state.count } } } // 三、建立並掛載根實例 const app = new Vue({ store, // 注入 store components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
如今假設咱們須要在一個組件中使用多個 state,若是爲每一個狀態都寫一條語句將其映射爲計算屬性未免太過繁瑣
因此 Vuex 提供 mapState()
輔助函數可以幫助咱們完成這些工做
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { counter1: 0, counter2: 10, counter3: 100 } }) const Counter = { template: ` <div> <p>couter1: {{ counter1 }}</p> <p>couter2: {{ counter2 }}</p> <p>couter3: {{ counter3 }}</p> </div> `, data: function () { return { localCounter: 1 } }, computed: { // mapState() 返回一個對象,使用對象展開運算符將其混入 computed 對象 ...Vuex.mapState({ // 使用箭頭函數,能夠簡化代碼 counter1: state => state.counter1, // 使用字符串 'counter2',等價於 `state => state.counter2` counter2: 'counter2', // 使用常規函數,可以使用 `this` 以獲取局部狀態 counter3 (state) { return state.counter3 + this.localCounter } }) } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
Vuex 中的 getter 用於管理派生出來的狀態
它就至關於計算屬性同樣,會被緩存起來,當依賴發生改變時纔會從新計算
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-todo></my-todo> </div> <script> const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'Say Hello', done: true}, { id: 2, text: 'Say Goodbye', done: false} ] }, getters: { // 定義 getters // 其接受 state 做爲第一個參數 doneTodos: state => { return state.todos.filter(todo => todo.done) }, // 其接受 getters 做爲第二個參數 doneTodosCount: (state, getters) => { return getters.doneTodos.length } } }) const Todo = { template: ` <div> <p>{{ doneTodosCount }} task(s) done</p> <ul><li v-for="item in doneTodos">{{ item.text }}</li></ul> </div> `, computed: { doneTodos () { return this.$store.getters.doneTodos }, doneTodosCount () { return this.$store.getters.doneTodosCount } } } const app = new Vue({ store, components: { 'my-todo': Todo } }).$mount('#app') </script> </body> </html>
和 state 同樣,getters 也有一個名爲 mapGetters()
的輔助函數將其映射爲計算屬性
computed: { ...mapGetters([ 'doneTodos', 'doneTodosCount' ]) }
若是要給 getter 重命名,能夠用對象形式
computed: { ...mapGetters({ doneTodosAlias: 'doneTodos', doneTodosCountAlias: 'doneTodosCount' }) }
上面咱們講了怎麼訪問 state,下面咱們來看看怎麼修改 state,改變狀態的惟一方法是提交 mutation
這裏,請記住一條重要的規則:mutation 必須是同步函數
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { count: 0 }, mutations: { // 定義 mutations // 傳入的第一個參數是 state increment(state) { state.count += 1 }, // 傳入的第二個參數是 payload,能夠提供額外的信息 incrementN(state, payload) { state.count += payload.amount } } }) const Counter = { template: ` <div> <p>{{ count }}</p> <button @click="increment"> 加 1 </button> <button @click="incrementN"> 加 10 </button> </div> `, // 若是須要訪問 state,能夠在計算屬性中經過 store.state 返回數據 computed: { count() { return store.state.count } }, // 若是須要修改 state,能夠經過 store.commit() 提交 mutation methods: { increment() { store.commit('increment') }, incrementN() { // 以對象的形式給 commit() 傳入 payload store.commit('incrementN', { amount: 10 }) } } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
固然,咱們也能夠使用 mapMutations()
輔助函數將 mutation 映射爲 methods
methods: { ...mapMutations([ 'increment', 'incrementN' ]) }
也一樣能夠使用對象形式支持重命名
methods: { ...mapMutations({ add: 'increment', addN: 'incrementN' }) }
還記得上面咱們說過 mutation 只能是同步函數,若須要使用異步操做,則能夠經過分發 action
action 內部能夠包含異步邏輯,它作的工做是提交 mutation,而不是直接改變狀態
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count += 1 }, incrementN(state, payload) { state.count += payload.amount } }, actions: { //定義 actions // 該方法接受一個與 store 實例具備相同屬性和方法的對象做爲參數 // 咱們能夠調用 context.commit() 提交 mutation // 也能夠經過 context.state 和 context.getters 訪問 state 和 getters incrementAsync(context) { setTimeout(() => { context.commit('increment') }, 1000) }, // 和 mutations 同樣,也能夠傳入第二個參數 payload incrementNAsync(context, payload) { setTimeout(() => { context.commit('incrementN', payload) }, 1000) } } }) const Counter = { template: ` <div> <p>{{ count }}</p> <button @click="increment"> 同步加 1 </button> <button @click="incrementN"> 同步加 10 </button> <button @click="incrementAsync"> 異步加 1 </button> <button @click="incrementNAsync"> 異步加 10 </button> </div> `, computed: { count() { return store.state.count } }, methods: { // 經過 store.commit() 提交 mutation increment() { store.commit('increment') }, incrementN() { // 以對象的形式給 commit() 傳入 payload store.commit('incrementN', { amount: 10 }) }, // 經過 store.dispatch() 分發 action incrementAsync() { store.dispatch('incrementAsync') }, incrementNAsync() { // 以對象的形式給 dispatch() 傳入 payload store.dispatch('incrementNAsync', { amount: 10 }) } } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
若是須要處理更復雜的異步邏輯,咱們也能夠使用 Promise 和 async/await
<!DOCTYPE html> <html> <head> <title>Demo</title> <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vuex"></script> </head> <body> <div id="app"> <my-counter></my-counter> </div> <script> const store = new Vuex.Store({ state: { count: 0 }, mutations: { add(state) { state.count += 10 }, multiply(state) { state.count *= 10 } }, actions: { addAsync(context) { setTimeout(() => { context.commit('add') }, 1000) }, multiplyAsync(context) { setTimeout(() => { context.commit('multiply') }, 1000) }, // 只容許使用異步函數 addAsync 和 multiplyAsync,實現先乘後加 // 使用 async/await async multiplyBeforeAdd(context) { await context.dispatch('multiplyAsync') context.dispatch('addAsync') }, // 只容許使用異步函數 addAsync 和 multiplyAsync,實現先加後乘 // 使用 async/await async addBeforeMultiply(context) { await context.dispatch('addAsync') context.dispatch('multiplyAsync') } } }) const Counter = { template: ` <div> <p>{{ count }}</p> <button @click="done"> 乘10,加10,加10,乘10 </button> </div> `, computed: { count() { return store.state.count } }, methods: { // 先完成先乘後加,再完成先加後乘 done() { store.dispatch('multiplyBeforeAdd').then(() => { store.dispatch('addBeforeMultiply') }) } } } const app = new Vue({ store, components: { 'my-counter': Counter } }).$mount('#app') </script> </body> </html>
【 閱讀更多 Vue 系列文章,請看 Vue學習筆記 】