Vue學習筆記(十一) Vuex

一、介紹

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)

三、State

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>

五、Getter

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'
    })
}

五、Mutation

上面咱們講了怎麼訪問 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'
    })
}

六、Action

還記得上面咱們說過 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學習筆記

相關文章
相關標籤/搜索