最近在学习Vue的源码,看了一些大神的文章,感觉还是蛮吃力的,在这里自己学习记录一下,首先说下感受,一定不要硬着头皮从源码入口去一行一行的读,不要像个无头苍蝇一样乱看,那样会非常吃力而且记忆不深, 一定要带着问题去看源码。最好是通过常见的面试题去看源码。
常见面试题之 new Vue() 初始化做了什么?
通过 new 关键字我们得知 Vue 是个构造函数,new 初始化了一个Vue的实例
Vue构造函数源码位置: vue/src/core/instance/index.js
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
// new 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')
}
// 执行 _init 方法
this._init(options)
}
// 在Vue上初始化mixin
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
通过上述代码可以看到在Vue构造函数内部调用了_init方法
_init方法源码位置: vue/src/core/instance/init.js
// 这里的混入方法入参 Vue
export function initMixin (Vue: Class<Component>) {
// 添加_init原型方法
Vue.prototype._init = function (options?: Object) {
// 修改this指向
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 上
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)
// 执行beforeCreate生命周期函数
callHook(vm, 'beforeCreate')
// 初始化inject 注意是在初始化state之前
initInjections(vm) // resolve injections before data/props
// 初始化 data/props
initState(vm)
// 之后初始化 provide 注意是在初始化state之后
initProvide(vm) // resolve provide after data/props
// 执行created生命周期函数
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)
}
// 挂载dom
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
综上所述,得出以下结论
- new Vue(options) 执行构造函数(执行 _init 方法)
- _init方法内部修改this指向当前实例vm
- 合并配置项(判断_isComponent是不是true,是的话优化内部组件实例,不是的话合并配置参数,最后都是挂载到 vm.$options 上面)
- 初始化vm代理(initProxy方法判断当前是否支持Proxy,支持的话意思就是_renderProxy 属性搞了个代理 vm._renderProxy = new Proxy(vm, handlers), 不支持的话就是 vm._renderProxy = vm)
- 初始化生命周期函数、初始化事件相关、初始化渲染相关等
- 执行beforeCreate生命周期函数
- 在初始化 state/props 之前初始化注入 inject
- 响应式处理 data / props / methods / computed / watch 等
- 在初始化 state/props 之后初始化注入 provide
- 执行created生命周期函数
- 挂载dom
总结
通过阅读源码,了解到Vue初始化的大概操作流程,后面会逐渐详细和深入,循序渐进的理解和学习更深层的源码知识。如果你也想了解源码,可以关注一下。