new Vue后发生了什么?
以下为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)
}
?可见在new Vue后,构造函数内部调用了_init方法,其中_init方法源码如下:
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)
}
}
大概讲讲,部分忽略?
1.添加uid
vm._uid = uid++
给当前实例添加一个唯一的uid标识。
2.合并options
// 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
)
}
首先通过_isComponent判断该组件是否为子组件
为子组件:调用initInternalComponent函数,其主要做了两件事情:1.指定组件$options原型,2.把组件依赖于父组件的props、listeners也挂载到options上,方便子组件调用。
为根组件:对?options ?进行合并,vue ?会将相关的属性和方法都统一放到?vm.$options ?中。vm.$options ?的属性来自两个方面,一个是?Vue ?的构造函数?vm.constructor ?预先定义的,一个是?new Vue ?时传入的入参对象。
3.initProxy
在非生产环境下执行了?initProxy ?函数,参数是实例;在生产环境下设置了实例的?_renderProxy ?属性为实例自身。
将实例的?_self ?属性设置为实例自身。
4.initLifecycle
初始化组件实例关系属性 , 比如?$parent 、$children 、$root 、$refs ?等 (不是组件生命周期?mounted ?,?created …)。
5.initEvents
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)
}
}
这个函数的功能就是初始化了一个存放事件的空对象,只存放挂载在该组件上的事件。_hasHookEvent属性是表示父组件是否有将钩子函数绑定到该组件上。如果父组件有绑定事件到该组件上则调用updateComponentListeners方法,下面看一下该方法的实现
function updateComponentListeners (
vm: Component,
listeners: Object,
oldListeners: ?Object
) {
target = vm
updateListeners(listeners, oldListeners || {}, add, remove, vm)
target = undefined
}
其中add和remove是Vue中自己实现的两个添加Listener、移除Listener的方法。?
6.initRender
初始化插槽 , 获取?this.slots ?, 定义?this._c 和this.$createElement 方法。其中 _c 用于从模板<template>编译得到的组件中使用,通常是vue内部使用生成vnode,而$createElement是放给用户用作render渲染写法。
// a: 标签
// b: 标签属性
// c: children
// d: normalizationType(是否需要额外兜底处理)
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);
7.callHook(vm, 'beforeCreate')
执行?beforeCreate ?生命周期函数。
8.initInjections
初始化?inject ?选项。
9.initState
响应式原理的核心 , 处理?props 、methods 、computed 、data 、watch ?等。
10.initProvide
解析组件配置项上的?provide ?对象,将其挂载到?vm._provided ?属性上。
11.callHook(vm, 'created')
执行 create?生命周期函数。
最后判断vm.$options.el属性是否存在
存在:调用?vm.$mount ?方法挂载?vm ?,挂载的目标就是把模板渲染成最终的?DOM
不存在 :new Vue时手动挂载。
|