vuex 基本入门和使用(二)

vuex 版本为^2.3.1,按照我自己的理解来整理vuex。

关于 state

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

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

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

// 在组件里面使用 vuex
// 初始化 vuex 实例
const store = new Vuex.Store({state: {count: 0},mutations: {increment: state => state.count++,decrement: state => state.count--}
})// 创建一个 Counter 组件
const Counter = {template: `<div>{{ count }}</div>`,computed: {count () {// 直接返回 statereturn store.state.count}}
}// 初始化 vue 实例
const app = new Vue({el: '#app',components: { Counter },template: `<div class="app"><button @click="increment">+</button><button @click="decrement">-</button> <counter></counter></div>`,methods: {increment () {store.commit('increment')},decrement () {store.commit('decrement')}}
})
  • 每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
  • 然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入,并且在测试组件时需要模拟状态。

我以为,当项目发展到多个模块,多个组件和子组件混合的时候,在这种场合,单纯的值传递方式会很麻烦,因为组件或者模块之间的变量是独立的,对于一些全局使用的属性类似 token,cookie 之类的东西,或者是一些多个模块之间共享的属性。

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

// 初始化 vuex的 store(可以将这个放到其他文件里面去)
const store = new Vuex.Store({state: {count: 0},mutations: {increment: state => state.count++,decrement: state => state.count--}
})// 在初始化 vue的时候注册 store(store 即是 vuex 的 store)
const app = new Vue({el: '#app',// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件store,components: { Counter }, // 子组件template: `<div class="app"><counter></counter></div>`
})

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

// 这是子组件 Counter
const Counter = {template: `<div>{{ count }}</div>`,computed: {count () {//  通过this.$store能够访问到 store 并且获取到 statereturn this.$store.state.count}}
}

通过这种方式可以在实际应用中,将 vuex 的初始化分离出去其他模块化文件,然后在 vue初始化的引用相关 vuex 的文件即可,然后通过this.$store全局调用 vuex 的 store 来实现数据存储。

我整理了一下完整的例子,这是 jsrun的例子:
http://jsrun.net/qWqKp

高级用法:mapState 辅助函数

当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。(其实就是自动转换了一些语法输出)

import { mapState } from 'vuex'需要先引入才可以使用

mapState使用前后对比:

// 不使用mapState时:
computed: {count () {return this.$store.state.count}}
// 使用mapState时:
computed: mapState({count: state => state.count,
})

如果在大批量的类似这种的计算属性的话,使用 mapState 会更加便捷,而且不只是普通字符串,函数也可以转换,确实是比较方便的。

这里会有一个疑问,mapState到底做了什么事情,怎么将代码转为 vue 能识别的代码?所以需要参考 vuex 源代码中关于mapState的部分:(我找了当前 vuex 版本3.01的源代码:https://github.com/vuejs/vuex/blob/e821f1bf5b818992d7f34c03029ce2ded1f52a75/dist/vuex.esm.js)

这是normalizeMap的代码:

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

这是mapState的代码:

var mapState = normalizeNamespace(function (namespace, states) {var res = {}; // 这是一个对象类型// 将 state 通过normalizeMap格式化变成一个数组,数组元素是一个对象normalizeMap(states).forEach(function (ref) {var key = ref.key;// 将数组元素对象解出来,先保存起来被后面使用var val = ref.val;// 组成一个新数组,以 key 为 key,值是一个函数mappedStateres[key] = function mappedState () {var state = this.$store.state; // 将本身 vuex 的 store 的 state 和 getters 保存var getters = this.$store.getters;// 先不看 namespace 部分// ......// 这个函数里面会判断真正的值 ref.val 是函数还是普通值return typeof val === 'function'? val.call(this, state, getters) // 函数会被直接执行,并且传入 vuex 的state 和 getters: state[val] // 值会直接放到  vuex 的state 里面};});return res
});
  • states参数在这里只有2种,一种是对象{},一种是数组[]。因为normalizeMap只做了这2个判断。
  • states 会先通过normalizeMap进行序列化,转换为一个数组,数组元素是{ key, val: key } 这种结构的。
  • 这里需要注意一下:如果传入的是对象里面只有函数,如下例的countPlusLocalState,那么被 object.keys获取的 key 是函数的名字。
// 例如传入的state 是一个数组,如下:
[{count: state => state.count,   }
]
// 那么被normalizeMap转换后:
// 即转换为{ key, val: key })
[{ key, // key 是对象{count: state => state.count}val: key // val是对象{count: state => state.count}},//.....
]//------------------------// 例如传入的state 是一个对象,如下:
{count: state => state.count,
}
// 那么被normalizeMap转换后:
// 即被Object.keys(map).map(key => ({ key, val: map[key] }))处理后
[{ key, // key 是count,因为被Object.keys提取出来val: map[key] //  val 是state => state.count,这里以 key 为键找对象的值},//.....
]
  • normalizeMap(states)格式化之后会使用 forEach 遍历这个数组:

    • 如果 val 是一个函数,则直接调用这个 val 函数,把当前 store 上的 state 和 getters 作为参数,返回值作为 mappedState 的返回值。
    • 否则直接把 this.$store.state[val] 作为 mappedState 的返回值。
// 被 foreach 遍历,继续用上面的例子的state来举例,因为不是函数,所以被直接返回:
res["count"] = this.$store.state['state => state.count']
// 虽然这里是以数组的写法,但是在 js 里面数组的写法也可以用在对象上。//如果是函数的话,会被执行val.call,并且传入了几个参数(this, this.$store.state, this.$store.getters)
res["countPlusLocalState"] = this.$store.state['state => state.count']
  • 最终能够形成一个新的数组对象结构,并返回。
  • 因为最终生成的数据就是 computed 的数据格式,所以直接将这个对象传给 computed 就可以直接使用了。

这是mapState经过源代码解析前和后的对比:

// states 为对象时候,mapState转换前
computed: mapState({count: state => state.count,// 传字符串参数 'count' 等同于 `state => state.count`countAlias: 'count',// 为了能够使用 `this` 获取局部状态,必须使用常规函数countPlusLocalState (state) {return state.count + this.localCount}
})// states 为对象时候,mapState转换后
computed: { count() { // 直接转换为一般的计算属性的使用方式return this.$store.state.count },countAlias() { // 也是转为一般的计算属性的使用方式,只不过有指定名字的会使用中括号括起来return this.$store.state['count']}, countPlusLocalState() { // 因为是函数,所以会被直接执行,并且传入了当前 store 上的 state 和 getters作为参数//(但这里没使用 getters)return this.$store.state.count + this.localCount }
}

组件仍然保有局部状态

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

关于 getter

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

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

mapGetters 辅助函数

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

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

export function mapGetters(getters) {const res = {}// 先格式化,然后再处理normalizeMap(getters).forEach(({key, val}) => {res[key] = function mappedGetter() {if (!(val in this.$store.getters)) {console.error(`[vuex] unknown getter: ${val}`)}return this.$store.getters[val]}})return res
}

相比 mapState 来说,他简单一点。

唯一的区别就是它的 val 不能是函数,只能是一个字符串,而且会检查 val in this.$store.getters 的值,如果为 false 会输出一条错误日志。

关于三个点...mapstate 处理

其实三个点就是es6的扩展运算符。

可以将一个数组转为用逗号分隔的参数序列,也可以将对象进行展开,如果应用到 mapstate 身上就是:

需要注意:vue的 computed 是对象,里面的属性是对象属性。

computed: {// 一般 computed 对象属性now: function () {return Date.now()}// 使用对象展开运算符将此对象混入到外部对象中...mapGetters(['doneTodosCount','anotherGetter',// ...])// 转换后是这样,跟一般 computed 对象属性差不多doneTodosCount:function(){},anotherGetter:function(){}
}

参考:

  • Vuex 2.0 源码分析(下)_慕课手记
  • 这篇文章描述更为清楚,可以先看这篇:https://segmentfault.com/a/1190000011668825

vuex 基本入门和使用(二)相关推荐

  1. vue2 + vue-router + vuex + iview 入门项目

    简单的vue2 + vue-router + vuex + iview 入门项目,一个移动端平台的简易考试系统.灵感来源于近期工作项目中开发的一套考试平台系统.欢迎大家来issues指出错误共同进步. ...

  2. 这可能是最好的RxJava 2.x 入门教程(二)

    这可能是最好的 RxJava 2.x 入门教程系列专栏 文章链接: 这可能是最好的 RxJava 2.x 入门教程(完结版)[推荐直接看这个] 这可能是最好的RxJava 2.x 入门教程(一) 这可 ...

  3. Python 简单入门指北(二)

    Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...

  4. [转]OllyDBG 入门系列(二)-字串参考

    标 题: [原创]OllyDBG 入门系列(二)-字串参考 作 者: CCDebuger 时 间: 2006-02-14,13:34 链 接: http://bbs.pediy.com/showthr ...

  5. STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置

    摘自:STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置 作者:自信且爱笑' 发布时间: 2021-05-01 12:08:32 网址:https://blog ...

  6. Oracle入门(十二)之SQL的DDL

    一.数据类型 Character 数据类型 Number 数据类型 Date 数据类型 Raw 和 Long Raw 数据类型 LOB 数据类型 注:Oracle数据类型详解 二.表 (1)创建表 c ...

  7. WPF入门教程系列(二) 深入剖析WPF Binding的使用方法

    同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProperty)只能拥有一个binding. 这一点可以通过设置bindi ...

  8. OllyDBG 入门系列(二)-字串参考

    标 题: [原创]OllyDBG 入门系列(二)-字串参考 作 者: CCDebuger 时 间: 2006-02-14,13:34:43 链 接: http://bbs.pediy.com/show ...

  9. 【Python】Python实战从入门到精通之二 -- 教你使用Python中列表操作

    本文是Python实战–从入门到精通系列的第二篇文章: [Python]Python实战从入门到精通之一 – 教你深入理解Python中的变量和数据类型 Python实战从入门到精通之二 – 教你使用 ...

最新文章

  1. Django博客系统(首页分类数据展示)
  2. php bootstraptable分页,php+bootstrap+dataTable+jquery分页列表
  3. Matlab 牛顿方法求解方程
  4. Python ImportError: No module named 'requests'解决方法
  5. Linux 中内核级加强型火墙的管理
  6. 了解java.nio.file.Path – 2
  7. 内存泄漏 和 内存溢出
  8. 一行代码引来的安全漏洞,就让我们丢失了整个服务器的控制权
  9. 计算机密码学奖,上海交通大学计算机科学与工程系(CSE)
  10. Django发送邮件
  11. python plt 一张图两个y轴,图例legend合并
  12. postman访问abp接口因cookies问题导致400错误
  13. flink cdc 2.0.0 sql 开发模板,及踩坑记录
  14. pr视频两边模糊_Premiere视频教程 在使用Premiere中常见3种模糊效果的制作方法
  15. 我看肖老师的《明日世界-云端计算下的程序设计需求》视频时,做的简单笔记
  16. nio中的Files类常用方法
  17. 电路其他基本元器件(蜂鸣器、晶振等)
  18. FLUENT两相流如何选择模型
  19. 7-207 孔融分梨(函数实现)
  20. 热图6:系列终章之“奇形怪状”的热图

热门文章

  1. JStorm与Storm源码分析(二)--任务分配,assignment
  2. 漫威游戏的VR体验 让你尖叫
  3. softAP配网:用Android手机为linux无屏设备输入wifi密码
  4. Deepfake这次不换脸了,直接换了座城市
  5. “金星生命论”乌龙事件新进展:那只是二氧化硫而已
  6. 机器学习也能套模版:在线选择模型和参数,一键生成demo
  7. 英伟达再发边缘AI计算设备:仅信用卡大小,性能比TX2强15倍
  8. type和object
  9. 文件和目录权限chmod、更改所有者和所属组chown、umask、隐藏权限lsattr/chattr
  10. WebServiceHost 在ConsoleApplication中使用时添加命名空间添加不上,报错