![6cd9f2601cb25255f70f8557c3fd8313.png](https://i-blog.csdnimg.cn/blog_migrate/7e8bfc55db337b1adf96bb5fd0790b67.jpeg)
书接上文,上面分析了引入Vue的时候都做了哪些事,一般项目里引入Vue之后可能还要添加一些公共组件、全局方法、插件等,主要是使用Vue.component、Vue.extend、Vue.use等方法,这些会在以后给出分析,然后就是new Vue()了,(注:其中Vue.extend也是需要new一下的,会在源码里有表现),主要分为4步
这部分的逻辑是从this._init(options)进入的,_init是Vue原型对象上的方法
入口文件是src/core/instance/init.js
3.1 设置_uid以及_isVue
import config from '../config'
import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { mark, measure } from '../util/perf'
import { initLifecycle, callHook } from './lifecycle'
import { initProvide, initInjections } from './inject'
import { extend, mergeOptions, formatComponentName } from '../util/index'
let uid = 0
// 定义initMixin函数
export function initMixin (Vue: Class<Component>) {
// 为Vue的原型对象添加_init方法
Vue.prototype._init = function (options?: Object) {
// 这里的this是指向的实例对象,而不是原型对象
const vm: Component = this
// a uid
// 实例对象添加_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
// 记录这是Vue实例化的
vm._isVue = true
3.2 合并options
options的来源主要由两部分,一是new Vue(options)时传入的options,另一个是Vue的构造函数预先定义准确的说是vm.constructor实例的构造函数内的options(区别在于vm.constructor.options是可以被Vue.extend()改变的)
// 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.
// 翻译上面的英文:优化内部组件实例化,因为动态选项合并非常慢,而且没有一个内部组件选项需要特殊处理
// 应该就是说内部组件是用另一种优化的方法来解析options
initInternalComponent(vm, options)
} else {
// 把下面的参数1的值和(参数2的值格式规整后)合在一起,给vm
vm.$options = mergeOptions(
// 解析构造函数的options
// 你说实例对象没有constructor?但是架不住他爹__proto__有啊,这就是原型链的用法
resolveConstructorOptions(vm.constructor),
// 传入的options
options || {},
vm
)
}
function initInternalComponent (vm: Component, options: InternalComponentOptions) {
const opts = vm.$options = Object.create(vm.constructor.options)
// doing this because it's faster than dynamic enumeration.
opts.parent = options.parent
opts.propsData = options.propsData
opts._parentVnode = options._parentVnode
opts._parentListeners = options._parentListeners
opts._renderChildren = options._renderChildren
opts._componentTag = options._componentTag
opts._parentElm = options._parentElm
opts._refElm = options._refElm
if (options.render) {
opts.render = options.render
opts.staticRenderFns = options.staticRenderFns
}
}
export function resolveConstructorOptions (Ctor: Class<Component>) {
// 将Vue构造函数自身的options赋值给options
let options = Ctor.options
// 如果构造函数是通过Vue.extend继承来的
// 下面的方法跟Vue.extend有一些关系,可以先去读Vue.extend方法
if (Ctor.super) {
// 继续解析构造函数的父类
const superOptions = resolveConstructorOptions(Ctor.super)
// Ctor.superOptions是Vue.extend中子类中记录的父类的options
const cachedSuperOptions = Ctor.superOptions
// 如果父类中的options不等于子类中记录的父类的options
// 目的来判断父类中的options 有没有发生变化,例如使用了Vue.mixin方法
if (superOptions !== cachedSuperOptions) {
// super option changed,
// need to resolve new options.
// 将子类中记录的options重置
Ctor.superOptions = superOptions
// check if there are any late-modified/attached options (#4976)
// 检查options是否在后期被修改了,并将修改的选项找出来
const modifiedOptions = resolveModifiedOptions(Ctor)
// update base extend options
// 如果被修改了
if (modifiedOptions) {
// 将modifiedOptions合并到Ctor.extendOptions中
extend(Ctor.extendOptions, modifiedOptions)
}
// 将父类options与扩展的options合并在一起赋值给这个构造函数的options
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}
function resolveModifiedOptions (Ctor: Class<Component>): ?Object {
let modified
const latest = Ctor.options
const extended = Ctor.extendOptions
const sealed = Ctor.sealedOptions
for (const key in latest) {
if (latest[key] !== sealed[key]) {
if (!modified) modified = {}
modified[key] = dedupe(latest[key], extended[key], sealed[key])
}
}
return modified
}
function dedupe (latest, extended, sealed) {
// compare latest and sealed to ensure lifecycle hooks won't be duplicated
// between merges
if (Array.isArray(latest)) {
const res = []
sealed = Array.isArray(sealed) ? sealed : [sealed]
extended = Array.isArray(extended) ? extended : [extended]
for (let i = 0; i < latest.length; i++) {
// push original options and not sealed options to exclude duplicated options
if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) {
res.push(latest[i])
}
}
return res
} else {
return latest
}
}
合并主要是调用了mergeOptions()方法,入口文件是src/core/util/options.js
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
//检查组件名称的合法性
checkComponents(child)
}
//如果传入的是类型是function,则取其options
if (typeof child === 'function') {
child = child.options
}
//格式化props、inject、direcrives,主要是对开发时的多种写法进行统一
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
//处理options中的extends,都放到parent里
const extendsFrom = child.extends
if (extendsFrom) {
parent = mergeOptions(parent, extendsFrom, vm)
}
//处理options中的mixins,都放到parent里
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
const options = {}
let key
//按parent上的属性和方法名来合并parent和child里的属性和方法
for (key in parent) {
mergeField(key)
}
//再在child里摘吧摘吧,如果parent上没有这个属性和方法名,按这个属性和方法名来合并parent和child里的属性和方法
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
//这是因为每一种属性和方法都有自己的合并规则,所以经过这里分发到自己的规则里,具体规则可以参考源码
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
3.3 初始化各种属性和事件
到了这里vm的$options属性算是整合完毕了,接下来就是其他的属性和方法了,还会调用一些生命周期钩子,本篇只是罗列一个大纲,后面应该会细讲
/* istanbul ignore else */
//给vm添加_renderProxy()方法
// 生产环境就老实地让vm._renderProxy等于vm的,非生产环境用ES6的Proxy这个玩意
// vm._renderProxy,这是后期为render做准备的,作用是在render中将this指向vm._renderProxy
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
//给vm添加_self属性,指向自身
vm._self = vm
//给vm添加属性:$children、$parent、$refs、$root、_directInactive、
//_inactive、_isBeingDestroyed、_isDestroyed、_isMounted、_watcher
initLifecycle(vm)
//给vm添加属性:_events、hasHookEvent
initEvents(vm)
//给vm添加属性:$attrs、$listeners、$scopedSlots、$slots、$vnode
//给vm添加方法:$createElement、_c
//给vm添加get代理:$attrs、$listeners
//给vm添加set代理:$attrs、$listeners
initRender(vm)
//调用beforeCreate生命周期钩子内的方法
callHook(vm, 'beforeCreate')
//获取inject定义的各属性值并增加对该属性的监听
initInjections(vm) // resolve injections before data/props
//对prop,method,data,computed,watch的初始化
initState(vm)
//设置 vm._provided值,以便子组件逐级查找
initProvide(vm) // resolve provide after data/props
//调用create生命周期钩子内的方法
callHook(vm, 'created')
3.4 挂载
这个就牛逼了,主要流程是el、template->render表达式->Vnode->DOM
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}