zoukankan      html  css  js  c++  java
  • Vuex详解笔记2

    关于 state

    每个vuex 应用只有一个 store 实例,所以使用起来不会太复杂,对于定位错误状态和操作会很方便。

    简单用法:在vuex 的计算属性中返回vuex 的状态

    最基本的使用方式,通过在vue文件里面初始化 vuex 的 store 来进行操作 vuex 的数据:如下例子:

     1 // 在组件里面使用 vuex
     2 // 初始化 vuex 实例 
     3 const store = new Vuex.Store({
     4   state: {
     5     count: 0
     6   },
     7   mutations: {
     8     increment: state => state.count++,
     9     decrement: state => state.count--
    10   }
    11 })
    12 
    13 // 创建一个 Counter 组件
    14 const Counter = {
    15   template: `<div>{{ count }}</div>`,
    16   computed: {
    17     count () {
    18         // 直接返回 state
    19       return store.state.count
    20     }
    21   }
    22 }
    23 
    24 // 初始化 vue 实例
    25 const app = new Vue({
    26   el: '#app',
    27   components: { Counter },
    28   template: `
    29     <div class="app">
    30      <button @click="increment">+</button>
    31      <button @click="decrement">-</button> 
    32      <counter></counter>
    33     </div>
    34   `
    35   ,
    36    methods: {
    37     increment () {
    38       store.commit('increment')
    39     },
    40     decrement () {
    41         store.commit('decrement')
    42     }
    43   }
    44 })
    View Code

    每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
    然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入,并且在测试组件时需要模拟状态。
    我以为,当项目发展到多个模块,多个组件和子组件混合的时候,在这种场合,单纯的值传递方式会很麻烦,因为组件或者模块之间的变量是独立的,对于一些全局使用的属性类似 token,cookie 之类的东西,或者是一些多个模块之间共享的属性。

    所以 vuex 提供一个新的方式来将 vuex 的 store 存放到根组件下,通过 store 选项,将store从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):

     1 // 初始化 vuex的 store(可以将这个放到其他文件里面去)
     2 const store = new Vuex.Store({
     3   state: {
     4     count: 0
     5   },
     6   mutations: {
     7     increment: state => state.count++,
     8     decrement: state => state.count--
     9   }
    10 })
    11 
    12 // 在初始化 vue的时候注册 store(store 即是 vuex 的 store)
    13 const app = new Vue({
    14   el: '#app',
    15   // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
    16   store,
    17   components: { Counter }, // 子组件
    18   template: `
    19     <div class="app">
    20       <counter></counter>
    21     </div>
    22   `
    23 })
    View Code

    通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。让我们更新下 Counter 的实现:

     1 // 这是子组件 Counter
     2 const Counter = {
     3   template: `<div>{{ count }}</div>`,
     4   computed: {
     5     count () {
     6         //  通过this.$store能够访问到 store 并且获取到 state
     7       return this.$store.state.count
     8     }
     9   }
    10 }
    View Code

    通过这种方式可以在实际应用中,将 vuex 的初始化分离出去其他模块化文件,然后在 vue初始化的引用相关 vuex 的文件即可,然后通过this.$store全局调用 vuex 的 store 来实现数据存储。
    我整理了一下完整的例子,这是 jsrun的例子:
    http://jsrun.net/qWqKp
    高级用法:mapState 辅助函数
    当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。(其实就是自动转换了一些语法输出)
    import { mapState } from 'vuex'需要先引入才可以使用
    mapState使用前后对比:

     1 // 不使用mapState时:
     2 computed: {
     3     count () {
     4       return this.$store.state.count
     5     }
     6   }
     7 // 使用mapState时:
     8 computed: mapState({
     9        count: state => state.count,
    10 })
    View Code

    如果在大批量的类似这种的计算属性的话,使用 mapState 会更加便捷,而且不只是普通字符串,函数也可以转换,确实是比较方便的。
    这里会有一个疑问,mapState到底做了什么事情,怎么将代码转为 vue 能识别的代码?所以需要参考 vuex 源代码中关于mapState的部分:(我找了当前 vuex 版本3.01的源代码:https://github.com/vuejs/vuex/blob/e821f1bf5b818992d7f34c03029ce2ded1f52a75/dist/vuex.esm.js)
    这是normalizeMap的代码:

    1 function normalizeMap (map) {
    2   // 判断是否数组,并且最终返回也是一个数组
    3   return Array.isArray(map)
    4     // 是数组就直接 map 循环
    5     ? map.map(key => ({ key, val: key }))
    6     // 是对象就将 key拿出来,然后再进行 map 循环
    7     : Object.keys(map).map(key => ({ key, val: map[key] }))
    8 }
    View Code

    这是mapState的代码:

     1 var mapState = normalizeNamespace(function (namespace, states) {
     2   var res = {}; // 这是一个对象类型
     3   // 将 state 通过normalizeMap格式化变成一个数组,数组元素是一个对象
     4   normalizeMap(states).forEach(function (ref) {
     5     var key = ref.key;// 将数组元素对象解出来,先保存起来被后面使用
     6     var val = ref.val;
     7     // 组成一个新数组,以 key 为 key,值是一个函数mappedState
     8     res[key] = function mappedState () {
     9       var state = this.$store.state; // 将本身 vuex 的 store 的 state 和 getters 保存
    10       var getters = this.$store.getters;
    11       // 先不看 namespace 部分
    12       // ......
    13       // 这个函数里面会判断真正的值 ref.val 是函数还是普通值
    14       return typeof val === 'function'
    15         ? val.call(this, state, getters) // 函数会被直接执行,并且传入 vuex 的state 和 getters
    16         : state[val] // 值会直接放到  vuex 的state 里面
    17     };
    18   });
    19   return res
    20 });
    View Code

    states参数在这里只有2种,一种是对象{},一种是数组[]。因为normalizeMap只做了这2个判断。
    states 会先通过normalizeMap进行序列化,转换为一个数组,数组元素是{ key, val: key } 这种结构的。
    这里需要注意一下:如果传入的是对象里面只有函数,如下例的countPlusLocalState,那么被 object.keys获取的 key 是函数的名字。

     1 // 例如传入的state 是一个数组,如下:
     2 [
     3    {
     4      count: state => state.count,   
     5    }
     6 ]
     7 // 那么被normalizeMap转换后:
     8 // 即转换为{ key, val: key })
     9 [
    10     { 
    11      key, // key 是对象{count: state => state.count}
    12      val: key // val是对象{count: state => state.count}
    13     },
    14     //.....
    15 ]
    16 
    17 //------------------------
    18 
    19 // 例如传入的state 是一个对象,如下:
    20 {
    21   count: state => state.count,
    22 }
    23 // 那么被normalizeMap转换后:
    24 // 即被Object.keys(map).map(key => ({ key, val: map[key] }))处理后
    25 [
    26     { 
    27      key, // key 是count,因为被Object.keys提取出来
    28      val: map[key] //  val 是state => state.count,这里以 key 为键找对象的值
    29     },
    30     //.....
    31 ]
    View Code

    normalizeMap(states)格式化之后会使用 forEach 遍历这个数组:
    如果 val 是一个函数,则直接调用这个 val 函数,把当前 store 上的 state 和 getters 作为参数,返回值作为 mappedState 的返回值。
    否则直接把 this.$store.state[val]作为 mappedState 的返回值

    1 // 被 foreach 遍历,继续用上面的例子的state来举例,因为不是函数,所以被直接返回:
    2 res["count"] = this.$store.state['state => state.count']
    3 // 虽然这里是以数组的写法,但是在 js 里面数组的写法也可以用在对象上。
    4 //如果是函数的话,会被执行val.call,并且传入了几个参数(this, this.$store.state, this.$store.getters)
    5 res["countPlusLocalState"] = this.$store.state['state => state.count']
    View Code

    最终能够形成一个新的数组对象结构,并返回。
    因为最终生成的数据就是 computed 的数据格式,所以直接将这个对象传给 computed 就可以直接使用了。
    这是mapState经过源代码解析前和后的对比:

     1 // states 为对象时候,mapState转换前
     2 computed: mapState({
     3     count: state => state.count,
     4     // 传字符串参数 'count' 等同于 `state => state.count`
     5     countAlias: 'count',
     6     // 为了能够使用 `this` 获取局部状态,必须使用常规函数
     7     countPlusLocalState (state) {
     8         return state.count + this.localCount
     9     }
    10 })
    11 
    12 // states 为对象时候,mapState转换后
    13 computed: { 
    14     count() { 
    15        // 直接转换为一般的计算属性的使用方式
    16         return this.$store.state.count 
    17     },
    18     countAlias() { 
    19         // 也是转为一般的计算属性的使用方式,只不过有指定名字的会使用中括号括起来
    20         return this.$store.state['count']
    21     }, 
    22     countPlusLocalState() { 
    23         // 因为是函数,所以会被直接执行,并且传入了当前 store 上的 state 和 getters作为参数
    24         //(但这里没使用 getters)
    25         return this.$store.state.count + this.localCount 
    26     } 
    27 }
    View Code

    组件仍然保有局部状态
    使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
    关于 getter
    Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

     1 // 初始化 getter
     2 const store = new Vuex.Store({
     3   state: {
     4     todos: [
     5       { id: 1, text: '...', done: true },
     6       { id: 2, text: '...', done: false }
     7     ]
     8   },
     9   getters: { // 这就是 getters
    10     doneTodos: state => {
    11       return state.todos.filter(todo => todo.done)
    12     }
    13   }
    14 })
    15 
    16 // 使用getter
    17 store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
    18 // 或者可以this.$store.getters.xxxx 这样使用。
    View Code
     1 // 可以在第二个参数里面传一个 getter 作为参数
     2 getters: {
     3   // ...
     4   doneTodosCount: (state, getters) => {
     5       // 传入了之前设置的doneTodos的 getters,所以直接使用了doneTodos
     6     return getters.doneTodos.length
     7   }
     8 }
     9 
    10 store.getters.doneTodosCount // -> 1
    11 
    12 // 让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
    13 getters: {
    14   // ...
    15   getTodoById: (state) => (id) => { // 返回一个函数
    16     return state.todos.find(todo => todo.id === id)
    17   }
    18 }
    19 
    20 // 对返回的函数传入参数来使用
    21 store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
    View Code

    mapGetters 辅助函数

    mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。

    参考 vuex 源代码,类似的处理也是跟...mapState类似:

     1 export function mapGetters(getters) {
     2     const res = {}
     3     // 先格式化,然后再处理
     4     normalizeMap(getters).forEach(({key, val}) => {
     5         res[key] = function mappedGetter() {
     6             if (!(val in this.$store.getters)) {
     7                 console.error(`[vuex] unknown getter: ${val}`)
     8             }
     9             return this.$store.getters[val]
    10         }
    11     })
    12     return res
    13 }
    View Code

    相比 mapState 来说,他简单一点。
    唯一的区别就是它的 val 不能是函数,只能是一个字符串,而且会检查 val in this.$store.getters的值,如果为 false 会输出一条错误日志。
    关于三个点...mapstate 处理
    其实三个点就是es6的扩展运算符。
    可以将一个数组转为用逗号分隔的参数序列,也可以将对象进行展开,如果应用到 mapstate 身上就是:
    需要注意:vue的 computed 是对象,里面的属性是对象属性。

     1 computed: {
     2   // 一般 computed 对象属性
     3   now: function () {
     4     return Date.now()
     5   }
     6   // 使用对象展开运算符将此对象混入到外部对象中
     7   ...mapGetters([
     8       'doneTodosCount',
     9       'anotherGetter',
    10       // ...
    11     ])
    12 
    13   // 转换后是这样,跟一般 computed 对象属性差不多
    14   doneTodosCount:function(){},
    15   anotherGetter:function(){}
    16 }
    View Code

    作者: whynotbetter 
    链接:https://www.imooc.com/article/22673
    来源:慕课网
    本文原创发布于慕课网 ,转载请注明出处,谢谢合作

  • 相关阅读:
    无限维
    黎曼流形
    why we need virtual key word
    TOJ 4119 Split Equally
    TOJ 4003 Next Permutation
    TOJ 4002 Palindrome Generator
    TOJ 2749 Absent Substrings
    TOJ 2641 Gene
    TOJ 2861 Octal Fractions
    TOJ 4394 Rebuild Road
  • 原文地址:https://www.cnblogs.com/joyco773/p/8701305.html
Copyright © 2011-2022 走看看