VueX源码分析(1)
VueX源码分析(1)
文件架构如下
/module/pluginshelpers.jsindex.esm.jsindex.jsstore.jsutil.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.js和index.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.js比index.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
- 由于
beforeCreate是Vue的周期钩子,this指向当前组件实例,所以this.$store可以把store直接注入当前组件 - 所有组件都是继承于一个全局Vue的,全局mixin组件周期钩子
beforeCreate,这样每个组件都能自动注入store,也即每个组件都能直接通过$store拿到全局Vuenew Vue({ el: 'app', store, router })的store
VueX源码分析(1)的更多相关文章
- VueX源码分析(5)
VueX源码分析(5) 最终也是最重要的store.js,该文件主要涉及的内容如下: Store类 genericSubscribe函数 resetStore函数 resetStoreVM函数 ins ...
- VueX源码分析(3)
VueX源码分析(3) 还剩余 /module /plugins store.js /plugins/devtool.js const devtoolHook = typeof window !== ...
- VueX源码分析(4)
VueX源码分析(4) /module store.js /module/module.js import { forEachValue } from '../util' // Base data s ...
- VueX源码分析(2)
VueX源码分析(2) 剩余内容 /module /plugins helpers.js store.js helpers要从底部开始分析比较好.也即先从辅助函数开始再分析那4个map函数mapSta ...
- 逐行粒度的vuex源码分析
vuex源码分析 了解vuex 什么是vuex vuex是一个为vue进行统一状态管理的状态管理器,主要分为state, getters, mutations, actions几个部分,vue组件基于 ...
- vuex源码分析3.0.1(原创)
前言 chapter1 store构造函数 1.constructor 2.get state和set state 3.commit 4.dispatch 5.subscribe和subscribeA ...
- vuex 源码分析(七) module和namespaced 详解
当项目非常大时,如果所有的状态都集中放到一个对象中,store 对象就有可能变得相当臃肿. 为了解决这个问题,Vuex允许我们将 store 分割成模块(module).每个模块拥有自己的 state ...
- vuex 源码分析(六) 辅助函数 详解
对于state.getter.mutation.action来说,如果每次使用的时候都用this.$store.state.this.$store.getter等引用,会比较麻烦,代码也重复和冗余,我 ...
- vuex 源码分析(五) action 详解
action类似于mutation,不同的是Action提交的是mutation,而不是直接变更状态,而且action里可以包含任意异步操作,每个mutation的参数1是一个对象,可以包含如下六个属 ...
随机推荐
- 阿里云服务器新手安装nginx
1.域名购买之后 备案. 2.购买服务器. 3.这两个条件具备之后进行下一步. 本人使用Mac ,实践流程按照Mac本的流程操作. 作为初学者,简配的服务器, 实例类型: I/O优化,操作系统: Ce ...
- append、replace、replaceAll、indexof、lastIndexOf、substring的用法
1.append StringBuffer buf = new StringBuffer("Hard "): String aString = "Waxworks& ...
- 关于java后台接入百度地图返回参数为{"status":211,"message":"APP SN校验失败"}的解决方法
1.关于百度地图,天气预报,微信公众平台等一系列权威机构java接入的认识: a.首先要认识到的是这些信息都属于外部接口,我们在进行接入的时候一定要注意到这些接口的参数设置,稍微一不留神就会出现接入失 ...
- 福昕阅读器把pdf某一页保存出来
第一步: 第二步: 第三步: 第四步:点击保存即可
- python3的encode()和decode()
python3的encode()和decode() 在python3的内存中. 在程序运行阶段. 使⽤用的是unicode编码. 因为unicode是万国码. 什么内容都可以进行显示. 那么在数据传输 ...
- A.dreamstart的催促
题目描述 有一天集训队的学弟们正在计算一堆数,但是dreamstart感觉他们算的太慢了,就让他们坐在一起想出一个快速计算的方法,但是由于他们一时想不出来,想让你帮助他们.他们说现在有一个数列,要算出 ...
- shell 经典
使用新写法 这里的新写法不是指有多厉害,而是指我们可能更希望使用较新引入的一些语法,更多是偏向代码风格的,比如 尽量使用func(){}来定义函数,而不是func{} 尽量使用[[]]来代替[] 尽量 ...
- 《四 spring源码》手写springioc框架
手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...
- JDBC连接中Class.forName("")到底干了什么?
思考了一个问题,Class.forName("***");到底干了什么? 我们知道Class.forName( )静态方法的目的是为了动态加载类,但是一般来说,一个类forName ...
- Coroutine(协程)模式与线程
概念 协程(Coroutine)这个概念最早是Melvin Conway在1963年提出的,是并发运算中的概念,指两个子过程通过相互协作完成某个任务,用它可以实现协作式多任务,协程(coroutine ...