new Vue(obj)时,底层都经历了什么
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
- initGlobalAPI 混入一些全局API
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开始累加
扫描二维码关注公众号,回复: 17142776 查看本文章 -
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。
-
整体流程执行完成