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学习笔记

相关文章
相关标签/搜索