问题:new Vue(options) 发生了什么?
1.处理组件配置项
- initInternalComponent(性能优化,减少原型链的动态查找),初始化每个子组件时做了一些性能优化,将组件配置对象上的一些深层次属性放到 vm.$options 选项中,以提高代码的执行效率。
- mergeOptions (选项合并),根组件初始化时进行了选项合并,将全局配置的选项合并到跟组建的局部配置上。
- 源码文件路径 :/src/core/instance/init.js
export function initMixin(Vue: Class<Component>) {
// 初始化Vue的过程
Vue.prototype._init = function (options?: Object) {
//vue实例
const vm: Component = this;
// 每个vue实例都有一个递增uid
vm._uid = uid++;
vm._isVue = true;
//配置组件配置项
if (options && options._isComponent) {
/*性能优化,减少原型链的动态查找,提高执行效率
*每个子组件初始化时走这里,这里做了一些性能优化
*将组件一些深层次的属性放入vm.$option中,提高代码效率
*/
initInternalComponent(vm, options);
} else {
/**
*初始化根组件时走这里,合并 Vue 的全局配置到根组件的局部配置,比如 Vue.component 注册的全局组件会合并到 根实例的 components 选项中
*至于每个子组件的选项合并则发生在两个地方:
* 1、Vue.component 方法注册的全局组件在注册时做了选项合并
* 2、{ components: { xx } } 方式注册的局部组件在执行编译器生成的 render 函数时做了选项合并,包括根组件中的 components 配置
* */
// 根组件选项合并,将全局配置合并到根组件的局部配置上
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== "production") {
// 设置代理,将 vm 实例上的属性代理到 vm.renderProxy
initProxy(vm);
} else {
vm._renderProxy = vm;
}
// expose real self
vm._self = vm;
//初始化组件实例关系属性,比如 $parent、$children、$root、$refs 等
initLifecycle(vm);
/**
* 初始化自定义事件,这里需要注意一点,<comp @click="handelClick"> 上注册的事件,监听者不是父组件,
* 而是子组件本身,也就是说事件的派发和监听这都是子组件本身,和父组件无关
*
**/
initEvents(vm);
//解析组件的插槽信息,得到vm.$slot处理渲染函数,得到vm.$ ceraterElement方法
initRender(vm);
//
callHook(vm, "beforeCreate");
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, "created");
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
};
}
2.初始化实例关系,比如 children、refs 等
- initLifecycle()
- 源码文件路径:src\core\instance\lifecycle.js
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
3.处理自定义事件
- initEvents
- 源码文件路径 :src\core\instance\events.js
export 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)
}
}
- 问题思考:父组件调用子组件时子组件调用的事件是谁在监听
<comp @click="handleClick" />
//通过vue的初始化 this.$emit('click')会被转为this.$on('click',function handleClick(){})
//这里的this指的是子组件,所以子组件事件调用以后是子组件自己在监听自己
4.调用 beforeCreate 钩子函数
- callHook(vm, "beforeCreate")
- 源码文件路径:src\core\instance\lifecycle.js
export function callHook (vm: Component, hook: string) {
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget()
const handlers = vm.$options[hook]
const info = `${hook} hook`
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
invokeWithErrorHandling(handlers[i], vm, null, vm, info)
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
popTarget()
}
5.初始化组件的 inject 配置项(inject 为一个高阶函数可在vue.js文档中查看具体的使用方法)
- initInjections
- 源码文件路径 :/src/core/instance/inject.js
/**
* 解析 inject 配置项,从祖代组件的 provide 配置中找到 key 对应的值,否则用 默认值,最后得到 result[key] = val
* inject 对象肯定是以下这个结构,因为在 合并 选项时对组件配置对象做了标准化处理
* @param {*} inject = {
* key: {
* from: provideKey,
* default: xx
* }
* }
*/
export function resolveInject (inject: any, vm: Component): ?Object {
if (inject) {
// inject is :any because flow is not smart enough to figure out cached
const result = Object.create(null)
// inject 配置项的所有的 key
const keys = hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)
// 遍历 key
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
// 跳过 __ob__ 对象
// #6574 in case the inject object is observed...
if (key === '__ob__') continue
// 拿到 provide 中对应的 key
const provideKey = inject[key].from
let source = vm
// 遍历所有的祖代组件,直到 根组件,找到 provide 中对应 key 的值,最后得到 result[key] = provide[provideKey]
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent
}
// 如果上一个循环未找到,则采用 inject[key].default,如果没有设置 default 值,则抛出错误
if (!source) {
if ('default' in inject[key]) {
const provideDefault = inject[key].default
result[key] = typeof provideDefault === 'function'
? provideDefault.call(vm)
: provideDefault
} else if (process.env.NODE_ENV !== 'production') {
warn(`Injection "${key}" not found`, vm)
}
}
}
return result
}
}
6. 数据响应式的处理 props、methods、data、computed、watch
- initState
- 源码文件路径 :src\core\instance\state.js
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
7.解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上
- initProvide()
- 源码文件路径 :\src\core\instance\inject.js
- 注:inject()和 provide()是组合使用的高阶方法 provide负责提供inject则负责子组件注入
export function initProvide (vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
8.调用 created 钩子函数
- callHook(vm, 'created')
- 源码文件路径:src\core\instance\lifecycle.js
9.如果发现配置项上有 el 选项,则自动调用 mount 方法,反之,没提供 el 选项则必须调用 $mount
- 源码文件路径:src\core\instance\init.js
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
10.接下来进入挂载阶段