上篇博文中说到Vue源码的目录结构是什么样的,每一个目录的做用应该也有所了解。咱们知道core/instance目录主要是用来实例化Vue对象,因此咱们在这个目录下去寻找Vue构造函数。果真找到了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)
}
复制代码
当你新建一个Vue实例时候,会判断若是当前的环境不是生产环境,且你在调用Vue的时候,没有用new操做符。就会调用warn函数,抛出一个警告。告诉你Vue是一个构造函数,须要用new操做符去调用。这个warn函数不是单纯的console.warn,它的实现咱们后面的博文会介绍。segmentfault
接下来,把options做为参数调用_init方法。options不作过多的介绍了,就是你调用new Vue时候传入的参数。在深刻_init方法以前,咱们先把目光移到index.js文件里缓存
function Vue (options) {
...
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
复制代码
在Vue的构造函数定义以后,有一系列方法会被调用,这些方法主要用来给Vue函数添加一些原型属性和方法的。其中就有接下来要介绍的Vue.prototyoe._initide
在core/instance/init.js中咱们找到了_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
// 有子组件时,options._isComponent才会为true
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')
initState(vm)
initProvide(vm)
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)
}
}
复制代码
咱们逐一来分析上述代码。首先缓存当前的上下文到vm变量中,方便以后调用。而后设置_uid属性。_uid属性是惟一的。当触发init方法,新建Vue实例时(当渲染组件时也会触发)uid都会递增。性能
下面这段代码主要是用来测试代码性能的,在这个时候至关于打了一个"标记点"来测试性能。测试
let startTag, endTag
/* istanbul ignore if */
process.env.NODE_ENV === 'develop'
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
复制代码
对这部份内容感兴趣的朋友们能够点击个人另外一篇文章Performance API查看。接下来执行这行代码vm._isVue = true
,Vue的做者对这句话作了注释。ui
an flag to avoid this being observedthis
乍看起来好像不太明白,好像是说为了防止this被observed实例化。那这到底是什么意思呢?咱们来看observer的代码。spa
export function observe (value: any, asRootData: ?boolean): Observer | void {
...
else if (
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
...
}
复制代码
若是传入值的_isVue为ture时(即传入的值是Vue实例自己)不会新建observer实例(这里能够暂时理解新建observer实例就是让数据响应式)。
再回到init源码部分
if (options && options._isComponent) {
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
复制代码
mergeOptions主要调用两个方法,resolveConstructorOptions和mergeOptions。这两个方法牵涉到了不少知识点,为了咱们文章篇幅的考虑。接下来准备经过两篇博文来介绍这两个方法。
下篇博文主要介绍resolveConstructorOptions相关的内容,涉及到原型链和构造函数以及部分Vue.extend的实现,敬请期待!