笔记中的Vue与Vuex版本为1.0.21和0.6.2,须要阅读者有使用Vue,Vuex,ES6的经验。javascript
俗话说得好,没有平白无故的爱,也没有平白无故的恨,更不会平白无故的去阅读别人的源代码。
之因此会去阅读Vuex的源代码,是由于在刚开始接触Vuex时,就在官方文档的Actions部分,看到这么一句:html
// the simplest action function increment (store) { store.dispatch('INCREMENT') } // a action with additional arguments // with ES2015 argument destructuring function incrementBy ({ dispatch }, amount) { dispatch('INCREMENT', amount) }
上面的Action还好说,能看懂,可是下面使用ES6写法的Action是什么鬼呀喂(摔!)
虽然知道有解构赋值,可是那个{ dispatch }
又是从哪儿冒出来的呀喂!明明我在调用时,没有传这个参数呀!
以前由于赶项目进度,因此抱着能用就行的态度,也就没管那么多。现在有了空闲时间,必须好好钻研一下呀。
而钻研最好的方式,就是阅读Vuex的源代码。这样就能弄清楚,那个{ dispatch }
到底从哪儿冒出来的。前端
Vuex的源代码量挺少的,加起来也才600行不到,可是其中大量使用了ES6的语法,且部分功能(如Vuex初始化)使用到了Vue。因此读起来仍是有些费劲的。
整个Vuex的源代码,核心内容包括两部分。一部分是Store的构造函数,另外一部分则是Vuex的初始化函数。
而刚才问题的答案,就在第二部分。vue
首先要介绍的,就是Vuex在Vue项目中的初始化。这儿贴一段代码:
首先是Vuex中,我写的Actions源代码:java
// global/Vuex/action.js export const getMe = ({ dispatch }) => { /** * 异步操做,获取用户信息,并存入Vuex的state中 */ res.user.get_me() .then(data => { dispatch('GET_ME', data) }) .catch(err => { console.log(err) }) }
这个则是顶层组件,调用store的地方。因为Vuex的特色,store只须要在最顶层的组件声明一次。vuex
<template> <div id="wrapper"> <router-view></router-view> </div> </template> <script type="text/javascript"> import store from './Vuex/store.js' export default { store } </script>
接下来则是组件中,则是实际调用Vuex的代码。api
// index.vue import { getMe } from './../global/Vuex/action' export default { vuex: { actions: { getMe }, getters: { // 从state中获取信息 user: state => state.user } }, ready() { // 开始获取用户信息 this.getMe() } }
在这儿,能够很明显的看出,我在使用this.getMe()
时,是没有任何参数的。可是在getMe
函数的定义中,是须要解构赋值出{dispatch}
的。
就比如说这个:闭包
function getX({ x }) { console.log(x) } getX({ x: 3, y: 5 }) // 3
你得传入相应的参数,才能进行解构赋值。
同时,我注意到在Vuex的Actions调用,须要在Vue的options的Vuex.actions中先声明,以后才能使用。
那么,必定是Vuex对这个Action动了手脚。(逃)
而动手脚的代码,就存在于Vuex源代码的override.js
中。这个文件,是用于初始化Vuex的。app
在override.js
中,有个vuexInit
的函数。看名字就知道,这是拿来初始化Vuex的。
在代码开头,有这么一句:异步
const options = this.$options const { store, vuex } = options // 感受解构赋值真的很棒,这样写能省不少时间。 // 下面的是老写法 // const store = options.store // const vuex = options.vuex
在这儿,用因而在Vue中调用,因此this指向Vue,而this.$options则是Vue的配置项。
也就是写Vue组件时的:export default {……一些配置}
这里,就把Vue配置项的store和vuex抽离出来了。
接下来,则看到了Vuex源代码的精妙之处:
// store injection if (store) { this.$store = store } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store }
解构赋值并非必定成功的,若是store在options中不存在,那么store就会是undefined。可是咱们须要找store。
因而Vuex提供了向父级(Vue中的功能)寻找store的功能。不难看出,这儿父级的$store若是不存在,那么其实他也会到本身的父级去寻找。直到找到为止。
就想一条锁链同样,一层一层的连到最顶部store。因此在没有找到时,Vuex会给你报个错误。
// 声明了Vuex但没有找到store时的情况 if (vuex) { if (!this.$store) { console.warn( '[vuex] store not injected. make sure to ' + 'provide the store option in your root component.' ) }
接下来,则是对Vuex声明的内容,进行改造。
首先的是获取Vuex对象的内容:
let { state, getters, actions } = vuex
同时,在这儿还看到了对过期API的处理。感受算是意料以外的惊喜。
// handle deprecated state option // 若是使用state而不是getters来获取Store的数据,则会提示你state已通过时的,你须要使用新的api。 // 可是,这儿也作了兼容,确保升级时服务不会挂掉。 if (state && !getters) { console.warn( '[vuex] vuex.state option will been deprecated in 1.0. ' + 'Use vuex.getters instead.' ) getters = state }
接下来,则是对getters和actions的处理:
// getters if (getters) { options.computed = options.computed || {} for (let key in getters) { defineVuexGetter(this, key, getters[key]) } } // actions if (actions) { options.methods = options.methods || {} for (let key in actions) { options.methods[key] = makeBoundAction(this.$store, actions[key], key) } }
能够看出,在这儿对getters和actions都进行了额外处理。
在这儿,咱们讲述actions的额外处理,至于getters,涉及了过多的Vue,而我不是很熟悉。等我多钻研后,再写吧。
对整个Actions的改造,首先是Vuex的检测:
// actions if (actions) { // options.methods是Vue的methods选项 options.methods = options.methods || {} for (let key in actions) { options.methods[key] = makeBoundAction(this.$store, actions[key], key) } }
在这儿,咱们一点一点的剖析。能够看出,全部的actions,都会被makeBoundAction
函数处理,并加入Vue的methods选项中。
那么看来,makeBoundAction
函数就是我要找的答案了。
接下来贴出makeBoundAction
函数的源代码:
/** * Make a bound-to-store version of a raw action function. * * @param {Store} store * @param {Function} action * @param {String} key */ function makeBoundAction(store, action, key) { if (typeof action !== 'function') { console.warn(`[vuex] Action bound to key 'vuex.actions.${key}' is not a function.`) } return function vuexBoundAction(...args) { return action.call(this, store, ...args) } }
事情到这儿,其实已经豁然明朗了。
我在Vuex中传入的actions,实际会被处理为vuexBoundAction
,并加入options.methods中。
在调用这个函数时,实际上的action会使用call,来改变this指向并传入store做为第一个参数。而store是有dispatch这个函数的。
那么,在我传入{dispatch}
时,天然而然就会解构赋值。
这样的话,也造成了闭包,确保action能访问到store。
今天应该算是解决了心中的一个大疑惑,仍是那句话:
没有平白无故的爱,也没有平白无故的恨,更没有平白无故冒出来的代码。
整个源代码读下来一遍,虽然有些部分不太理解,可是对ES6和一些代码的使用的理解又加深了一步。好比这回就巩固了我关于ES6解构赋值的知识。并且还收获了不少别的东西。总而言之,收获颇丰~
最后的,依然是那句话:前端路漫漫,且行且歌。
最后附上本人博客地址和原文连接,但愿能与各位多多交流。
Lxxyx的前端乐园
原文连接:Vuex源码阅读笔记