function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)) {
warn('Vue should be called with the `new` keyword')
}
// 初始化
this._init(options)
}
2.Vue 实例方法_init(options)
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-init:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
/*一个防止vm实例自身被观察的标志位*/
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(
// 解析constructor上的options属性的
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)
/*初始化render*/
initRender(vm)
/*调用beforeCreate钩子函数并且触发beforeCreate钩子事件*/
callHook(vm, 'beforeCreate')
// 初始化 inject, 在数据/Props之前注入
initInjections(vm)
/*初始化props,methods,data,computed与watch*/
initState(vm)
// 初始化 provide
// provide 选项应该是一个对象或返回一个对象的函数。
// 该对象包含可注入其子孙的属性,用于组件之间通信
initProvide(vm)
/*调用created钩子函数并且触发created钩子事件*/
callHook(vm, 'created')
/* istanbul ignore if */
// 性能监控
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
/*格式化组件名*/
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
/*挂载组件*/
vm.$mount(vm.$options.el) // runtime 不能编译模板 runtime-with-compiler template
// 注意 new Vue(options).$mount()为手动挂载
// 在项目中可用于延时挂载
//(例如在挂载之前要进行一些其他操作,判断, 加入路由插件等等)之后要手动挂载上。
}
}
new Vue 的过程
在我们new一个Vue对象的时候, 现在我们就从源码看看经过了哪些流程
1.Vue 构造函数
new Vue(options)这一步调用了实例方法_init
2.Vue 实例方法_init(options)
2.挂载组件 调用vm.$mount方法
mergeOptions方法: 递归合并父子的 options 选项
3.initLifecycle: 初始化生命周期
//更新组件事件 function updateComponentListeners(vm, //虚拟dom listeners, //新的数据队列 oldListeners //旧的事件数据队列 ) { target = vm; //更新数据源 并且为新的值 添加函数 旧的值删除函数等功能 updateListeners(listeners, oldListeners || {}, add, remove$1, vm); target = undefined; }
8.initState(重点): 初始化组件实例数据, 使变为响应式数据
data 的 双向绑定
vm.$mount(vm.$options.el)
new Vue中的options没有传入el的话,就不会触发实际的渲染,就需要自己手动调用了$mount方法;
总结
1.new Vue之后,对生命周期,事件中心,data,props等进行了初始化
2.手动调用$mount函数对$el节点进行渲染。 渲染的过程中,Vue只认render函数,那么就会先将template转换为render函数(mountComponent函数)。
3.调用render函数生成vnode。怎么生成? 主要是利用createELement函数,先将vnode的children处理为一维数组,然后通过判断tag来生成vnode。
4.生成vnode之后,就是通过patch函数转换为dom节点,渲染在视图上。vnode转换为dom节点 vnode是如何转换为dom节点的: 在vnode中,有个elm用来保存对应的dom节点,因此不用再额外生成dom(除了文本节点以外. 只是需要不深度遍历children,将children中的dom节点添加到父节点中,即完成dom树的构建