原示例文件中没有将 getters 写到 store.js 中,而是直接在组件中定义的。为了更清晰,我仿照官方示例也提取出来写在了 store.js 中,这样在组件中调用时比较方便。其次也引入了 action.js,并作为 actions 对象传递给 Vuex.store(),这算是 vuex 的标准写法吧,对于后面在组件中调用比较有利。
其中要注意 DELETE_NOTE (state){} 这个方法,原示例使用了 vue1 提供的 remove 方法,但是 vue2 中去掉了这个方法。仔细想想就会明白,这个函数的作用就是删除 notes 数组中的元素。可以使用原生的 splice 方法。如果 JS 基础扎实的话,这里应该很好理解,没有什么大问题。其次相比原示例,添加一个删除后操作的判断。
我之前一直不太理解 flux 的概念,感觉像是新东西,完全不知道它的目的及作用。换成 Vuex,还是有点稀里糊涂。但是通过修改这个示例,基本算是开窍了。这些东西本身并没有玄机奥妙,想一想,如果我们不用框架,而是自己手写一个 todoMVC 时要怎么做?应该也是这样的思路,定义一个 notes 数组变量以及 activeNote 的变量。然后在创建一些改变状态的方法。我在面试中遇到过一个情况,面试官反复问我为什么需要使用框架,用 jQuery 不是也可以实现吗?这样说确实没错,用比较原始的方法当然可以做,只是代码结构会冗余或者凌乱,缺少小而美的特点。框架以及设计模式对代码做了整合封装,对于一个 CURD 应用比较友好,实现起来更方便更简单。我对于 Vuex 的理解就是,它是一个对象,封装了与状态相关的方法和属性。而所谓的状态就是点击、按键等操作之后的变化。
组件中使用 vuex先看一下 Toolbar.vue 这个组件。修改后的代码如下:
<template> <div> <i @click="addNote"></i> <i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNote.favorite}"></i> <i @click="deleteNote"></i> </div> </template> <script> import { mapGetters, mapActions } from 'vuex' export default { computed: mapGetters([ 'activeNote' ]), methods: { ...mapActions([ 'addNote', 'deleteNote', 'toggleFavorite' ]) } } </script>
通过和原示例代码对比,这里的区别一目了然。我通过在控制台打印 Vue 实例,折腾很长时间才大体明白怎么回事。vuex 1 在组件中使用时会直接将 getters 以及 actions 挂到 vuex 这个属性上,没有提供 mapGetters 及 mapActions 等一些方法。而 vuex2 使用 mapGetters 及 mapActions 等一些方法将 actions 的方法挂到 Vue 实例上。总的来说,都是把 actions 的方法挂到 Vue 实例上。我们从这个层面上谈谈 Vue 实例,Vue 2 的变化就是其属性的变化。比如 Vue1 中在 methods 中添加的方法可以在 vue 实例的 $options 属性中查看,而 vue2 中这些方法可以直接在第一级属性中查找或者在 $options 属性下的原型方法中 __proto__ 寻找。在 vue1 中可以查看 vuex 这个属性,但是 vue2 中移除了。至于其它的不同,大家可以自己对比,通过这种方式,可以深入理解 vue 的设计思想。
下图是 Vue1 实例截图:
ES5 实现扩展运算符假设其它组件都以这种方式改好了,就在我们满心欢喜地运行示例时,又报错了。问题出在扩展运算符 ... 上,webpack-simple 这个模板无法解析 ES6 的 ...。为此,我又折腾了很久,想试着修改 webpack 的配置文件,但改动太大。我妥协了,决定抛弃扩展运算符,手写这个方法。当然如果使用 webpack 的模板就没有问题,这个比较简单,我们最后再说。
手写扩展运算符 ... 之前,我们先看一下 mapActions 这个方法。对于 mapGetters 以及 mapActions 这两个函数,最简单的理解办法就是查看 vuex 的源码,最终返回的是一个对象。也就是根据需要获取 store.js 中 actions 对象的某些方法。然后通过扩展运算符把返回的对象拆开然后挂到 Vue 实例上。举例来说(以下只是扩展运算符的用法之一,别的用法可以参考其它的文章):
var obj = { a: 1, b: 2, } var methods = { ...obj } // console.log(methods) { a: 1, b: 2 }
明白扩展运算符的用法之后就好办了。为了简单一点,我直接使用 babel 官网的在线解析器,查看扩展运算符的 ES5 写法。
_extends = Object.assign || function(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
完整的 Toolbar.vue 组件代码如下:
<template> <div> <i @click="addNote"></i> <i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNote.favorite}"></i> <i @click="deleteNote"></i> <i @click="_test"></i> </div> </template> <script> import { mapGetters, mapActions } from 'vuex' _extends = Object.assign || function(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var actions = mapActions([ 'addNote', 'deleteNote', 'toggleFavorite' ]); var methodsObj = _extends({},actions) export default { computed: mapGetters([ 'activeNote' ]), methods:methodsObj } </script>
其余两个子组件类似,相信大家已经明白了我的思路,具体代码如下:
NotesList.vue