vuex 版本为
^2.3.1
,按照我本身的理解来整理vuex。
每一个vuex 应用只有一个 store 实例,因此使用起来不会太复杂,对于定位错误状态和操做会很方便。vue
最基本的使用方式,经过在vue文件里面初始化 vuex 的 store 来进行操做 vuex 的数据:以下例子:git
// 在组件里面使用 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 () { // 直接返回 state return 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。我觉得,当项目发展到多个模块,多个组件和子组件混合的时候,在这种场合,单纯的值传递方式会很麻烦,由于组件或者模块之间的变量是独立的,对于一些全局使用的属性相似 token,cookie 之类的东西,或者是一些多个模块之间共享的属性。
因此 vuex 提供一个新的方式来将 vuex 的 store 存放到根组件下,经过 store 选项,将store从根组件“注入”到每个子组件中(需调用 Vue.use(Vuex)
):es6
// 初始化 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 的实现:github
// 这是子组件 Counter const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { // 经过this.$store可以访问到 store 而且获取到 state return this.$store.state.count } } }
经过这种方式能够在实际应用中,将 vuex 的初始化分离出去其余模块化文件,而后在 vue初始化的引用相关 vuex 的文件便可,而后经过this.$store
全局调用 vuex 的 store 来实现数据存储。vuex
我整理了一下完整的例子,这是 jsrun的例子:
http://jsrun.net/qWqKpsegmentfault
当一个组件须要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,咱们可使用 mapState 辅助函数帮助咱们生成计算属性,让你少按几回键。(其实就是自动转换了一些语法输出)数组
import { mapState } from 'vuex'
须要先引入才可使用
mapState使用先后对比:缓存
// 不使用mapState时: computed: { count () { return this.$store.state.count } } // 使用mapState时: computed: mapState({ count: state => state.count, })
若是在大批量的相似这种的计算属性的话,使用 mapState 会更加便捷,并且不仅是普通字符串,函数也能够转换,确实是比较方便的。cookie
这里会有一个疑问,mapState到底作了什么事情,怎么将代码转为 vue 能识别的代码?因此须要参考 vuex 源代码中关于mapState的部分:(我找了当前 vuex 版本3.01的源代码:https://github.com/vuejs/vuex/blob/e821f1bf5b818992d7f34c03029ce2ded1f52a75/dist/vuex.esm.js)app
这是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,值是一个函数mappedState res[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 });
{}
,一种是数组[]
。由于normalizeMap只作了这2个判断。{ 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 遍历这个数组:
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']
这是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 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。若是有些状态严格属于单个组件,最好仍是做为组件的局部状态。你应该根据你的应用开发须要进行权衡和肯定。
Vuex 容许咱们在 store 中定义“getter”(能够认为是 store 的计算属性),就像计算属性同样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被从新计算。
// 初始化 getter const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { // 这就是 getters doneTodos: 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,因此直接使用了doneTodos return 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 辅助函数仅仅是将 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 会输出一条错误日志。
其实三个点就是es6的扩展运算符。
能够将一个数组转为用逗号分隔的参数序列,也能够将对象进行展开,若是应用到 mapstate 身上就是:
须要注意:vue的 computed 是对象,里面的属性是对象属性。
computed: { // 通常 computed 对象属性 now: function () { return Date.now() } // 使用对象展开运算符将此对象混入到外部对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) // 转换后是这样,跟通常 computed 对象属性差很少 doneTodosCount:function(){}, anotherGetter:function(){} }
参考: