VueX源码分析(2)

剩余内容

  • /module
  • /plugins
  • helpers.js
  • store.js

helpers要从底部开始分析比较好。也即先从辅助函数开始再分析那4个map函数mapState

helpers.js

getModuleByNamespace

  1. /**
  2. * Search a special module from store by namespace. if module not exist, print error message.
  3. * @param {Object} store
  4. * @param {String} helper
  5. * @param {String} namespace
  6. * @return {Object}
  7. */
  8. function getModuleByNamespace (store, helper, namespace) {
  9. const module = store._modulesNamespaceMap[namespace]
  10. if (process.env.NODE_ENV !== 'production' && !module) {
  11. console.error(`[vuex] module namespace not found in ${helper}(): ${namespace}`)
  12. }
  13. return module
  14. }

解析:

  • 通过namespace来寻找module,如果找不到打印错误信息(开发环境)
  • _modulesNamespaceMap这个Map存有所有的module
  • 在vuex中,不同的作用域用'/'来分隔开的(嵌套模块),如商城中的购物车的namespace可以这样表示'shop/shopping_cart'

normalizeMap

  1. /**
  2. * Normalize the map
  3. * normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]
  4. * normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]
  5. * @param {Array|Object} map
  6. * @return {Object}
  7. */
  8. function normalizeMap (map) {
  9. return Array.isArray(map)
  10. ? map.map(key => ({ key, val: key }))
  11. : Object.keys(map).map(key => ({ key, val: map[key] }))
  12. }

解析:

  • 将数组或者对象转化成[Map, Map]的格式,Map关键字有{ key, val }
  • 如果是数组,生成Map的key === val
  • 如果是对象,生成Map的key就是对象的键名,val就是对象的值

normalizeNamespace

  1. /**
  2. * Return a function expect two param contains namespace and map. it will normalize the namespace and then the param's function will handle the new namespace and the map.
  3. * @param {Function} fn
  4. * @return {Function}
  5. */
  6. function normalizeNamespace (fn) {
  7. return (namespace, map) => {
  8. if (typeof namespace !== 'string') {
  9. map = namespace
  10. namespace = ''
  11. } else if (namespace.charAt(namespace.length - 1) !== '/') {
  12. namespace += '/'
  13. }
  14. return fn(namespace, map)
  15. }
  16. }

解析:

  • 这里的fn就是mapState等4大map函数,使用柯里化缓存fn
  • typeof namespace !== 'string'第一个判断是支持两种传参模式:1、可以不传namespace直接传map,如mapActions(['action']);2、支持传namespace,如mapActions('shop', ['action'])
  • 也即namespace可传可不传,不传最后初始化namespace = ''
  • 如果传了namespace,要检查最后一个字符带不带'/',没有则补全
  • 这个函数就是在执行mapState、mapAction等4大map函数之前的namespace预处理,最终才把namesapce和map传个fn函数

createNamespacedHelpers

  1. /**
  2. * Rebinding namespace param for mapXXX function in special scoped, and return them by simple object
  3. * @param {String} namespace
  4. * @return {Object}
  5. */
  6. export const createNamespacedHelpers = (namespace) => ({
  7. mapState: mapState.bind(null, namespace),
  8. mapGetters: mapGetters.bind(null, namespace),
  9. mapMutations: mapMutations.bind(null, namespace),
  10. mapActions: mapActions.bind(null, namespace)
  11. })

解析:

  • 这个bind函数涉及到柯里化,要理解柯里化才可理解这个意思
  • 柯里化和函数的参数个数有关,可以简单把柯里化理解成是一个收集参数的过程,只有收集够函数所需的参数个数,才会执行函数体,否则返回一个缓存了之前收集的参数的函数。
  • 4大map函数都要接受两个参数,namespace和map
  • 由柯里化:mapState函数有2个参数,要收集够2个参数才会执行mapState的函数体
  • createNamespacedHelpers的作用是让mapState收集第一个参数namespace,由于还差一个参数map,所以返回的是一个缓存了namespace参数的函数,继续接收下一个参数map
  • 所以被createNamespacedHelpers返回的mapState只需传入1个参数map就可以执行了,且传入的第一个参数必须是map,因为namespace已经收集到了,再传入namespace最终执行的结果会是mapState(namespace, namespace)
  • 总之,如果了解过柯里化,这里应该很好理解。

mapState、mapMutations、mapActions、mapGetters

mapState

  1. /**
  2. * Reduce the code which written in Vue.js for getting the state.
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} states # Object's item can be a function which accept state and getters for param, you can do something for state and getters in it.
  5. * @param {Object}
  6. */
  7. export const mapState = normalizeNamespace((namespace, states) => {
  8. const res = {}
  9. normalizeMap(states).forEach(({ key, val }) => {
  10. res[key] = function mappedState () {
  11. let state = this.$store.state
  12. let getters = this.$store.getters
  13. if (namespace) {
  14. const module = getModuleByNamespace(this.$store, 'mapState', namespace)
  15. if (!module) {
  16. return
  17. }
  18. state = module.context.state
  19. getters = module.context.getters
  20. }
  21. return typeof val === 'function'
  22. ? val.call(this, state, getters)
  23. : state[val]
  24. }
  25. // mark vuex getter for devtools
  26. res[key].vuex = true
  27. })
  28. return res
  29. })

