美文网首页
Vuex使用详解

Vuex使用详解

作者: 刷题刷到手抽筋 | 来源:发表于2022-05-27 07:24 被阅读0次

    一、Vuex概述

    引用官方文档的话解释什么是Vuex

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

    设想每个Vue组件都只需要关心自己的数据和数据处理逻辑,组件之间完全独立,没有共享数据的需求,那么web应用会非常简单。

    但是实际情况是,一个web应用中不同组件常常会有数据共享的需求。例如文档类型的应用,编辑的内容和大纲之间要共享数据,大纲列表组件需要根据编辑的内容生成文档大纲、电商类型的应用,商品展示列表需要根据用户选择的分类展示相应的商品,那么商品列表组件需要知道分类选项组件中用户选择的是哪个类别。

    我们知道Vue父组件可以和子组件之间通过props、事件、ref引用来进行通信,但是这种组件之间的通信方式在规模较大的应用中会有些力不从心。因此我们更倾向于将不同组件通用的数据提取出来统一管理。

    我们知道Vue组件根据数据渲染视图,不同的数据对应不同的视图。我们也可以理解为web应用处于不同的“状态”,每个状态对应数据的一组取值。因此我们将数据抽出来统一管理也可以称为“状态管理”。

    总之,Vuex就是专为Vue.js开发的状态管理工具,用于解决组件之间数据共享的需求。

    状态管理工具都需要包含哪些要素?

    1. 初始状态
    2. 改变状态
    3. 监听状态的改变

    首先我们看下Vuex都有哪些API,然后说明这些API的作用。

    1. state
    2. getters
    3. mutations
    4. actions
    5. module
    6. 辅助函数:mapState、mapGetters、mapMutations、mapActions
    7. createStore

    其中state和getters用来保存状态;mutations和actions用来改变状态;监听状态用的是Vue组件中的computed属性;module是用来组织整个应用的状态管理代码,使状态划分模块,更易于管理;辅助函数用来在监听状态时候简化代码,createStore则用来创建状态管理对象。

    Vuex状态管理就是创建一个对象(store),这个对象上面保存了应用中大部分数据,组件可以通过store获取数据,也可以改变数据的值。上面说的这些能力一个普通的js对象也可以做到。store和普通的对象的区别是:

    1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
    2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用

    Vuex的数据流是组件中触发Action,Action提交Mutations,Mutations修改State。 组件根据 States或Getters来渲染页面。

    参考官网的图示Vuex官网

    二、Vuex基本使用

    前端面试刷题网站灵题库,收集大厂面试真题,相关知识点详细解析。】

    注意这里使用Vuex版本为4


    基本使用

    首先我们来使用Vuex中的createStore方法创建一个store,我们注意到创建store的对象中目前有state和mutations两个属性。state中保存着我们需要使用的数据 count,mutations里面是改变state中数据的方法,方法接受一个state参数,通过改变这个state参数的属性(count)的值就可以修改状态了。

    执行了store.commit('increase');之后,Vuex会执行mutations中的increase方法,让count加一。


    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            count: 0
        },
        mutations: {
            increase(state) {
                state.count++;
            }
        }
    });
    store.commit('increase');
    
    console.log(store.state.count); // 1
    

    mutations还可以接收参数

    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            count: 0
        },
        mutations: {
            increase(state, {count}) {
                state.count = count;
            }
        }
    });
    store.commit('increase', {count: 2});
    
    console.log(store.state.count); // 2
    

    getters

    getters和state的关系类似于Vue组件data属性和computed属性的关系,getters根据state或者其他getters计算出另一个变量的值,当其依赖的数据变化时候,它也会实时更新。

    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            count: 0
        },
        getters: {
            msg(state) {
                return state.count % 2 === 0 ? '偶数': '奇数';
            }
        },
        mutations: {
            increase(state) {
                state.count++;
            }
        }
    });
    store.commit('increase');
    
    console.log(store.state.count, store.getters.msg); // 1 "奇数"
    

    actions


    既然已经有了mutations可以改变state,为什么还需要actions呢?因为mutations不应该用于异步修改状态。实际上mutations是可以异步修改状态的,比如:

    mutations: {
        increase(state) {
            setTimeout(() => {
                state.count++;
            }, 1000);
        }
    }
    

    但是这样做的话,Vuex是无法知道修改state.count的时机的,因为它是在异步回调里面指定的,因此Vuex无法在调试工具中打印出我们实际改变state的操作。

    因此Vuex中有actions API,在actions中可以进行异步操作,在actions中可以提交mutations,也可以触发其他的action。

    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            count: 0
        },
        mutations: {
            increase(state) {
                state.count++;
            }
        },
        actions: {
            increase(context) {
                setTimeout(() => {
                    // contex.dispatch可以用于触发其他action
                    context.commit('increase');
                }, 1000);
            }
        }
    });
    
    store.dispatch('increase');
    
    console.log(store.state.count); // 0
    
    setTimeout(() => {
        console.log(store.state.count); // 1
    }, 1000);
    

    三、Vuex modules

    通常在一个web应用中,会有很多数据,都放在一个store里面会让数据很混乱,因此我们应该根据功能模块将数据划分成一个一个的模块。

    Vuex支持将store划分成模块,并且模块还可以嵌套。

    下面看官方文档上的多个模块的示例:

    在调用createStore时候传入的对象中,提供modules属性,传入两个子模块。

    import {createStore} from 'vuex';
    const moduleA = {
        state: {
            name: 'a'
        }
    };
    
    const moduleB = {
        state: {
            name: 'b'
        }
    };
    
    const store = createStore({
        modules: {
            a: moduleA,
            b: moduleB
        }
    });
    
    console.log(store.state.a.name); // a
    console.log(store.state.b.name); // b
    

    看下包含嵌套子模块时候,访问子模块的getters和state的示例,

    下面的示例中,有两个子模块a和b,其中a中还有嵌套的子模块c,

    (注意默认情况下,所有子模块的getters、mutations和actions都是注册在全局的)

    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            counter: 0
        },
        getters: {
            counter10times(state) {
                return state.counter * 10;
            }
        },
        modules: {
            a: {
                state: {aName: 'A·a'},
                aGetters: {
                    aFirstName(state) {
                        return state.aName.split('·')[0];
                    }
                },
                modules: {
                    c: {
                        state: {cName: 'C·c'},
                        getters: {
                            cFirstName(state) {
                                return state.cName.split('·')[0];
                            }
                        }
                    }
                }
            },
            b: {
                state: {bName: 'B·b'},
                getters: {
                    bFirstName(state) {
                        return state.bName.split('·')[0];
                    },
                    bNewName(state, getters, rootState, rootGetters) {
                        // 访问局部state
                        const {bName} = state;
                        // 访问全局state
                        const {a: {c: {cName}}} = rootState;
                        // 访问局部getters
                        const {bFirstName} = getters;
                        // 访问全局getters
                        const {cFirstName} = rootGetters;
                        return `${bName}  ${bFirstName}  ${cName}  ${cFirstName}`;
                    }
                }
            }
        }
    });
    
    // 子模块的state通过子模块路径访问
    console.log(store.state.a.c.cName);
    
    // 子模块的getters都注册到了全局,在store.getters下面直接能访问到
    console.log(store.getters.bNewName);
    

    下面是一个多模块,commit mutation和dispatch action的示例,

    模块可以提交其他模块的mutation,从而改变其他的模块的状态;模块也可以触发其他模块的action

    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            counter: 0
        },
        mutations: {
            increaseCounter(state) {
                state.counter++;
            }
        },
        modules: {
            a: {
                state: {aName: 'A·a'},
                mutations: {
                    changeAName(state) {
                        state.aName = 'A-a';
                    }
                },
                actions: {
                    callChangeCNameAsync({dispatch}) {
                        // 触发其他模块的action
                        setTimeout(() => {
                            dispatch('changeCNameAsync');
                        }, 500);
                    }
                },
                modules: {
                    c: {
                        state: {cName: 'C·c'},
                        mutations: {
                            changeCName(state, payload) {
                                state.cName = `C-c-${payload.suffix}`;
                            }
                        },
                        actions: {
                            changeCNameAsync({commit, rootState}) {
                                setTimeout(() => {
                                    // 提交其他模块的mutation,mutation是全局的
                                    commit('increaseCounter');
                                    // 提交局部模块的mutation
                                    commit('changeCName', {
                                        suffix: rootState.counter
                                    });
                                }, 500);
                            }
                        }
                    },
                }
            },
            b: {
                state: {bName: 'B·b'},
                mutations: {
                    changeBName(state) {
                        state.bName = 'B-b';
                    }
                }
            }
        }
    });
    
    // 全局的commit
    store.commit('increaseCounter');
    console.log(store.state.counter); // 1
    
    // 子模块mutation注册到全局了
    store.commit('changeCName', {suffix: '123'});
    console.log(store.state.a.c.cName); // C-c-123
    
    // 子模块commit其他模块的mutation
    store.dispatch('changeCNameAsync');
    setTimeout(() => {
        console.log(store.state.a.c.cName); // C-c-2
    }, 1000);
    
    // 子模块dispatch其它模块的action
    store.dispatch('callChangeCNameAsync');
    setTimeout(() => {
        console.log(store.state.a.c.cName); // C-c-3
    }, 1500);
    

    之前提到默认情况下,所有模块的getters、mutations和actions都是注册到全局的,这样如果多个子模块的getters、mutations和actions中有同名时候,会导致覆盖,引起问题。因此通常我们需要给子模块加命名空间。

    给子模块加命名空间的方式是给子模块加namespaced属性并赋值为true。

    加了命名空间后,访问state的方式不变(因为默认state也不是注册到全局的),访问getters时候需要加命名空间前缀,如果访问模块自身子模块的getters、提交mutations、触发actions时候,只需要加相对路径前缀,不需要加自身命名空间前缀,例如模块a访问其子模块c时候,不需要加'a/c'前缀,只需要'c'就可以了。

    看下多模块(包含嵌套模块情况)时候访问state和getters的示例:

    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            counter: 0
        },
        getters: {
            counter10times(state) {
                return state.counter * 10;
            }
        },
        modules: {
            a: {
                namespaced: true,
                state: {aName: 'A·a'},
                getters: {
                    aFirstName(state) {
                        return state.aName.split('·')[0];
                    }
                },
                modules: {
                    c: {
                        namespaced: true,
                        state: {cName: 'C·c'},
                        getters: {
                            cFirstName(state) {
                                return state.cName.split('·')[0];
                            }
                        }
                    }
                }
            },
            b: {
                namespaced: true,
                state: {bName: 'B·b'},
                getters: {
                    bNewName(state, getters, rootState, rootGetters) {
                        // 局部state
                        const bName = state.bName.split('·')[0];
                        // 其他模块的getter
                        const cFirstName = rootGetters['a/c/cFirstName'];
                        // 其他模块的state
                        const aName = rootState.a.aName;
                        return `${bName} ${cFirstName} ${aName}`;
                    }
                }
            }
        }
    });
    
    // getters命名空间
    console.log(store.getters['b/bNewName']); // B C A·a
    
    // 子节点state仍然是通过节点路径访问
    console.log(store.state.a.c.cName); // C·c
    

    看下在使用了命名空间的多模块的提交mutations和触发actions

    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            counter: 0
        },
        mutations: {
            increaseCounter(state) {
                state.counter++;
            }
        },
        modules: {
            a: {
                namespaced: true,
                state: {aName: 'A·a'},
                mutations: {
                    changeAName(state) {
                        state.aName = 'A-a';
                    }
                },
                actions: {
                    callChangeCNameAsync({dispatch}) {
                        // 触发子模块的action,是相对于自身的路径,不需要加a前缀
                        setTimeout(() => {
                            dispatch('c/changeCNameAsync');
                        }, 500);
                    }
                },
                modules: {
                    c: {
                        namespaced: true,
                        state: {cName: 'C·c'},
                        mutations: {
                            changeCName(state, payload) {
                                state.cName = `C-c-${payload.suffix}`;
                            }
                        },
                        actions: {
                            changeCNameAsync({commit, rootState}) {
                                setTimeout(() => {
                                    // 提交其他模块的mutation,mutation是全局的
                                    commit('increaseCounter', null, {root: true});
                                    // 提交局部模块的mutation,不需要加前缀
                                    commit('changeCName', {
                                        suffix: rootState.counter
                                    });
                                }, 500);
                            }
                        }
                    },
                }
            },
            b: {
                namespaced: true,
                state: {bName: 'B·b'},
                mutations: {
                    changeBName(state) {
                        state.bName = 'B-b';
                    }
                }
            }
        }
    });
    
    // 全局的commit
    // 注意加了命名空间之后,提交根模块的mutation和触发根模块的action时候,都需要加上{root: true}的选项
    store.commit('increaseCounter', null, {root: true});
    console.log(store.state.counter); // 1
    
    // 子模块mutation注册到全局了
    store.commit('a/c/changeCName', {suffix: '123'});
    console.log(store.state.a.c.cName); // C-c-123
    
    // 子模块commit其他模块的mutation
    store.dispatch('a/c/changeCNameAsync');
    setTimeout(() => {
        console.log(store.state.a.c.cName); // C-c-2
    }, 1000);
    
    // 子模块dispatch其它模块的action
    store.dispatch('a/callChangeCNameAsync');
    setTimeout(() => {
        console.log(store.state.a.c.cName); // C-c-3
    }, 1500);
    

    四、在Vue组件中使用Vuex

    1. 注入store

    使用Vue3、Vuex4版本,通过如下方式向注入store,

    import { createApp } from 'vue';
    import App from './App.vue';
    import {createStore} from 'vuex';
    
    const store = createStore({
      state: {
        counter: 0
      },
      getters: {
        counter10times(state) {
          return state.counter * 10;
        }
      },
      mutations: {
        increaseCounter(state) {
          state.counter++;
        }
      },
      modules: {
        a: {
          namespaced: true,
          state: {aName: 'A·a'},
          getters: {
            aFirstName(state) {
              return state.aName.split('·')[0];
            }
          },
          mutations: {
            changeAName(state) {
              state.aName = 'A-a';
            }
          },
          actions: {
            callChangeCNameAsync({dispatch}) {
              // 触发子模块的action,相对与自身的路径
              setTimeout(() => {
                dispatch('c/changeCNameAsync');
              }, 500);
            }
          },
          modules: {
            c: {
              namespaced: true,
              state: {cName: 'C·c'},
              getters: {
                cFirstName(state) {
                  return state.cName.split('·')[0];
                }
              },
              mutations: {
                changeCName(state, payload) {
                  state.cName = `C-c-${payload.suffix}`;
                }
              },
              actions: {
                changeCNameAsync({commit, rootState}) {
                  setTimeout(() => {
                    // 提交其他模块的mutation,mutation是全局的
                    commit('increaseCounter', null, {root: true});
                    // 提交局部模块的mutation,不需要加前缀
                    commit('changeCName', {
                      suffix: rootState.counter
                    });
                  }, 500);
                }
              }
            }
          }
        },
        b: {
          namespaced: true,
          state: {bName: 'B·b'},
          getters: {
            bNewName(state, getters, rootState, rootGetters) {
              // 局部state
              const bName = state.bName.split('·')[0];
              // 其他模块的getter
              const cFirstName = rootGetters['a/c/cFirstName'];
              // 其他模块的state
              const aName = rootState.a.aName;
              return `${bName} ${cFirstName} ${aName}`;
            }
          },
          mutations: {
            changeBName(state) {
              state.bName = 'B-b';
            }
          }
        }
      }
    });
    
    createApp(App).use(store).mount('#app');
    

    将刚才的加了命名空间的store注入到Vue组件树中。这样在所有的Vue组件中,都能够通过this.$store方式访问store。

    Vue组件通过computed属性来监听store的数据变化。

    看下面的示例,computed依赖了this.$store里面的一些模块的state和getters,并将计算结果展示在界面上。

    <template>
      <div>
        {{counter}}
        {{bName}}
        {{cFirstName}}
      </div>
    </template>
    
    <script>
      export default {
        computed: {
          counter() {
            return this.$store.state.counter;
          },
          bName() {
            return this.$store.state.b.bName;
          },
          cFirstName() {
            return this.$store.getters['a/c/cFirstName'];
          }
        }
      }
    </script>
    
    <style>
      #app {
        margin-top: 60px;
      }
    </style>
    

    下面看下Vue组件中改变状态的示例,

    可以看到Vue组件中通过methods方法调用this.$store的commit和dispatch方法来提交修改和触发action。

    /**
    * @file main.js
    */
    import { createApp } from 'vue';
    import App from './App.vue';
    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
            counter: 0
        },
        getters: {
            counter10times(state) {
                return state.counter * 10;
            }
        },
        mutations: {
            increaseCounter(state) {
                state.counter++;
            }
        },
        modules: {
            a: {
                namespaced: true,
                state: {aName: 'A·a'},
                getters: {
                    aFirstName(state) {
                        return state.aName.split('·')[0];
                    }
                },
                mutations: {
                    changeAName(state) {
                        state.aName = 'A-a';
                    }
                },
                actions: {
                    callChangeCNameAsync({dispatch}) {
                        // 触发子模块的action,相对于自身的路径
                        setTimeout(() => {
                            dispatch('c/changeCNameAsync');
                        }, 500);
                    }
                },
                modules: {
                    c: {
                        namespaced: true,
                        state: {cName: 'C·c'},
                        getters: {
                            cFirstName(state) {
                                return state.cName.split('·')[0];
                            }
                        },
                        mutations: {
                            changeCName(state, payload) {
                                state.cName = `C-c-${payload.suffix}`;
                            }
                        },
                        actions: {
                            changeCNameAsync({commit, rootState}) {
                                setTimeout(() => {
                                    // 提交其他模块的mutation,mutation是全局的
                                    commit('increaseCounter', null, {root: true});
                                    // 提交局部模块的mutation,不需要加前缀
                                    commit('changeCName', {
                                        suffix: rootState.counter
                                    });
                                }, 500);
                            }
                        }
                    }
                }
            },
            b: {
                namespaced: true,
                state: {bName: 'B·b'},
                getters: {
                    bNewName(state, getters, rootState, rootGetters) {
                        // 局部state
                        const bName = state.bName.split('·')[0];
                        // 其他模块的getter
                        const cFirstName = rootGetters['a/c/cFirstName'];
                        // 其他模块的state
                        const aName = rootState.a.aName;
                        return `${bName} ${cFirstName} ${aName}`;
                    }
                },
                mutations: {
                    changeBName(state) {
                        state.bName = 'B-b';
                    }
                }
            }
        }
    });
    
    createApp(App).use(store).mount('#app');
    
    /**
    * @file App.vue
    */
    <template>
        <div>
            {{counter}}
            {{bName}}
            {{cFirstName}}
            <button @click="modifyBName">修改b的name</button>
            <button @click="modifyCName">修改c的name</button>
        </div>
    </template>
    
    <script>
    export default {
        computed: {
            counter() {
                return this.$store.state.counter;
            },
            bName() {
                return this.$store.state.b.bName;
            },
            cFirstName() {
                return this.$store.getters['a/c/cFirstName'];
            }
        },
        methods: {
            modifyBName() {
                this.$store.commit('b/changeBName');
            },
            modifyCName() {
                this.$store.dispatch('a/callChangeCNameAsync');
            }
        }
    }
    </script>
    
    <style>
    #app {
        margin-top: 60px;
    }
    </style>
    

    2. 辅助函数

    我们知道我们使用Vuex时候,通过computed绑定store的state和getters的数据,通过methods中调用this.$store的commit和dispatch方法来改变状态。

    但是每次都要写this.$store,当需要绑定的数据多的时候会比较繁杂,因此Vuex提供了辅助函数来简化代码。辅助函数包括

    1. mapState
    2. mapGetters
    3. mapMutations
    4. mapActions

    其中mapState和mapGetters将映射到computed属性中,mapMutations和mapActions映射到methods属性中。

    用法见下面示例

    <template>
        <div>
            {{counter}}
            {{bName}}
            {{counter10times}}
            {{cFirstName}}
            <button @click="modifyBName">修改b的name</button>
            <button @click="modifyCName">修改c的name</button>
        </div>
    </template>
    
    <script>
    import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
    
    export default {
        computed: {
            ...mapState({
                            // 将this.$store.state.counter映射为counter
                counter: state => state.counter,
                // 也可以这样实现
                // counter: 'counter',
                bName: state => state.b.bName
            }),
            // 全局的getters
            ...mapGetters(['counter10times']),
            // 也可以这样实现,指定组件中的数据名称
            // ...mapGetters({
            //     counter10times: 'counter10times'
            // }),
            // 子模块的getters
            ...mapGetters({
                cFirstName: 'a/c/cFirstName'
            }),
            // 带有命名空间的子模块也可以这样实现映射,在方法多的时候可以简化代码
            // ...mapGetters('a/c', [
            //     'cFirstName'
            // ])
        },
        methods: {
            // 映射mutations到方法
            ...mapMutations({
                modifyBName: 'b/changeBName'
            }),
            // 也可以这样实现
            // ...mapMutations('b', {
            //     modifyBName: 'changeBName'
            // }),
            // 带有命名空间的子模块映射到组件的方法
            ...mapActions('a', {
                modifyCName: 'callChangeCNameAsync'
            }),
        }
    }
    </script>
    
    <style>
    #app {
        margin-top: 60px;
    }
    </style>
    

    3. 组件之间共享数据

    上面说明了Vuex的使用方法,下面看下Vuex在组件共享数据场景的一个简单示例。

    有两个子组件bChild和cChild,它们直接从store中获取数据并渲染。在根组件App.vue中修改store中的数据,可以看到子组件会相应数据更新,展示最新的数据。

    <template>
        <div>
            {{counter}}
            {{counter10times}}
            <b-child></b-child>
            <c-child></c-child>
            <button @click="modifyBName">修改b的name</button>
            <button @click="modifyCName">修改c的name</button>
        </div>
    </template>
    
    <script>
    import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
    import bChild from './components/bChild';
    import cChild from './components/cChild';
    
    export default {
        computed: {
            ...mapState({
                counter: state => state.counter,
                // 也可以这样实现
                // counter: 'counter',
            }),
            // 全局的getters
            ...mapGetters(['counter10times']),
            // 也可以这样实现,指定组件中的数据名称
            // ...mapGetters({
            //     counter10times: 'counter10times'
            // }),
        },
        components: {
            'b-child': bChild,
            'c-child': cChild
        },
        methods: {
            // 映射mutations到方法
            ...mapMutations({
                modifyBName: 'b/changeBName'
            }),
            // 也可以这样实现
            // ...mapMutations('b', {
            //     modifyBName: 'changeBName'
            // }),
            // 带有命名空间的子模块映射到组件的方法
            ...mapActions('a', {
                modifyCName: 'callChangeCNameAsync'
            }),
        }
    }
    </script>
    
    <style>
    #app {
        margin-top: 60px;
    }
    </style>
    

    五、Vuex原理

    1. 说明

    Vuex通过createStore创建了一个数据中心,然后通过发布-订阅模式来让订阅者监听到数据改变。

    那么Vuex是怎么应用到Vue中的呢?

    先来看一个在Vue中使用Vuex的简单例子:

    // main.js
    import { createApp } from 'vue';
    import App from './App.vue';
    import {createStore} from 'vuex';
    
    const store = createStore({
        state: {
          message: 'hello'
        },
        mutations: {
            change(state) {
                state.message = 'world';
            }
        }
    });
    
    createApp(App).use(store).mount('#app');
    
    export default {
      name: 'App',
      computed: {
          info() {
              return this.$store.state.message;
          }
      },
      mounted() {
          this.$store.commit('change');
      }
    }
    

    可以看到,在Vue中使用Vuex,主要有3个关键步骤:

    1. 使用Vuex创建store,再将store注入Vue中。Vue组件中就可以通过this.$store来访问到store。
    2. Vue使用computed获取$store中的状态。
    3. Vue通过store.commit和store.action来修改状态。

    那么我们需要问两个问题:

    1. 注入的原理是什么?为什么调用use()方法之后,就可以在组件通过$store来访问store了?
    2. 响应式原理是什么?为什么使用computed可以监听到store中的状态改变?

    这两个是Vuex比较核心的两个原理。

    2. 注入原理

    store注入 vue的实例组件的方式,是通过vue的 mixin机制,借助vue组件的生命周期钩子beforeCreate 完成的。

    Vue.mixin({
        beforeCreate() {
            if (this.$options && this.$options.store) {
                // 找到根组件 main 上面挂一个$store
                this.$store = this.$options.store;
    
            }
            else {
                // 非根组件指向其父组件的$store
                this.$store = this.$parent && this.$parent.$store;
            }
        }
    });
    

    3. Vuex响应式原理

    Vuex使用vue中的reactive方法将state设置为响应式,原理和Vue组件的data设置为响应式是一样的。

    // vuex/src/store-util.js
    import {reactive} from 'vue';
    
    store._state = reactive({
        data: state
    });
    

    4. 总结

    Vuex是个状态管理器。

    它Vuex通过createStore创建了一个数据中心,然后通过发布-订阅模式来让订阅者监听到数据改变。

    Vuex的store注入 vue的实例组件的方式,是通过vue的 mixin机制,借助vue组件的生命周期钩子beforeCreate 完成的。这样Vue组件就能通过this.$store获取到store了。

    Vuex使用vue中的reactive方法将state设置为响应式,这样组件就可以通过computed来监听状态的改变了。

    相关文章

      网友评论

          本文标题:Vuex使用详解

          本文链接:https://www.haomeiwen.com/subject/ewppurtx.html