VueX源码分析(1)

文件架构如下

  • /module
  • /plugins
  • helpers.js
  • index.esm.js
  • index.js
  • store.js
  • util.js

util.js

先从最简单的工具函数开始。

find函数

/**
* Get the first item that pass the test
* by second argument function
*
* @param {Array} list
* @param {Function} f
* @return {*}
*/
export function find (list, f) {
return list.filter(f)[0]
}

find函数的测试用例

it('find', () => {
const list = [33, 22, 112, 222, 43]
expect(find(list, function (a) { return a % 2 === 0 })).toEqual(22)
})

解析:

  • 先用断言函数f过滤列表list,最后取过滤后列表的第一个元素。

deepCopy函数

/**
* Deep copy the given object considering circular structure.
* This function caches all nested objects and its copies.
* If it detects circular structure, use cached copy to avoid infinite loop.
*
* @param {*} obj
* @param {Array<Object>} cache
* @return {*}
*/
export function deepCopy (obj, cache = []) {
// just return if obj is immutable value
if (obj === null || typeof obj !== 'object') {
return obj
} // if obj is hit, it is in circular structure
const hit = find(cache, c => c.original === obj)
if (hit) {
return hit.copy
} const copy = Array.isArray(obj) ? [] : {}
// put the copy into cache at first
// because we want to refer it in recursive deepCopy
cache.push({
original: obj,
copy
}) Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key], cache)
}) return copy
}

deepCopy的测试用例

  // 普通结构
it('deepCopy: nornal structure', () => {
const original = {
a: 1,
b: 'string',
c: true,
d: null,
e: undefined
}
const copy = deepCopy(original) expect(copy).toEqual(original)
}) // 嵌套结构
it('deepCopy: nested structure', () => {
const original = {
a: {
b: 1,
c: [2, 3, {
d: 4
}]
}
}
const copy = deepCopy(original) expect(copy).toEqual(original)
}) // 循环引用结构
it('deepCopy: circular structure', () => {
const original = {
a: 1
}
original.circular = original const copy = deepCopy(original) expect(copy).toEqual(original)
})

解析:

  • 功能:支持循环引用的深克隆函数
  • 第一个if判断obj === null || typeof obj !== 'object'判断如果不是引用类型直接返回(基本类型是值拷贝),也是递归的一个出口。
  • 第二个判断hit是判断是不是循环引用,由于是循环引用,在cache中应该有缓存到一份拷贝,直接取cache的,避免再次重复拷贝一份。
  • 什么是循环引用看测试用例第三个original.circular = original,循环引用和被引用的内容是一样的,用缓存就是避免重复的克隆(内容一样)
  • original.circular是循环引用,original是被循环引用
  • 先把cope放到cache中,是在递归的时候,如果遇到循环引用,要确保cache中有一份被循环引用的copy,但是copy必须是引用类型。
  • 为什么cope必须是引用类型?循环引用保存的是引用不是内容(这时候还没拷贝完),在递归的时候并未完成拷贝,只有递归跑完了才完成拷贝,这样未来被循环引用的内容改变时(拷贝完),循环引用的内容同步改变
  • 所以const copy = Array.isArray(obj) ? [] : {}必须是引用类型。
  • 最后Object.keys可以遍历对象和数组的所有键名(只返回实例的属性,不包含原型链和Symbol),实现递归克隆。
  • 一共两个出口,一个是基本类型,另一个是循环引用。

forEachValue

/**
* forEach for object
*/
export function forEachValue (obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key))
}

测试用例

  it('forEachValue', () => {
let number = 1 function plus (value, key) {
number += value
}
const origin = {
a: 1,
b: 3
} forEachValue(origin, plus)
expect(number).toEqual(5)
})

解析:

  • 一个遍历对象的函数(支持对象和数组)
  • fn(value, key)但是回调函数第一个参数是值,第二个参数是键值

isObject

export function isObject (obj) {
return obj !== null && typeof obj === 'object'
}

测试用例

  it('isObject', () => {
expect(isObject(1)).toBe(false)
expect(isObject('String')).toBe(false)
expect(isObject(undefined)).toBe(false)
expect(isObject({})).toBe(true)
expect(isObject(null)).toBe(false)
expect(isObject([])).toBe(true)
expect(isObject(new Function())).toBe(false)
})

解析:

  • 判断是不是对象,这里没有判断是不是原生对象,数组也是通过的。
  • 由于typeof null === 'object'要先判断是不是null

isPromise

export function isPromise (val) {
return val && typeof val.then === 'function'
}

测试用例

  it('isPromise', () => {
const promise = new Promise(() => {}, () => {})
expect(isPromise(1)).toBe(false)
expect(isPromise(promise)).toBe(true)
expect(isPromise(new Function())).toBe(false)
})

