Vuex笔记 - 一篇搞定Vuex

Vuex

1.Vuex简介

  • Vuex是专门在Vue中实现集中式数据管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理,也是一种组件间通信的方式,适用于任意组件间通信

  • 使用Vuex的场合:

    • 当多个组件依赖于同一数据
    • 来自不同组件的行为需要变更同一状态
  • Vuex工作原理

  • store:store是一个容器,包含着应用中大部分的状态 (state),store同时管理着State,Mutations,Actions

  • State:提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,相似与data

  • Mutations:更改store中数据的唯一方法是提交 mutation。mutation 类似于事件,每个 mutation 都有一个字符串形式的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是实际进行数据更改的地方,并且它会接受 state 作为第一个参数

  • Actions:Action和Mutation相似,Mutation 不能进行异步操作,若要进行异步操作,就得使用Action

    • 如果不需要与后台进行数据交互,一般会直接略过过Actions

2. 准备工作

  • 搭建vuex的步骤

    1. 安装vuex npm i vuex@[版本号] Vue2使用3,Vue3使用4

    2. 创建一个文件夹(一般命名为store),在内部创建一个js文件(一般命名为index),这就是vuex存放公用数据的地方。index文件内部:

      import Vue from 'vue'
      import Vuex from 'vuex'
       
      Vue.use(Vuex)
       
      export default new Vuex.Store({
          state:{},
          mutation:{}
      })
      
    3. 在main.js中挂载store

      import store from './store/index'
      
      new Vue({
        render: h => h(App),
        store
      }).$mount('#app')
      

3. State

  • State提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,相似与data

  • 由于Vuex 使用单一状态树,即一个对象就包含了全部的应用层级状态,所以每个应用只能包含一个 store 实例。State中的数据都只能通过Mutations来改变,除此之外任何直接修改State中数据的方式都将报错(v-model这种双向绑定的也会)

  • 创建state

    import Vue from 'vue'
    import Vuex from 'vuex'
     
    Vue.use(Vuex)
     
    export default new Vuex.Store({
      state: {
        name:"张三",
        age:12,
        count:0
      },
    })
    
  • 获取state中的数据

    由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态

    const Counter = {
      template: `<div>{{ count }}</div>`,
      computed: {
        count () {
          return this.$store.state.count
        }
      }
    }
    

4. Mutation

  • 更改store中数据的唯一方法是提交 mutation。mutation 类似于事件,每个 mutation 都有一个字符串形式的事件类型 (type)和一个回调函数 (handler),这个回调函数就是实际进行数据更改的地方

  • mutation会接受 state 作为第一个参数,除了State之外额外附加的参数被称为载荷(payload)

  • mutation内的函数必须是同步的,任何异步操作都将导致devtools无法捕捉到数据变化。如果需要异步操作,使用actions

  • 创建Muatation

    import Vue from 'vue'
    import Vuex from 'vuex'
     
    Vue.use(Vuex)
     
    export default new Vuex.Store({
      state: {
        name:"张三",
        age:12,
        count:0
      },
      mutations:{
    	addcount(state,num){
    		state.count = state.count + num
      	}
      }
    })
    
  • 调用Muatation

    methods:{
    	btn(){
            //这里的10就是载荷
    		this.$store.commit("addcount",10)
    	}
    }
    

5. Action

  • Action 类似于 mutation,不同点在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作

  • Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation

  • 创建Action

    const store = createStore({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit('increment')
        }
      }
    })
    
  • 调用Action

    this.$store.dispatch('increment')
    

6. Getter

  • 有时候需要从 store 中的 state 中派生出一些数据(过滤、数学计算等),此时就会用到Getter。Getter可以理解为store中的计算属性

  • 创建Getter

    const store = createStore({
      state: {
        todos: [
          { id: 1, text: '...', done: true },
          { id: 2, text: '...', done: false }
        ]
      },
      getters: {
        doneTodosCount (state) {
          return state.todos.filter(todo => todo.done)
        }
      }
    })
    
  • 调用Getter

    this.$store.getters.doneTodosCount
    

7. 辅助函数

