zoukankan      html  css  js  c++  java
  • vuex状态管理之学习笔记

    概述及使用场景

    Vuex 是一个主要应用在中大型单页应用的类似于 Flux 的数据管理架构。它主要帮我们更好地组织代码,以及把应用内的的状态保持在可维护、可理解的状态。
    但如果是简单的应用 ,就没有必要使用vuex来管理状态了, 只会增加应用的复杂性 ,一个简单的应用完全可以使用$emit这种方式就可以来解决https://cn.vuejs.org/v2/guide/components.html#非父子组件的通信。 比如 ,通过父组件传值给子组件,子组件处理完数据,
    如果需要对该属性值进行变更,则再发送给父组件就可以了,不需要使用vuex,
    但是如果是大型项目中的公共属性,多个不关联的组件之间都需要使用某个属性,
    如果这时候再使用传值就特别的不方便,逻辑很混乱,这时候放到vuex仓库就很简单了,
    某组件对vuex的属性更改完了,其他任何组件都可以拿来用

    用一张图来理解

    以上是官方提供的一张状态管理图,从图中可以看出以下几点:
    1、整个过程它是一个单向的数据流
    2、涉及到几个核心概念Actions,mutations和state
    3、组件可以调用actions,通过action来分发mutations ,只有 mutations 可以修改状态,store(仓库) 是响应式的,即状态的变化会在组件内部得到反映

    另外,每一个 Vuex 应用的核心就是 store(仓库),包含着你的应用中大部分的状态 (state),他和单纯的全局对象不同的是:
    1、Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
    2、不能直接改变 store 中的状态,改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation

    vuex核心概念

    state单一状态树

    组件中获取状态,在计算属性中返回某个状态,例如

    // 创建一个 Counter 组件
    const Counter = {
      template: `<div>{{ count }}</div>`,
      computed: {
        count () {
          return store.state.count
        }
      }
    }
    

    当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM
    以上模式组件依赖全局状态单例,需要在每个使用 state 的组件中需要频繁地导入,vuex提供了一种机制将状态从根组件“注入”到每一个子组件中,通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,在子组件中就可以通过this.$store形式访问到

    const app = new Vue({
      el: '#app',
      // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
      store,
      components: { Counter },
      template: `
        <div class="app">
          <counter></counter>
        </div>
      `
    })
    

    当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余,这时候可以使用 mapState辅助函数帮助我们生成计算属性

    1、通过import先把mapState 导入进来
    import { mapState } from 'vuex'
    2、使用mapState
    computed: mapState({
        // 箭头函数可使代码更简练
        count: state => state.count,
    
        // 传字符串参数 'count' 等同于 `state => state.count`
        countAlias: 'count',
    
        // 为了能够使用 `this` 获取局部状态,必须使用常规函数
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
      })
    

    当计算属性的名称与 state 的子节点名称相同时,也可以给 mapState 传一个字符串数组

    computed: mapState([
      // 映射 this.count 为 store.state.count
      'count'
    ])
    

    通过对象展开运算符可以使用...mapState形式简化写法

    computed: {
      localComputed () { /* ... */ },
      // 使用对象展开运算符将此对象混入到外部对象中
      ...mapState({
        // ...
      })
    }
    

    getters,从 store 中的 state 中派生出一些状态

    有时候我们需要从 store 中的 state 中派生出一些状态,例如过滤某个数组,可以在组件中的计算属性中获取到state然后过滤,但如果我们要在多个组件中去使用它,这样做法不太理想,这时我们可以在store中定义一个getters

    const store = new Vuex.Store({
      state: {
        todos: [
          { id: 1, text: '...', done: true }
        ]
      },
      getters: {
        doneTodos: state => {
          return state.todos.filter(todo => todo.done)
        }
      }
    })
    

    组件中使用getters

    computed: {
      doneTodosCount () {
        return this.$store.getters.doneTodos
      }
    }
    

    使用mapGetters 辅助函数将 store 中的 getter 映射到局部计算属性

    import { mapGetters } from 'vuex'
    export default {
      // ...
      computed: {
      // 使用对象展开运算符将 getter 混入 computed 对象中
        ...mapGetters([
          'doneTodosCount',
          'anotherGetter',
          // ...
        ])
      }
    }
    

    mutation提交,更改store状态

    更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler),Vuex 中的 mutation 非常类似于事件

    const store = new Vuex.Store({
      state: {
        count: 1
      },
      mutations: {
        increment (state) {
          // 变更状态
          state.count++
        }
      }
    })
    

    如果要调用increment函数时,要唤醒一个 mutation handler,需要以相应的 type 调用 store.commit 方法

    store.commit('increment')
    也可以向 store.commit 传入额外的参数
    mutations: {
      increment (state, n) {
        state.count += n
      }
    }
    
    store.commit('increment', 10)
    这里的参数也可以是一个对象
    store.commit('increment', {
      amount: 10
    })
    

    使用常量替代 mutation 事件类型,这在多人协作的大型项目中,很有帮助,当然也可以不使用,看情况。

    // mutation-types.js
    export const SOME_MUTATION = 'SOME_MUTATION'
    
    // store.js
    import Vuex from 'vuex'
    import { SOME_MUTATION } from './mutation-types'
    
    const store = new Vuex.Store({
      state: { ... },
      mutations: {
        // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
        [SOME_MUTATION] (state) {
          // mutate state
        }
      }
    })
    

    在组件中使用 this.$store.commit('xxx') 提交 mutation,也可以使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)

    import { mapMutations } from 'vuex'
    
    export default {
      // ...
      methods: {
        ...mapMutations([
          'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    
          // `mapMutations` 也支持载荷:
          'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
        ]),
        ...mapMutations({
          add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
        })
      }
    }
    

    使用Action可以使得在mutation中更方便的使用异步

    在 Vuex 中,mutation 都是同步事务,Action 类似于 mutation,但不同的是Action 提交的是 mutation,而不是直接变更状态,并且可以包含任意异步操作

    1、注册action

    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit('increment')
        }
      }
    })
    

    在我们需要调用 commit 很多次的时候,使用ES2015 的 参数解构 来简化代码

    actions: {
      increment ({ commit }) {
        commit('increment')
      }
    }
    

    2、分发action
    Action 通过 store.dispatch 方法触发:
    store.dispatch('increment')
    看上去有点多此一举,为何不直接分发mutation,这是因为 mutation 必须同步执行,而我们可以在 action 内部执行异步操作

    actions: {
      incrementAsync ({ commit }) {
        setTimeout(() => {
          commit('increment')
        }, 1000)
      }
    }
    

    同样,也可以使用载荷方式和对象方式进行分发

    // 以载荷形式分发
    store.dispatch('incrementAsync', {
      amount: 10
    })
    
    // 以对象形式分发
    store.dispatch({
      type: 'incrementAsync',
      amount: 10
    })
    

    3、在组件中分发action
    在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用

    import { mapActions } from 'vuex'
    
    export default {
      // ...
      methods: {
        ...mapActions([
          'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
    
          // `mapActions` 也支持载荷:
          'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
        ]),
        ...mapActions({
          add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
        })
      }
    }
    

    module,将 store 分割成模块

    由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
    为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter

    const moduleA = {
      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 的状态
    

    vuex调试工具
    https://github.com/vuejs/vue-devtools

    相关文章
    用 Vuex 构建一个笔记应用
    https://segmentfault.com/a/1190000005015164
    从一个改写后的vue小应用认识vuex
    https://www.jianshu.com/p/9dcbe1fec24d

    作者:fozero
    声明:原创文章,转载请注明出处,谢谢!http://www.cnblogs.com/fozero/p/8094265.html
    标签:vuejs,vuex,前端

  • 相关阅读:
    洛谷P2740 草地排水
    BZOJ 4326 运输计划
    BZOJ 1036 树的统计
    BZOJ 1003 物流运输
    BZOJ 1251 序列终结者
    HDU4864 Task(算竞进阶习题)
    洛谷P4281 紧急集合 / 聚会
    CH0802 占卜DIY
    node.js(二)各种模块
    node.js对象数据类型
  • 原文地址:https://www.cnblogs.com/fozero/p/8094265.html
Copyright © 2011-2022 走看看