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时手动挂载。