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
    来源:慕课网
    本文原创发布于慕课网 ,转载请注明出处,谢谢合作

  • 相关阅读:
    typeid抛出异常的解释
    [原创]公平数的解法
    [原创]我的北大ACM POJ 1012解答
    [原创]我的PKU ACM POJ1029题解
    asp.net 单用户登录经典解决方案
    [转]SQL事务回滚的问题及其解决的方法
    获取json数据
    js中Date对象的用法
    解决刷新后回到顶部的问题
    C#获取客户端及服务器端主机信息
  • 原文地址:https://www.cnblogs.com/joyco773/p/8701305.html
Copyright © 2011-2022 走看看