01、什么是Vuex?

1.1、为什么需要状态管理?

在复杂的系统中,我们会把系统按照业务逻辑拆分为多个层次、多个模块,采用组件式的开发方式。而此时不同模块、父子模块之间的通信就成了一个问题。
image.png
为了解决这个问题,就有了状态管理,核心概念就是把大家共享的状态(数据)抽出来,放到一个全局共享的仓库里,按照一定约定统一管理,让状态的变化可预测。这里就有两个关键点:

  • 统一存储:共享的状态统一存储,全局共享。
  • 可预测:共享的状态不可随意修改,需要按照约定的规则修改,才能监测状态变更、通知更新。

    1.2、Vuex简介

    Vuex就是面向Vue的状态管理组件,采用集中式存储管理应用的所有组件的共享状态。Vuex只能在Vue中使用,深度使用了Vue的能力,如用new Vue()来实现state的响应式特性。

  • Vue2.版本 >对应> Vuex3.版本,Vuex3.* 中文文档

  • Vue3.版本 >对应> Vuex4.版本,Vuex4.* 中文文档

简单来说,就是Vuex有一个全局公共的store(类似Vue里的data),作为公共数据仓库,保存了大家共享的状态(数据)。这个数据仓库store实现了数据响应、自动通知更新,这样就很容易实现了各个组件间的数据通信了。
其实,对于简单的应用不一定需要Vuex。
Vuex极速入门 - 图2
Vuex主要特点就是:单向数据流+单一数据源。

  • state:存储数据仓库,类似Vue的data,也是响应式的,变更后会自动通知View。
  • views:组件视图,就是使用state的组件。
  • actions:更新state状态,state不能直接修改,必须通过action进行提交。Vue中分为同步Mutation、和异步Action。

    02、准备开始

    调试以及被集成在了Vue的调试工具Devtools中了。

    直接引用JS

    1. <script src="https://unpkg.com/vuex@3/dist/vuex.js"></script>
    2. <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

    CLI安装使用

    基于VSCode、Node环境,以及vue-cli环境

  • 安装vuex

  • 引入vuex,安装Vue插件,Vue.use(Vuex);

    03、Vuex3入门

    Vuex选项&实例

    | Store构造器选项 | 描述 | | —- | —- | | state | Vuex store 实例的根 state 对象 | | mutations | 注册 mutation,就是修改数据的方法,参数为state。不支持异步,通过 store.commit(name) 调用 | | actions | 注册 action,参数为context,同store实例,但不是她。支持异步,通过 store.dispatch(name) 调用 | | getters | 注册 getter,{ [key: string]: Function },参数为state,定义、使用同计算属性 | | modules | 子模块的对象,分割管理store,{ key(moduleName) : {state, namespaced?, mutations?, actions? … }} | | strict | 是否严格模式,默认false,true=严格模式下,任何 mutation 处理函数以外修改state 都会抛出错误。 |
store实例属性
state 数据源state根对象
getters 所有注册的getter
store 实例方法
commit(name, arg?, options?) 提交 mutation 执行申请
dispatch(name, arg?, options?) 提交 action 执行申请
replaceState(state: Object) 替换 store 的根状态,用于合并状态,如加载持久化的state数据。
watch(fn, callback) 响应式地侦听 fn 的返回值,当值改变时调用回调函数
subscribe(handler, option?) 订阅 store 的 mutation,每一个mutation执行完调用
subscribeAction(handler, option?) 订阅 store 的 action
registerModule(path, module) 注册一个动态模块
unregisterModule(path) 卸载一个动态模块
hasModule(path) 检查模块是否以注册
hotUpdate(newOptions: Object) 热替换新的 action 和 mutation

Vuex核心流程

Vuex核心概念:

  • Store 单一状态树:一个应用程序中只有一个Store实例(单例模式),Store包含了state、actions、mutations、getter、modules。
  • 🟢State 数据源:实现了响应式监听,可用mapState辅助函数包装为计算属性访问。
  • Getter 访问属性:返回对state状态数据进行加工后的结果,类似Vue中的计算属性、过滤器,区别就是这是全局共享的。
  • 🟢Mutation 修改数据:Vuex中用于修改状态数据的主要方式,是唯一修改state数据的合理途径了。通过store.commit() 调用mutation
  • Action 异步操作:类似methods,支持异步操作。通过store.dispatch()调用,修改数据也是要调用mutation的。Action 可用来发起异步ajax请求获取处理 state的数据,这是和mutation最大的不同了。
  • Module 模块:当Store很复杂时,用Module拆分为多个模块管理,每个模块里有自己的state、actions、mutations、getter、modules。

Vuex极速入门 - 图3
基本流程:
❶ 定义数据 state,和data一样,预先定义好数据结构,以及数据更新的mutation方法。
使用数据 state

  • 在根组件注入store实例,组件内所有地方(包括后代组件)都可以 this.$store 通过访问了。
  • 通过计算属性computed包装所需的 state数据。如果state数据需要双向绑定到表单元素,则需要用计算属性实现get、set来代理实现。
  • 通过方法methods包装数据的更新store.mutation
  • View上绑定使用,可以绑定包装后的计算属性、方法,也可以直接绑定注入的$store

