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. 基于ZFAKA二次开发,添加PayJS支付渠道

    项目地址:https://github.com/hiyouli/payjs-for-zfaka 关于ZFAKA,请移步:ZFAKA 免费.安全.稳定.高效的发卡系统,值得拥有! 演示地址:http:/ ...

  2. Uva11572

    读入可以不需要存入数组 #include<bits/stdc++.h> #define inf 0x3f3f3f3f //const int maxn=; using namespace ...

  3. Python 开发基础-字符串类型讲解(字符串方法)-2

    s = 'Hello World!'print(s.index('W',0,9))#返回某个字母的索引值,本例返回6.没有该字母会报错,和FIND比较像,find不会报错,没找到会返回-1print( ...

  4. 一步步实现自己的ORM(二)

    在第一篇<一步步实现自己的ORM(一)>里,我们用反射获取类名.属性和值,我们用这些信息开发了简单的INSERT方法,在上一篇文章里我们提到主键为什么没有设置成自增长类型,单单从属性里我们 ...

  5. html5 新增表单控件和表单属性

    新的输入型控件 email:电子邮箱文本框,跟普通的没什么区别 当输入不是邮箱的时候,验证通不过 移动端的键盘会有变化         tel:电话号码 一般用于手机端,是一个键盘切换 url:网页的 ...

  6. OPEN SQL

    OPEN SQL 1.SELECT .INSERT.UPDATE.DELETE.MODIFYSELECT 命令包含如下从句:SELECT: 需要查询资料库指定表的那些列,是一行还是多行INTO: 查询 ...

  7. 根据用户ID生成不重复的最小6位随机邀请码

    网上看到一个例子,借鉴修改一下 实现根据long类型的用户ID生成6位随机邀请码,并且根据邀请码能算出用户ID.代码如下: /** 自定义进制(选择你想要的进制数,不能重复且最好不要0.1这些容易混淆 ...

  8. ORACLE中能否找到未提交事务的SQL语句

      在Oracle数据库中,我们能否找到未提交事务(uncommit transactin)的SQL语句或其他相关信息呢?  关于这个问题,我们先来看看实验测试吧.实践出真知. 首先,我们在会话1(S ...

  9. 一键部署WordPress开源内容管理系统

    https://market.azure.cn/Vhd/Show?vhdId=9857&version=10889 产品详情 产品介绍WordPress是一款个人博客系统,并逐步演化成一款内容 ...

  10. 实现memcopy函数

    实现memcopy函数: void * memcpy(void *dest, const void *src, unsigned int count); { if ((src == NULL) || ...