解析:

  • 4大map函数最终结果都是返回一个对象{}
  • mappedState其实就是computed的属性的函数,看这个函数要联想到computed,且这个函数的this也是指向vue的
  • 上面的this.$state.statethis.$state.getters是全局的stategetters
  • 接下来就是判断是不是模块,是则拿到模块的state和getter。有种情况用到mapState({ name: (state, getter) => state.name })
  • 最后返回 val 。如果是函数,如上面那样要先执行一遍,再返回函数执行后的值
  • 因为mappedState就是computed中属性的函数,一定是要返回值的。
  • res是个对象,所以可以{ computed: { ...mapState(['name', 'age']) } }
  1. // mapState(['name', 'age'])
  2. const res = {
  3. // { key, val } 其中: key = 'name' val = 'name'
  4. name: function mappedState () {
  5. // 没有命名空间的情况
  6. // 这个函数要用到computed的,这里this指向Vue组件实例
  7. return this.$store.state[name]
  8. },
  9. age: function mappedState () {
  10. // 如果有命名空间的情况
  11. // 如上面源码根据namespace拿到模块module
  12. const state = module.context.state
  13. return state[age]
  14. }
  15. }
  16. // mapState({ name: (state, getter) => state.name })
  17. const res = {
  18. // { key, val } 其中:key = 'name' val = (state, getter) => state.name
  19. name: function mappedState () {
  20. // 没有命名空间
  21. // 如上面代码一样{ key, val }中的 val = (state, getter) => state.name }
  22. const state = this.$store.state
  23. cosnt getter = this.$store.getter
  24. // this 是指向Vue组件实例的
  25. return val.call(this, state, getter)
  26. }
  27. }

mapMutations

  1. /**
  2. * Reduce the code which written in Vue.js for committing the mutation
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} mutations # Object's item can be a function which accept `commit` function as the first param, it can accept anthor params. You can commit mutation and do any other things in this function. specially, You need to pass anthor params from the mapped function.
  5. * @return {Object}
  6. */
  7. export const mapMutations = normalizeNamespace((namespace, mutations) => {
  8. const res = {}
  9. normalizeMap(mutations).forEach(({ key, val }) => {
  10. res[key] = function mappedMutation (...args) {
  11. // Get the commit method from store
  12. let commit = this.$store.commit
  13. if (namespace) {
  14. const module = getModuleByNamespace(this.$store, 'mapMutations', namespace)
  15. if (!module) {
  16. return
  17. }
  18. commit = module.context.commit
  19. }
  20. return typeof val === 'function'
  21. ? val.apply(this, [commit].concat(args))
  22. : commit.apply(this.$store, [val].concat(args))
  23. }
  24. })
  25. return res
  26. })

解析:

  • 这里也要判断是不是模块,不同情况的commit不同,是用全局的还是用模块的
  • mappedMutationmethods的函数,this同样指向Vue的实例
  • val.apply(this, [commit].concat(args)),是这种情况mapMutations({ mutationName: (commit, ...arg) => commit('自定义') })
  • commit.apply(this.$store, [val].concat(args)),是这种情况mapMutations(['CHANGE_NAME'])使用的时候还可以传参数this['CHANGE_NAME'](name)

mapGetters

  1. /**
  2. * Reduce the code which written in Vue.js for getting the getters
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} getters
  5. * @return {Object}
  6. */
  7. export const mapGetters = normalizeNamespace((namespace, getters) => {
  8. const res = {}
  9. normalizeMap(getters).forEach(({ key, val }) => {
  10. // thie namespace has been mutate by normalizeNamespace
  11. val = namespace + val
  12. res[key] = function mappedGetter () {
  13. if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {
  14. return
  15. }
  16. if (process.env.NODE_ENV !== 'production' && !(val in this.$store.getters)) {
  17. console.error(`[vuex] unknown getter: ${val}`)
  18. return
  19. }
  20. return this.$store.getters[val]
  21. }
  22. // mark vuex getter for devtools
  23. res[key].vuex = true
  24. })
  25. return res
  26. })

解析:

  • val = namespace + val这里是,不管是模块的getter还是全局的getter最终都存在一个地方中($store.getters),是模块的会有'/,所以这里要补充namespace + val
  • 所以最后返回的是this.$store.getters[val]
  • 还有mappedGetter对应computed属性的函数,this指向Vue实例

mapActions

  1. /**
  2. * Reduce the code which written in Vue.js for dispatch the action
  3. * @param {String} [namespace] - Module's namespace
  4. * @param {Object|Array} actions # Object's item can be a function which accept `dispatch` function as the first param, it can accept anthor params. You can dispatch action and do any other things in this function. specially, You need to pass anthor params from the mapped function.
  5. * @return {Object}
  6. */
  7. export const mapActions = normalizeNamespace((namespace, actions) => {
  8. const res = {}
  9. normalizeMap(actions).forEach(({ key, val }) => {
  10. res[key] = function mappedAction (...args) {
  11. // get dispatch function from store
  12. let dispatch = this.$store.dispatch
  13. if (namespace) {
  14. const module = getModuleByNamespace(this.$store, 'mapActions', namespace)
  15. if (!module) {
  16. return
  17. }
  18. dispatch = module.context.dispatch
  19. }
  20. return typeof val === 'function'
  21. ? val.apply(this, [dispatch].concat(args))
  22. : dispatch.apply(this.$store, [val].concat(args))
  23. }
  24. })
  25. return res
  26. })

