Vuex学习笔记

146 阅读5分钟

序言

最近在学习vuex,这里整理了vuex的基本概念、基本用法以及关于vuex的调试相关的知识点,来跟大家交流交流,请大家多多指教~_~

官方链接护体: vuex.vuejs.org/zh/guide/

通过阅读本篇文章,你将收获 ==>

  • vuex的基本概念
  • 实战中的基本用法
  • 使用 vue-devtools 对Vuex 进行调试

这里提供了一个纯净的 Vuex购物车的 demo,对于想要掌握Vuex的小伙伴值得一试!!

基本概念

vuex是什么?[官链] Vuex是一个专门针对Vue应用开发的状态管理库, 结合Vue的数据响应式机制,可以进行高效状态更新。

什么时候使用Vuex?

当你需要多组件维护同一状态 或不同组件的行为都会改变同一状态时,这时如果感觉到组件间的传值开始变得复杂、混乱时,建议使用 Vuex 来将这些状态进行统一管理。

如果并不是很复杂,可以使用简易的Store来管理(创建一个Store对象,与Vue组件中的data绑定,这样状态也会是响应式的)。

核心

Vuex使用集中式存储状态,在 State中定义存储状态的数据对象,当我们需要使用状态时可以通过 store.state.name 来访问,当我们需要改变状态时,可以通过Mutation 进行修改,在Vuex中并不推荐直接修改 store.state中的数据,而是推荐 提交Mutation的方式。

在 开启严格模式后 直接修改 store.state中的数据会报错(但并不影响修改结果)

Store对象

  // Vuex Store 的基本结构
  const store = new Vuex.Store({
      state: { //  状态存储
         age: 20, name: 'Lisa'
      },
      mutations: { // 修改 状态的 Mutation 方法
        setAge(state,payload){
           state.age = payload
        }
      },
      getters: {
         student(state) {
           const {age,name} = state;
           return `${name}:${age}`
         }
      },
      actions:{
         setAgeAsync(store,payload){
           setTimeout(() => {
             store.commit('setAge',payload)
           },0)
         }
      },
      modules:{},
  })

State

存储状态的数据源对象,(经过new Vuex.Store()后)变为响应式的对象,所以在初始化之前必要要将 State 的结构先写好。因为在 new Vuex.Store 的过程中,将 使用 Vue.observeble(state) 将数据进行响应式处理。

如果需要动态的 添加向 State 中添加新的属性,可以使用 Vue.set(state,key,value)

如何访问状态?

属性访问其实就是通过 store.state 进行访问,在Vue组件中,因为 在Vue.use(Vuex) 中通过 调用Vuex.install方法已经在 beforeCreate钩子中将 store对象 挂载到Vue的原型上了。
所以可以直接使用 this.$store.state.age // 获取 age 属性

Mutation

用于修改 Store 中的状态,而且在 Mutation 中不能进行异步操作

修改状态

调用 store.commit(type,payload) 来提交 Mutation ,在 Mutation 函数中 接收 (state,payload) 作为参数,commit 通过 type 来找到 对应的 Mutation,并将 附带的 payload(载荷)传入 Mutation 中

  • store.commit(type,payload)
  • store.commit({type , ....}) 参数对象的情况
   // store 中定义 Mutaion 
   const store = {
     ...
       state: {age: 2, name: 'Lisa'},
       mutations: {
         // 定义 Mutation, commit 的第二个参数 将作为 payload 传入 
         setAge(state,payload){
            state.age = payload;
         },
         // 当commit传入对象时,载荷的也就是对象
         setName(state,payload){
            state.name = payload.name;
         },
       }
     ...
   }
   
   // 组件中提交 Mutation
   created() {
     this.$store.commit('setAge',20); // 
     this.$store.state.age;// age => 20
     // 传入对象的方式,type 必传
     store.commit({type: 'setName', name: 'David'});
   }
 

Getters

类似Vue中的计算属性, getter 中的返回值会被缓存起来,只有当它的依赖值发生变化后才会被重新计算。

注意: 当 getter 返回的是一个函数时,值将不会被缓存,每次被访问时都会重新执行一遍, Getters 在 初始化时 会遍历所有的getter 然后使用 Object.defineProperty 拦截 get方法,在访问时 get 方法将执行 getter 方法并返回结果。

  • getter 的第一个参数是 state ,第二个参数是 getters
  • getter 可以利用闭包机制 返回一个函数
   {  
      ...
      state: {
         count: 10 ,colors: ['blue','red']
      },
      // 定义 getter 
      getters: {
        totalPrice: (state)=> {
             return state.count * 12
        },
        // 接收 getters 作为参数
        length: (state,getters) => {
            return  getters.totalPrice.toString().length;
        },
        getColorIndex(state) {
           return (color) => {
              return state.colors.indexOf(color)
           }
        }
     }
     ...
  }
  
  // 访问 getter 属性
  
  created() {
     // 直接访问 
     this.$store.getters.totalPrice; // 总价 => 120
     // 访问接收 getters 作为参数的
     this.$store.getters.length; // 3
     // 调用 方法
     this.$store.getters.getColorIndex('blue'); // 0
 
  }
  

Actions