7.1 mapState & mapGetter

  • 当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余

    <!--这种写法很冗余-->
    <h1>当前求和为:{{ this.$store.state.sum }}</h1>
    <h2>当前求和放大10倍为
    <h3>我在{{ this.$store.state.school }} 学习{{ this.$store.state.subject }}</h3>
    
    <!--改成这样也还是很冗余-->
    <h1>当前求和为:{{ sum }}</h1>
    <h3>我在{{ school }} 学习{{ subject }}</h3>
    <!--......-->
    computed:{
    	sum(){
    		return this.$store.state.sum
    	},
    	school(){
    		return this.$store.state.school
    	},
    	subject(){
    		return this.$store.state.subject
    	}
    }
    
  • 借助mapState生成计算属性,从state中读取数据

    //mapState中的每一个项都会生成为一个函数,从而同时获取state中的多个数值
    //mapState返回的是一个对象,所以这里使用对象展开符...将对象混入到外部函数中
    
    ...mapState({sum:'sum',school:'school',subject:'subject'})
    ...mapState({
        count: state => state.count,
        countAlias: 'count',	//和上一行等价
        countPlusLocalState (state) {
          return state.count + this.localCount
        })
        
        
    //特别的,当函数名与调用的 state 的属性名相同时,可以给 mapState 传一个字符串数组
    sum(){return this.$store.state.sum},
    school(){return this.$store.state.school}
    //↓
    mapState({sum:'sum',school:'school'})
    //↓
    mapState(['sum','school'])
    
  • mapGetter同理

    doneCount(){return this.$store.getters.doneTodosCount}
    
    ...mapGetters({
      doneCount: 'doneTodosCount'
    })
    

7.2 mapMutations & mapActions

  • mapMutations和mapState类似,但是如果要提交载荷,那么就需要在调用时额外声明

    <template>
      <div id="app">
        <h1>当前求和为:{{ this.$store.state.sum }}</h1>
        <select name="addNumber" v-model="addNum">
          <option :value="1">1</option>
          <option :value="2">2</option>
          <option :value="3">3</option>
        </select>
    
        <button @click="Add(n)">+</button>
        <button @click="Reduce(n)">-</button>
        <button @click="Odd">当前求和为奇数再加</button>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
    	...mapMutations({
            //将this.Add(this.n)映射为this.$store.commit('Add',this.n)
    		Add:'Add' 
        }),
          //将this.Reduce(this.n)映射为this.$store.commit('Reduce',this.n)
          //将this.Odd()映射为this.$store.commit('Odd')
        ...mapMutations(['Reduce','Odd']),
      }
    }
        
    //其中,Odd无法正常工作,因为它没有提交载荷,也就无法获取n。最后获取的数值也将是错误的
    </script>
    
  • mapActions同理

    ...mapActions({add: 'increment'}),
    ...mapActions(['increment','incrementBy']),
    

8. 模块化

  • Vuex 允许我们将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter。模块化的目的是让代码更好维护,让多种数据分类更加明确

  • 创建具有多个模块的store

    const moduleA = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    const moduleB = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... }
    }
    
    const store = createStore({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    
  • 调用指定store内的数据

    this.$store.a
    this.$store.b
    
  • 单个模块中的Mutation,Action,Getter访问的都将是当前模块中的局部State数据

8.1 命名空间

  • 默认情况下,模块内部的 action, mutation, getter 仍然是注册在全局命名空间的。这会导致在不同的、无命名空间的模块中定义两个相同的 action/mutation/getter 时出现错误

  • 可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名

  • 注意:在开启模块化和命名空间之后,state中数据的调用方式将会发生变化

    const moduleB = {
      namespaced: true,
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... }
    }
    
  • 开启命名空间后,组件中读取state数据

    //方式一:直接读取
    this.$store.state.personAbout.list
    //方式二:借助辅助函数读取,第一个参数为要读取的模块,第二个参数为要获取的值
    ...mapState('countAbout',['sum','school','subject'])
    
  • 开启命名空间后,组件中读取getters数据

    //方式一
    this.$store.getters['personAbout/firstPersonName']
    //方式二
    ...mapState('countAbout',['bigSum'])
    
  • 开启命名空间后,组件中调用dispatch

    //方式一
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  • 开启命名空间后,组件中调用commit

    //方式一
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二
    ...mapState('countAbout',{incrementOdd:'JIA',decrement:'JIAN'})
    
posted @ 2022-11-08 11:20  Solitary-Rhyme  阅读(37)  评论(0编辑  收藏  举报