解析:

  • 这个和mapMutations差不多,只是commit换成了dispatch
  • mappedAction对应methods的属性的函数,this也是指向Vue实例

VueX源码分析(2)的更多相关文章

  1. VueX源码分析(5)

    VueX源码分析(5) 最终也是最重要的store.js,该文件主要涉及的内容如下: Store类 genericSubscribe函数 resetStore函数 resetStoreVM函数 ins ...

  2. VueX源码分析(3)

    VueX源码分析(3) 还剩余 /module /plugins store.js /plugins/devtool.js const devtoolHook = typeof window !== ...

  3. VueX源码分析(4)

    VueX源码分析(4) /module store.js /module/module.js import { forEachValue } from '../util' // Base data s ...

  4. VueX源码分析(1)

    VueX源码分析(1) 文件架构如下 /module /plugins helpers.js index.esm.js index.js store.js util.js util.js 先从最简单的 ...

  5. 逐行粒度的vuex源码分析

    vuex源码分析 了解vuex 什么是vuex vuex是一个为vue进行统一状态管理的状态管理器,主要分为state, getters, mutations, actions几个部分,vue组件基于 ...

  6. vuex源码分析3.0.1(原创)

    前言 chapter1 store构造函数 1.constructor 2.get state和set state 3.commit 4.dispatch 5.subscribe和subscribeA ...

  7. vuex 源码分析(七) module和namespaced 详解

    当项目非常大时,如果所有的状态都集中放到一个对象中,store 对象就有可能变得相当臃肿. 为了解决这个问题,Vuex允许我们将 store 分割成模块(module).每个模块拥有自己的 state ...

  8. vuex 源码分析(六) 辅助函数 详解

    对于state.getter.mutation.action来说,如果每次使用的时候都用this.$store.state.this.$store.getter等引用,会比较麻烦,代码也重复和冗余,我 ...

  9. vuex 源码分析(五) action 详解

    action类似于mutation,不同的是Action提交的是mutation,而不是直接变更状态,而且action里可以包含任意异步操作,每个mutation的参数1是一个对象,可以包含如下六个属 ...

随机推荐

  1. python基本数据类型2——操作

    字符串 name = "alex" # 移除两边的空格 print(name.strip()) #strip不修改值 # 是否以"al"开头 print(nam ...

  2. 将RegEx(正则表达式提取器)与JMeter一起使用

    JMeter的,最流行的开源性能测试工具,可以工作正则表达式,用正则表达式提取.正则表达式是一种用于通过使用高级操作提取文本的必需部分的工具.正则表达式在测试Web应用程序时很流行,因为它们可用于验证 ...

  3. css 文本溢出时显示省略号

    .text-ellipsis { width:100px; height:60px; overflow: hidden;//隐藏滚动条 text-overflow:ellipsis; white-sp ...

  4. HDU1409 Is It a Number

    http://acm.hdu.edu.cn/showproblem.php?pid=1409 没啥好说的,至今也不知道到底错在哪里了,看了discuss才过的 #include <iostrea ...

  5. RTX51 Tiny

    参考文档 :RTX51 Tiny 2.02 中文手册.doc.Keil_Rtx51_tiny_RTOS中文版.pdf RTX-51 有 2 个版本:Full 和 Tiny.类似的国人写的 Small ...

  6. (转)通过MySQL复制线程SQL_Thread加快增量恢复binlog

    数据回档常常是使用全量备份+binlog增量实现的.而数据量很大的情况下,增量恢复binlog一直是一个苦恼的问题,因为恢复binlog速度十分慢,并且容易出错. 恢复binlog文件一般有两种方法: ...

  7. 单周期cpu设计代码解读

    目录 写在前面 单周期cpu设计代码讲解 概念回顾 Verilog代码讲解 写在前面 欢迎转载,转载请说明出处. 单周期cpu设计代码讲解 概念回顾 一.电子计算机的部件 分为:中央处理器(cpu). ...

  8. PS高级特训班 百度云资源(价值2180元)

    课程目录:   第1章第一期1第一节 火焰拳头1:12:252第二节 荷叶合成00:05:143第三节 新年巨惠海报(一)1:00:374第四节 新年巨惠海报(二)1:05:345第五节 美食印刷品1 ...

  9. ruby 字符串常用方法学习

    引用链接:http://www.blogjava.net/nkjava/archive/2010/01/03/308088.html 1,切片:silce, [ ]-----------------[ ...

  10. Appium基础四:Desired Capabilities详讲

    Desired Capabilities在启动session的时候是必须提供的,先看如下代码: Desired Capabilities本质上是key value的对象,他告诉appium serve ...