解析:

  • 判断是不是Promise
  • 首先判断val不是undefined,然后才可以判断val.then,避免报错
  • 判断依据是val.then是不是函数

assert

export function assert (condition, msg) {
if (!condition) throw new Error(`[vuex] ${msg}`)
}

测试用例:

  it('assert', () => {
expect(assert.bind(null, false, 'Hello')).toThrowError('[vuex] Hello')
})

解析:

  • 断言函数,断言不通过抛出一个自定义错误信息的Error

index.jsindex.esm.js

index.js

import { Store, install } from './store'
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' export default {
Store,
install,
version: '__VERSION__',
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers
}

index.esm.js

import { Store, install } from './store'
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers' export default {
Store,
install,
version: '__VERSION__',
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers
} export {
Store,
install,
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers
}

解析:

  • 区别就是index.esm.jsindex.js多了个一个导入模式
  • import Vuex, { mapState } from 'index.esm.js':有两种方式导入
  • import Vuex from 'index.js':只有一种方式导入

mixin.js

export default function (Vue) {
const version = Number(Vue.version.split('.')[0]) if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit })
} else {
// override init and inject vuex init procedure
// for 1.x backwards compatibility.
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {}) {
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit
_init.call(this, options)
}
} /**
* Vuex init hook, injected into each instances init hooks list.
*/ function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
}

解析:

  • 为什么每个组件都拥有\(store属性,也即每个组件都能拿到\)store
  • Vue2直接用mixin和钩子函数beforeCreate,Vue1用外观(装饰者)模式重写Vue._init函数。
  • vuexInit是将全局注册的store注入到当前组件中,在创建该组件之前
  • \(options是`new Vue(options)`的options,\)options中有store
  • 由于beforeCreateVue的周期钩子,this指向当前组件实例,所以this.$store可以把store直接注入当前组件
  • 所有组件都是继承于一个全局Vue的,全局mixin组件周期钩子beforeCreate,这样每个组件都能自动注入store,也即每个组件都能直接通过$store拿到全局Vuenew Vue({ el: 'app', store, router })store

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

  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源码分析(2)

    VueX源码分析(2) 剩余内容 /module /plugins helpers.js store.js helpers要从底部开始分析比较好.也即先从辅助函数开始再分析那4个map函数mapSta ...

  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. Robot Frame应用实例讲解

    关键字与变量 内置库(build in)关键字 要使用的其它库关键字(学会看文档)

  2. tableview中用动画效果改变cell的高度

    我们要的效果大概就是如下效果: 当我们选择一个cell的时候,我们就要改变它的高度,并且以动画的形式. 我们该如何实现这个效果呢?我们主要需要使用以下这两个方法: - (CGFloat)tableVi ...

  3. log4j.properties错误及配置详解

    当在Eclipse上运行MapReduce程序遇到以上问题时,请检查项目中是否有log4j.properties配置文件,或者配置文件是否正确. 刚接触Hadoop的时候不太了解log4j.prope ...

  4. 《javascript设计模式》笔记之第九章:组合模式

    之前一直都是按照书的结构顺序做总结,觉得好像不是很好,现在试着完全按照自己的理解做总结.例子还是书上的例子. 一:组合模式的作用: 在web开发中,主要用于创建嵌套的html结点,使得我们方便的把各种 ...

  5. mongodb的创建删除数据库

    1.创建数据库 use 命令 MongoDB use DATABASE_NAME 用于创建数据库.该命令将创建一个新的数据库,如果它不存在,否则将返回现有的数据库. 语法: use DATABASE  ...

  6. tomcat服务器,从前端到后台到跳转

    前端页面: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...

  7. Windows中将nginx添加到服务

    下载安装nginx http://nginx.org/en/download.html 下载后解压到C盘 C:\nginx-1.14.0 添加服务 需要借助"Windows Service ...

  8. hadoop的shell总结

    ls命令(文件内容) 列出系统跟目录下的目录和文件 Hadoop fs -ls / 列出文件系统所有的目录和文件 Hadoop fs -ls -R / cat命令(列出文档内容) Hadoop fs ...

  9. SharePoint Online 缺少“将站点另存为模板”

    之前文章行给出在SharePoint 2010 .SharePoint 2013 中将站点保存模板选项的文章.其实同样的问题出现在Microsoft Office 365的一部分SharePoint ...

  10. JavaScript_4_数据类型

    1. JavaScript对大小写敏感. 2. JavaScript是脚本语言.浏览器会在读取代码时,逐行地执行脚本代码.而对于传统编程来说,会在执行前对所有代码进行编译. 3. 变量什么用var, ...