当我们写下这段简单new Vue()代码,vue框架做了什么呢?
var vm = new Vue({
el:"#app",
data:{
msg:'tttt'
}
})
会调用src/core/instance/index.js的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)
}
寥寥几句,承载了Vue的构造大业。判断当前的环境是不是生产环境,以及是不是采用new操作符创建的Vue对象,如果不是,则给出告警。接下来调用_init方法,是我们要重点分析的,其入参options就是我们定义的对象时传入的参数对象。
{
el:"#app",
data:{
msg:'tttt'
}
}
_init()的实现在src/core/instance/init.js中。
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
/**
*第二部分,合并相关option
*/
// merge options
console.log("befor merge",vm.$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
)
}
console.log("after merge",vm.$options);
/**
*第三部分,初始化相关功能
*/
/* 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)
}
/**
*第四部分,mount
*/
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
我们将_init过程划分四个部分分析。
第一部分:设置属性,uid以及isVue的flag,没啥好说的。
第二部分:对options进行合并,vue会将相关的属性和方法都统一放到vm.$options中,为后续的调用做准备工作。vm.$option的属性来自两个方面,一个是Vue的构造函数(vm.constructor)预先定义的,一个是new Vue时传入的入参对象。以开篇的实例为例,合并完成后的options属性包括:
第三部分,初始化各类属性和事件,我们梳理成下面的导图
initProxy:设置vm._renderProxy。
initLifecycle:初始化一系列变量。
initEvents:存储父组件绑定当前子组件的事件,保存到vm._events。
initRende:定义vm._c和 vm.$createElement等方法。
callHook:调用生命周期的钩子相关方法,包括beforeCreate,created。
initInjections与initProvide:通过逐级查找,从父级provide中获取子级组件inject定义的属性值,并增加对该属性的监听。
initState:是对prop,method,data,computed,watch的初始化,增加对定义属性的监听。
第四部分:挂载。如果说前面几部分都是准备阶段,那么这部分是整个new Vue的核心部分,将template编译成render表达式,然后转化为大名鼎鼎的Vnode,最终渲染为真实的dom节点。同样我们给出流程图(后面我们将重点分析)。
Vue 的初始化逻辑写的非常清楚,把不同的功能逻辑拆成一些单独的函数执行,让主线逻辑一目了然。本篇对每个部分的作用做了大概的描述,如果仅做一般性的了解,可以跳过接下来的章节;如详细了解,请做好准备,下面一起享受每部分的源码。
上一篇:VUE源码学习第二篇--准备工作 下一篇:VUE源码学习第四篇--new Vue都干了啥(options合并)