❸ 触发更新,根据业务需要更新state数据。

  • store.commit()
  • store.dispatch()

❹ 正式修改 state数据,并触发 View 自动更新。

创建Vuex()-购物车案例

  1. 注册插件:Vue.use(Vuex)
  2. 创建全局共享的Store实例,并配置数据、方法。
  3. 注入store,在根Vue组件上注入store实例,然后所有地方都可以用 this.$store访问了
  4. 愉快的使用了 ```html

购物车({{this.$store.getters.cartTotal}})(直接绑定)


购物车({{cartTotal}})

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/393451/1663674824713-563915ca-2267-4ca6-a058-65a95c026b85.png#clientId=u7d7d1b32-73b4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=198&id=u3b329452&margin=%5Bobject%20Object%5D&name=image.png&originHeight=198&originWidth=216&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7650&status=done&style=stroke&taskId=u4934ce78-6ced-48b1-86db-0202bdf7a4e&title=&width=216)
  2. <a name="V5GVQ"></a>
  3. ## ...mapState语法糖
  4. `mpaState``state`的一种Vuex提供的 “语法糖”,主要作用是简化代码。比如当`state`有多个状态属性,在组件中都要用就得一个一个包装,代码冗余。这时,`mapState`就可以简化这个重复、无聊的代码了。
  5. ```javascript
  6. computed:Vuex.mapState(['card','userId']),
  7. computed: {
  8. cartTotal: function () { return this.$store.getters.cartTotal; },
  9. ...Vuex.mapState({
  10. 'cart': 'cart', //计算属性名称:state状态名称
  11. currentUserId: 'userId',
  12. points: state => state.points * 2,
  13. }),
  14. ...Vuex.mapState(['cart','cart']) //更简洁的写法
  15. },
  • mpaState() 是Vuex提供一个辅助函数,帮助生成计算属性。返回的是一个对象(结构同计算属性computed)。

mapState(namespace?: string, map: Array<string> | Object<string | function>): Object

  • …mapState,三个点...是ES6的展开运算符,把对象展开混入当前环境。

其他还有 mapGetters、mapGetters、mapActions、mapMutations 都是类似作用和用法。

Module模块化

当共享的数据和操作太多时,就需要分模块管理了,如下模块示例。

  1. const moduleA = {
  2. state: { ... },
  3. mutations: { ... },
  4. actions: { ... },
  5. getters: { ... }
  6. }
  7. const moduleB = {
  8. state: { ... },
  9. mutations: { ... },
  10. actions: { ... }
  11. }
  12. const store = new Vuex.Store({
  13. modules: {
  14. a: moduleA,
  15. b: moduleB
  16. }
  17. })
  18. store.state.a // -> moduleA 的状态
  19. store.state.b // -> moduleB 的状态

每个模块 module 都包含完整的store结构。
module定义结构:{ **key**(moduleName) : {state, namespaced?, mutations?, actions?, getters? modules? }}

  • key:就是模块的名称,也是模块的命名空间。
  • value,就是一个和store结构相同的对象,存放模块的store信息。模块里方法的参数state、context都是命名空间内的局部对象。

模块化的项目结构:

  1. ├── index.html
  2. ├── main.js
  3. ├── api
  4. └── ... # 抽取出API请求
  5. ├── components
  6. ├── App.vue
  7. └── ...
  8. └── store
  9. ├── index.js # 我们组装模块并导出 store 的地方
  10. ├── actions.js # 根级别的 action
  11. ├── mutations.js # 根级别的 mutation
  12. └── modules
  13. ├── cart.js # 购物车模块
  14. └── products.js # 产品模块

04、Vuex4区别

几乎所有的 Vuex 4 API 都与 Vuex 3 保持不变,有少量差异。

  • 创建方式不同,Vuex4 使用 createStore({}) 函数创建store对象,之前的方式依然支持。
  • 安装方式,app.use(store),已经注入了store实例。
  • 删除了vue 的全局申明:this.$store,可以直接使用了?
  • 打包产物已经与 Vue 3 配套
  • 新特性:useStore组合式函数

    05、一些问题

    Vuex的持久化?

    如果用户刷新页面,导致页面的各种实例重新初始化,之前的全局状态就会丢失。解决方法就是存起来,当刷新页面的时候读取出来了,关闭页面就不用管了。
  1. 在页面刷新时的 beforeunload事件中保存state到sessionStorage里。
  2. Vue create中加载持久化的state,并清除。
    1. created: function () {
    2. window.addEventListener('beforeunload', () => {
    3. sessionStorage.setItem('vstore', JSON.stringify(this.$store.state));
    4. });
    5. try {
    6. const vstore = sessionStorage.getItem('vstore')
    7. if (vstore)
    8. this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(vstore)));
    9. }
    10. catch (ex) { console.log(ex) }
    11. sessionStorage.removeItem('vstore');
    12. }