从年初开始,用了将近6个月的vue,起初很懵逼,但是撸完一遍vue官方教程后,会用一点,但是vuex我还是很懵逼,偶然看到一篇博客解析vue源码,很受启发,决定挖坑,我也要做vue源码解析,我觉得我也可以做到。虽然我仅仅只是知道vueMVVM双向绑定是基于Object.definedPrototype()来设置set 和get 。
version": "2.5.17-beta.0
当然就是vue这个对象了。先看web端的吧,服务器端的不看先。Vue.js\src\platforms\web\entry-runtime.js
这就是我第一次见到的最表层的vue对象了,
进一步深入,Vue对象在runtime/index.js
文件中引入,
点开一看,发现依然在更深层次core中的index.js文件中引入
继续点开,终于发现了Vue的构造函数,结构如下图。它位于Vue.js\src\core\instance\index.js
中,
构造函数,做了啥???不难看出,this instanceof Vue
,this即是vue构造函数本身,vue构造函数是实例化的 new Vue
所返回的vue实例,this指向vue实例,实例原型必须是有vue,否则报错,意思就是强制使用new 实例化vue对象。
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
首先看 initMixin函数,他到底做了啥??他为vue扩展了_init
方法
stateMixin到底做了啥??他为vue扩展了$watch
方法,
接下来是eventsMixin
函数,他为vue扩展了$on
,$once
,$off
,$emit
方法;
接下来是lifecycleMixin
函数,他为vue扩展了_update
,forceUpdate
,destroy
,方法,表面意思就是声明周期混合。
接下来是renderMixin
函数,他为vue扩展了$nextTick
,_render
,方法,
好吧,拥有这些初始化方法的vue构造函数成为了一个最基本的vue对象。 下面的百度脑图清晰描述vue构造函数下面的基本方法 其中_开头的是内部方法,而$开头的方法是外部方法,供外部使用。
那么vue构造函数就基本方法已经清楚,那么接下来从初始化一个vue实例开始看,vue源码。
import App from './App.vue';
window.app = new Vue({
data: {
msg: 'sfdf'
},
render (h) {
return h('p', this.msg)
}
}).$mount('#root')
setTimeout(() => {
app.msg = '232423'
}, 1000)
在浏览器控制台打印app,发现有msg这条属性,同时有set和get 打开源码发现首先执行下面代码,option就是以下内容
this._init({
data: {
msg: 'sfdf'
},
render (h) {
return h('p', this.msg)
}
})
,那么下一步查看_init
方法,
// 只截取关键代码
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
字面意思就知道,是合并option属性的,mergeOption
函数接受3个参数,第一个是父,第二个是子,第三个是vue对象本身。
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
const extendsFrom = child.extends
if (extendsFrom) {
parent = mergeOptions(parent, extendsFrom, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
上面这段函数可以看出,他会合并父子属性,而后合并子属性的宏。
之后就是核心部分,
vm._self = vm // 初始化的时候,自己就是自己
initLifecycle(vm) // 执行初始化生命周期
initEvents(vm) // 初始化事件
initRender(vm) // 初始化渲染
callHook(vm, 'beforeCreate') // 初始化 执行 beoforeCreate 钩子
initInjections(vm) // resolve injections before data/props
initState(vm) // 初始化状态
initProvide(vm) // resolve provide after data/props 初始化提供???
callHook(vm, 'created') // 调用created钩子
vm.$parent = parent // 将传入参数的parent赋值到vm实例的parent上面,parent是对外暴露属性。
vm.$root = parent ? parent.$root : vm// 根节点如果是parent,则是它的根节点,否则根节点就是我本身
vm.$children = [] // 子节点是数组
vm.$refs = {} // 用于访问子组件 中 定义了ref=“name”,
vm._watcher = null // 观察者
vm._inactive = null // 活动的
vm._directInactive = false // 啥意思,草
vm._isMounted = false // 是否挂载
vm._isDestroyed = false // 是否被销毁
vm._isBeingDestroyed = false // 是否正在被销毁
明显就是为vue构造对象,初始化一些实例,
initEvents
函数export function initEvents (vm: Component) {
vm._events = Object.create(null)
vm._hasHookEvent = false
// init parent attached events
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
貌似是父子组件在通讯的时候用到,this.emit('234')
方法;
initRender
函数export function initRender (vm: Component) {
vm._vnode = null // the root of the child tree
vm._staticTrees = null // v-once cached trees
const options = vm.$options
const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
const renderContext = parentVnode && parentVnode.context
vm.$slots = resolveSlots(options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
const parentData = parentVnode && parentVnode.data
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
}, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
}, true)
} else {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}
}
初始化渲染函数,查看其中的$createElement
,这个属性用于创建虚拟节点,
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) // 第一个参数默认是 vm
vm.$createElement('p','12321') // 会输出一个虚拟的<p>123</p> 节点
下面查看createElement方法
export function createElement (
context: Component, // vm实例
tag: any, // 标签名 eg: p,div,span
data: any, // 值
children: any, // 子节点,类似于domcument.body.children // []
normalizationType: any, // 他应该是某个数字吧。
alwaysNormalize: boolean // 是否需要初始化
): VNode | Array<VNode> {
if (Array.isArray(data) || isPrimitive(data)) {
normalizationType = children
children = data
data = undefined
}
if (isTrue(alwaysNormalize)) {
normalizationType = ALWAYS_NORMALIZE
}
return _createElement(context, tag, data, children, normalizationType)
}
callHook
函数export function callHook (vm: Component, hook: string) {
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget()
const handlers = vm.$options[hook]
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) { // beforeCreate, 遍历他所有的方法
try {
handlers[i].call(vm) // 为每一个方法调用的时候绑定this。方法内部的this就是vm,前提是你用的普通函数,才有this,箭头函数是没有this的。
} catch (e) {
handleError(e, vm, `${hook} hook`) // 否则报错
}
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook) // 如果有钩子事件,就发送给他的父组件。
}
popTarget()
}
接下来是initInjections
函数,
export function initInjections (vm: Component) {
const result = resolveInject(vm.$options.inject, vm) // 另一个函数,传入vm实例,
if (result) {
toggleObserving(false)
Object.keys(result).forEach(key => {
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
defineReactive(vm, key, result[key], () => {
warn(
`Avoid mutating an injected value directly since the changes will be ` +
`overwritten whenever the provided component re-renders. ` +
`injection being mutated: "${key}"`,
vm
)
})
} else {
defineReactive(vm, key, result[key])
}
})
toggleObserving(true)
}
}
暂时看不懂,下一个
initState
函数export function initState (vm: Component) { // 用于初始化一些状态
vm._watchers = [] // 设置一个要被观察的空对象,
const opts = vm.$options // opts就是vm的传入的参数
initProps(vm, opts.props) // 初始化prop,也就是父组件传入的参数
initMethods(vm, opts.methods) // 初始化一些可以在模板中,使用的自定义函数
initData(vm) // 初始化data,如果没有,就设置成根节点 传入`{}`
initComputed(vm, opts.computed) // 初始化计算属性 computed
initWatch(vm, opts.watch) // 初始化要观察的对象
}
挑一两个来说吧,
initdata
用于初始化数据模型,下面是删减过的函数initdatafunction initData (vm: Component) {
let data = vm.$options.data // 拿到data函数返回的一个新的对象(数据模型Model)
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// proxy data on instance
const keys = Object.keys(data) // 枚举所有属性的键值
const props = vm.$options.props // 检查data是否和prop重复要用到
const methods = vm.$options.methods // 检查data是否和methods中的属性是否重,要用到
let i = keys.length
while (i--) { // 遍历每一个data属性,
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) { // 如果 methods有 和 data的属性 重复就报错
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) { // 如果 prop 有 和 data的属性 重复就报错
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key) // 如果都可以就代理vm对象 中的所有data属性
}
}
// observe data ,data 和 prop对象不能有重复的属性,
observe(data, true /* asRootData */) // 最主要的函数,观察data对象,
}
proxy函数
const sharedPropertyDefinition = {
enumerable: true, // 可枚举
configurable: true, // 可设置
get: noop, // function(){} 空函数
set: noop // function(){} 空函数
}
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () { // 空函数的get返回这个属性的值,代理优化了值,this._data.msg = this.msg
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) { // 空函数的set设置函数的值,代理优化了值,this._data.msg = this.msg
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
observe 可以说是vue的核心,下面就是obserber函数。
// observe 函数
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
这个没啥好看,虽然我也看不太懂,但是关键在于Observe对象
// Observe
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (Array.isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value) // 如果是数组
} else {
this.walk(value) // 如果是对象
}
}
/**
* Walk through each property and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) { // 如果是对象,用object.keys 来遍历对象所有可枚举属性,然后调用defineReactive函数,并传入对象,和他的键值。
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
/**
* Observe a list of Array items.
*/
observeArray (items: Array<any>) { // 如果是数组,遍历所有数组,重新调用该方法,直到它全部是对象位置,真的很巧妙。
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i]) // 如果是数组就递归自身。
}
}
}
/**
* 设置响应式函数
*/
export function defineReactive (
obj: Object, // 对象
key: string, // 键值
val: any, // 值
customSetter?: ?Function, // set函数?
shallow?: boolean // 浅??
) {
const dep = new Dep() // 构造dep对象
const property = Object.getOwnPropertyDescriptor(obj, key) // 用于获取obj对象的描述,也就是获取传入对象的一些自定义属性。
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get // 获取传入的data原本的get属性,
const setter = property && property.set // 获取传入的data原本的set属性,
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
Reference