Actions 跟 Mutation 有些类似,不同的是:

  • Actions 可以进行异步操作
  • Actions 函数的第一个参数 接收的是 store

可以在 Actions 中进行异步操作后 提交 Mutation,还可以根据 store 获取 State 和 Getters , 使用store.dispath(type,payload) 提交Action

 {     
       ...
       state: {
           age: 12
       },
       mutations: {
           setAge(state,payload) {
                state.age = payload
           }
       },
       actions: {
           setAgeAsync(store,payload) {
                 // 异步操作
                 setTimeout(() => {
                     // 提交 Mutation 
                     store.commit('setAge',payload) 
                 },0)
           }
       }
       ...
   }
   
   ... 使用
   store.dispath('setAgeAsync',20); // store.state.age => 20
   

Modules

Vuex 允许将 一个大的整体拆分成多个细小的模块,每个模块都拥有自己的 state、getters、mutation、actions 和 嵌套子模块。

const moduleA = {
  namespaced: process.env
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

namespaced

不开启的异常情况:

  • mutation 同名会被同时执行
  • getters 同名将会报错

当没有开启命名空间的时候,如果存在相同的 mutation 于子模块与主模块,进行 commit 时,会被同时执行。

因为当Vuex发现有同名的 mutation时会将其存到到一个数组当中

在子模块中开启 namespaced: true 后将消除这个副作用,commit 互不影响

实战中的基本用法

一般来说我们的状态是在Vue组件中使用, 使用 mapStates\mapGetters\mapMutations\mapActions 配合 Vue中的 computed 和 methods 可以极大的简化使用。

安装 & 基本使用

如果直接使用 vue cli ,可以通过选项直接创建

  yarn add vuex 
|--store
  |--index.js
|--main.js
|--package.json
...

store/index.js

   import Vue from 'vue'
   import Vuex from 'vuex'
   
   Vue.use(Vuex); //先进行注册,或者调用Vuex.install 方法
   
   const store = new Vuex.Store({
       state: {
           count: 12
       },
       getters: {
          totalPrice(state) {
              return state.count * 10
          } 
       },
       mutations:{
          setCount(state,count){
              state.count = count;
          }
       },
       actions: {
           setCountAsync(store,count){
               setTimeout(() => {
                   store.commit('setCount',count)
               },0)
           }
       },
       modules: [],// 模块
   }) 
   
   export default store;
   

main.js

  import Store from './store';
  
  ...
  // 在 vue中的配置选项中注入,在beforeCreate钩子触发时,将其挂载到Vue原型上
  new Vue({
      ...
      store
      ...
  }) 

使用映射简化使用

  • 映射时第一个参数传入数组: 数组中的元素为需要展开的元素,如 mapState(['age','name']) 表示要展开 age 和 name 属性
  • 映射时第一个参数传入对象: 要将对象的属性名作为 新创建的方法名,值为 字段所对应的 状态,如: mapState({age2: 'age'}), 表示将 age2 作为新方法的属性名
如何映射模块中的 State | Mutation ??

在映射方法的第一个参数 传入 要映射的 模块名称,要展开的数据放到第二个参数即可,例如:

  mapState('moduleA',{age3: 'age'}),// 从 moduleA 模块取出 age

demo

   import {mapState, mapGetters,mapMutations,mapActions} from 'vuex';
   
   ...
   {
      computed: {
         ...mapState(['age','name']),
         ...mapState({age2: 'age'}), // 对象可以进行重命名
         ...mapState('moduleA',{age3: 'age'}),// 从 moduleA 模块取出 age
         ...mapGetters(['totalPrice']),
         
      },
      methods: {
         ...mapMutations(['setAge']),
         ...mapActions(['setAgeAsync'])
      }
   }
   
   // 经过映射后会变成这样
   
   {
      computed: {
         age: () => this.$store.state.age ,
         name: () => this.$store.state.name,
         
         totalPrice: () => this.$store.getters.totalPrice
         
      },
      methods: {
        setAge(payload){
           this.$store.commit('setAge',payload)
        },
        setAgeAsync(payload){
           this.$store.dispath('setAgeAsync',payload)
        }
       
      }
   }

插件

在插件中 会暴露每次 mutation 的钩子 ,我们可以根据这个特性在 每次 mutation 结束后 执行一些操作。

结合 localStorage

当我们有一些状态需要持久化存储时,就需要使用到 localStorage 将数据存到浏览器中。

  // 每次 提交 mutation 后 都会重新将 cart 数据设置到本地存储中
  const localPlugin = (store) => {
      store.subscribe((mutation,state) => {
          if(mutation.type.startsWith('cart/')){
              localStorage.setItem('cart',JSON.stringify(state.cart);
          }
      })
  }
  // 
  const store = {
      state: {cart: JSON.parse(localStorage.getItem('cart'))},
      plugins: [localPlugin],
  }

内置的 Logger

Vuex 自带的插件,可以对 mutation 进行监控

vuex.vuejs.org/zh/guide/pl…

使用 vue-devtools 对 Vuex 进行调试

vue-devtools是谷歌浏览器的一款插件,需要先进行下载并安装

结合 vue-devtool 工具,提供了很方便的调试模式

image.png