Vue学习笔记(十一) Vuex

简介: Vue学习笔记(十一) Vuex


1、介绍


Vuex 是一个为 Vue 应用程序开发的状态管理模式,它用集中式存储来管理应用所有组件的状态

简单来说,它的作用就是把所有组件的共享状态抽取出来,以一个全局单例的模式进行管理

我们可以把 Vuex 理解成一个 store,里面存储着所有组件共享的 state(数据)和 mutations(操作)

这里还是先附上官方文档的链接:https://vuex.vuejs.org/zh/,有兴趣的朋友可以去看看


2、安装


(1)通过 CDN 引用

<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>


(2)通过 NPM 安装与使用

  • 安装
> npm install vuex


  • 使用

在项目中需要通过 Vue.use() 明确安装 Vuex

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)


3、State


Vuex 中的 state 用于集中存储数据,当我们需要访问 state 时,可以先将其映射为计算属性

由于 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>
        // 1、定义 store
        const store = new Vuex.Store({
            state: { // 定义 state
                count: 0
            }
        })
        // 2、定义组件
        const Counter = {
            template: `
                <div>
                    <p>{{ count }}</p>
                </div>
            `,
            // 如果需要访问 state,可以在计算属性中通过 this.$store.state 返回数据
            computed: {
                count() {
                    return this.$store.state.count
                }
            }
        }
        // 3、创建并挂载根实例
        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>


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


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


6、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>



文章知识点与官方知识档案匹配,可进一步学习相关知识

目录
相关文章
|
1天前
|
JavaScript API 开发者
Vue中双向数据绑定是如何实现的?底层原理简介
Vue中双向数据绑定是如何实现的?底层原理简介
9 4
|
1天前
|
缓存 JavaScript 开发者
什么是vue的计算属性
什么是vue的计算属性
7 0
|
1天前
|
JavaScript
vue组件中data为什么必须是一个函数?
vue组件中data为什么必须是一个函数?
7 1
|
1天前
|
缓存 JavaScript
什么是Vue的计算属性
什么是Vue的计算属性
3 0
|
1天前
|
JavaScript 前端开发 开发者
Vue常见的指令
Vue常见的指令
2 0
|
1天前
|
JavaScript
vue中的data为什么是一个函数
vue中的data为什么是一个函数
|
3天前
|
JavaScript
Vue模板语法、属性绑定、条件渲染的学习
Vue模板语法强调插值表达式需返回结果以显示。避免问题的方法是将逻辑处理放在JS中,不在模板内实现。Vue属性绑定使用`v-bind`(可简写为:)动态绑定类或ID,当值为null或undefined时自动移除。支持布尔类型和动态绑定多值。条件渲染包括`v-if`、`v-else`、`v-else-if`和`v-show`,其中`v-if`用于真值时渲染,`v-show`按条件显示,两者的区别在于频繁切换场景和渲染方式。
|
6天前
|
JavaScript 前端开发 安全
Vue响应式设计
【5月更文挑战第30天】Vue响应式设计
27 1
|
3天前
|
JavaScript API
vue组合式和选项式
vue组合式和选项式
4 2
|
6天前
|
JavaScript 程序员 网络架构
vue路由从入门到进阶 --- 声明式导航详细教程
vue路由从入门到进阶 --- 声明式导航详细教程
vue路由从入门到进阶 --- 声明式导航详细教程