new之前的准备,TARGET:web-full-dev
src/core/instance/index.js
创建使用ES5语法创建Vue构造函数
接着依次执行,一下都是在Vue原型对象上的混入方法和属性,或者访问器属性等:
initMixin(Vue) //混入_init()
stateMixin(Vue) //混入$delete,$set,$watch函数,$data,$props属性,$data,$props访问器属性
eventsMixin(Vue) //混入$emit,$off,$on,$once函数
lifecycleMixin(Vue) //混入$forceUpdate,$destroy,_update函数
renderMixin(Vue) //混入$nextTick,_render,在
installRenderHelpers里混入一些内部函数_n,_o,_s,_l,_t,_q,_i,_m,_f,_k,_b,_v,_e,_u,_g,_d,_p
src/core/index.js
Object.defineProperty(Vue, 'config', configDef)
config
//util里的方法有风险,谨慎使用。
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set
Vue.delete
Vue.nextTick
Vue.observable
Vue.options
initUse(Vue) //Vue.use
initMixin(Vue) //Vue.mixin
initExtend(Vue) //Vue.cid, Vue.extend
initAssetRegisters(Vue)//混入'component','directive','filter'属性
Vue.prototype.$isServer
Vue.prototype.$ssrContext
Vue.FunctionalRenderContext
Vue.version
src/platforms/web/runtime/index.js
Vue.config.mustUseProp
Vue.config.isReservedTag
Vue.config.isReservedAttr
Vue.config.getTagNamespace
Vue.config.isUnknownElement
Vue.prototype.__patch__
Vue.prototype.$mount
src/platforms/web/entry-runtime-with-compiler.js
Vue.prototype.$mount //重新复制,将上一个$mount的函数缓存起来,执行完这个新函数后,执行以前的$mount
Vue.compile
最终形成这么一个对象
Vue {
$data: undefined,
$isServer: false,
$props: undefined,
$route: '',
$router: '',
$ssrContext: undefined,
__proto__: {
$delete: ƒ del(target, key),
$destroy: ƒ (),
$emit: ƒ (event),
$forceUpdate: ƒ (),
$mount: ƒ ( el, hydrating ),
$nextTick: ƒ (fn),
$off: ƒ (event, fn),
$on: ƒ (event, fn),
$once: ƒ (event, fn),
$set: ƒ (target, key, val),
$watch: ƒ ( expOrFn, cb, options ),
__patch__: ƒ patch(oldVnode, vnode, hydrating, removeOnly),
_b: ƒ bindObjectProps( data, tag, value, asProp, isSync ),
_d: ƒ bindDynamicKeys(baseObj, values),
_e: ƒ (text),
_f: ƒ resolveFilter(id),
_g: ƒ bindObjectListeners(data, value),
_i: ƒ looseIndexOf(arr, val),
_init: ƒ (options),
_k: ƒ checkKeyCodes( eventKeyCode, key, builtInKeyCode, eventKeyName, builtInKeyName ),
_l: ƒ renderList( val, render ),
_m: ƒ renderStatic( index, isInFor ),
_n: ƒ toNumber(val),
_o: ƒ markOnce( tree, index, key ),
_p: ƒ prependModifier(value, symbol),
_q: ƒ looseEqual(a, b),
_render: ƒ (),
_s: ƒ toString(val),
_t: ƒ renderSlot( name, fallback, props, bindObject ),
_u: ƒ resolveScopedSlots(),,
_update: ƒ (vnode, hydrating),
_v: ƒ createTextVNode(val),
$data: (...),
$isServer: (...),
$props: (...),
$route: (...),
$router: (...),
$ssrContext: (...),
constructor: ƒ Vue(options),
get $data: ƒ (),
set $data: ƒ (),
get $isServer: ƒ (),
get $props: ƒ (),
set $props: ƒ (),
get $route: ƒ (),
get $router: ƒ (),
get $ssrContext: ƒ (),
},
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf(),
__defineGetter__: ƒ __defineGetter__(),
__defineSetter__: ƒ __defineSetter__(),
__lookupGetter__: ƒ __lookupGetter__(),
__lookupSetter__: ƒ __lookupSetter__(),
get __proto__: ƒ __proto__(),
set __proto__: ƒ __proto__(),
}
}
初始化Vue完毕,等待用户new Vue
当new Vue并且传入对象时,执行_init
生成一个内部vm._uid,uid从0开始累加
vm._isVue为true //a flag to avoid this being observed
更新vm.$options
更新vm._renderProxy
添加一个循环引用vm._self = vm
接下来一系列函数运行
initLifecycle(vm)
/*
该函数初始化一些实例属性
vm.$parent
vm.$root
vm.$children
vm.$refs
vm._watcher
vm._inactive
vm._directInactive
vm._isMounted
vm._isDestroyed
vm._isBeingDestroyed
*/
initEvents(vm)
/*
初始化事件
这里的事件其实是指的是使用vm的$once, $on,$off,$emit等的事件,
这些是用的发布订阅者模式来触发各种事件。
这里其实就是创建了一个原型对象为null的对象而已
vm._events = Object.create(null)
vm._hasHookEvent = false
*/
initRender(vm)
/*
初始化属性
vm._vnode
vm._staticTrees
vm.$vnode
vm.$slots
vm.$scopedSlots
vm._c和vm.$createElement
这俩函数是对createElement的封装。
createElement内部封装一个内部函数_createElement
这个函数是用来创建VNode的
劫持 vm, '$attrs'
劫持 vm, '$listeners'
*/
callHook(vm, 'beforeCreate')
/*
这里调用生命周期钩子beforeCreate
*/
initInjections(vm) // resolve injections before data/props
/*
初始化注入,过程是将vm.$options.inject里的每个属性的from作为vm._provided的键值组成一个原型对象指向null的对象result里,并且遍历result里每个键值,执行 defineReactive(vm, key, result[key]);
其中defineReactive函数封装的是Object.defineProperty。
*/
initState(vm)
/*
初始化对象数据
反正在创建vue实例时,对象里的数据和方法属性都被初始化了,如下:
初始化props,initProps
初始化methods,initMethods
初始化data,initData//这里很重要,这里递归对象,使用Object.defineProperty监听了对象属性
初始化computed,initComputed
初始化watch,initWatch
*/
initProvide(vm) // resolve provide after data/props
/*
vm.$options.provide 是否是函数,如果是就执行并把结果赋值给vm._provided,如果不是就直接返回给vm._provided
*/
callHook(vm, 'created')
/*
调用生命周期钩子created
*/
初始化vm._name
vm. o p t i o n s . e l 有 值 , 就 执 行 v m . options.el有值,就执行vm. options.el有值,就执行vm.mount(vm.$options.el)
执行src/platforms/web/entry-runtime-with-compiler.js,判断是否有render函数,没有的话就生成一个。vm. o p t i o n s . r e n d e r , v m . options.render,vm. options.render,vm.options.staticRenderFns。
获取template里的模板字符串,如果没有template就获取el里面的模板字符串,然后生成ast树,然后在优化ast,最终给render生成一个匿名函数,函数内部格式为with(this){return ${code}}`,这里code包含这一个createElement的封装
执行生命周期钩子beforeMount
创建watcher,把组件加入watcher里,执行updateComponent
updateComponent封装了vm._render(),这函数封装了vm.$options.render最终生成VNode
执行vm.patch,进到src/core/vdom/patch.js进行diff对比进行dom的渲染
执行生命周期钩子mounted。
整体流程执行完成