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是一个对象,可以包含如下六个属 ...
随机推荐
- 使用Git版本控制工具管理GitHub
使用Git版本控制工具管理GitHu Git是一个分步式的管理系统:只要上传操作得当,所有的都可以相当于是中央服务器,成员代码共享,A写的代码B也有,一般把一个人当做主机,其他人通过该主机拼装代码 ...
- 常用的js工具函数
JS选取DOM元素的方法注意:原生JS选取DOM元素比使用jQuery类库选取要快很多1.通过ID选取元素document.getElementById('myid');2.通过CLASS选取元素do ...
- mac mysql 编码配置
mac mysql 编码配置 (mysql目录下没有my.cnf) 想要修改编码发现自己的/usr/local/mysql/support-files里面根本没有my.cnf 安装方式是去mysql官 ...
- Codeforces Round #563 (Div. 2) B. Ehab Is an Odd Person
链接:https://codeforces.com/contest/1174/problem/B 题意: You're given an array aa of length nn. You can ...
- 7-36 旅游规划 (25 分(Dijkstra)
有了一张自驾旅游路线图,你会知道城市间的高速公路长度.以及该公路要收取的过路费.现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径.如果有若干条路径都是最短的,那么需要输出最便 ...
- 线程池(3)Executors.newCachedThreadPool
例子: ExecutorService es = Executors.newCachedThreadPool(); try { for (int i = 0; i < 20; i++) { Ru ...
- net core 中间件管道
net core 中间件管道 .net core 管道(Pipeline)是什么? 由上图可以看出,.net core 管道是请求抵达服务器到响应结果返回的中间的一系列的处理过程,如果我们简化一下成下 ...
- Linux学习笔记——如何使用echo指令向文件写入内容
0.前言 本文总结如何使用echo命令向文件中写入内容,例如使用echo指令覆盖文件内容,使用echo指令向文件追加内容,使用echo指令往文件中追加制表符. echo向文件中输出内容 ...
- spring之控制反转
IOC (Inversion of Control) 控制反转 我的理解:将创建对象的控制权从代码本身转交给了外部容器(spring容器). 1.将组件对象(业务对象)的控制权从代码本身转移到外部容器 ...
- Python3+selenium3环境搭建笔记
系统:win7 64位浏览器:ie9 64位 chrome70 32位 firefox63 64位python版本:3.6.5 Windows x86 executable installersele ...