VueX源码分析(1)
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.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是一个对象,可以包含如下六个属 ...
随机推荐
- Python序列化模块pickle和json使用和区别
这是用于序列化的两个模块: • json: 用于字符串和python数据类型间进行转换 • pickle: 用于python特有的类型和python的数据类型间进行转换 Json模块提供了四个功能:d ...
- 剑指Offer的学习笔记(C#篇)-- 二进制中1的个数
题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 一 . 解题思路 新颖的解法,使得该题目运用到了二进制的位运算符.先了解一下位运算符! 此题便很好的发挥了位运算符& ...
- 剑指Offer的学习笔记(C#篇)-- 斐波那契数列
题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). 一 . 理解概念 斐波那契数列概念:斐波那契数列(Fibonacci sequence), ...
- ES6入门教程---变量和常量
ES6提出了两个新的声明变量的命令:let 和 const 1. 建议不再使用var,而使用let 和const .优先使用const. 在定义之后值是固定不变的,即为常量 常量的值不能修改,但是如果 ...
- Js 数组对象排序
1.定义函数 /** * 数组对象排序函数 * @param {any} name 排序字段 * @param {any} order 升.降(这里事true.false记得处理下) */ var b ...
- mac 增加/usr/bin目录的操作无权限
起因:想要往/usr/bin目录下拷贝文件,提示: operation not permitted 解决办法: 1. 重启,启动中长安command+r进入恢复模式 2.顶部菜单拉中打开终端 3. 输 ...
- 2019湘潭校赛 H(dp)
题目传送 dp是常规的:\(m^2\)的预处理:把位置存进vector然后\(O(1)\)算出想要的:WA点:要注意特意设置一下val[i][v.size()]=0,即全天都放鸽子则花费时间为0. # ...
- Netty(2)Echo
上节介绍的是discard协议,即不给客户端返回消息.本节主要说下,echo协议,即服务端收到消息后原样返回给客户端. 为了实现此需求,只需要在DiscardServerHandler中重写chann ...
- NSSM把.Net Core部署至 Windows 服务
NSSM把.Net Core部署至 Windows 服务 https://www.cnblogs.com/emrys5/p/nssm-netcore.html 为什么部署至Windows Servic ...
- setTimout( , 0) 详解
setTimout( , 0) 一.前言 前端工程师们工作久了,一般都会在某些地方看见过这样的代码: setTimeout(function(){ // TODO }, 0); 举个实例,移动端我们经 ...