介绍:主要记录自己学习源码的整个历程;源码学习较晚,所以本次主要针对2.xx版本,如有理解不到位的地方,欢迎讨论(随着知识的增加,会不断修改、完善当前文章)
1.要想学习源码,个人觉得要知道vue的整体流程是什么,宏观的了解每一步是做什么的,然后再了解每一步是怎么实现的。
Vue主干流程
1.new Vue (合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher)
当 new Vue(options) 时调用的是 src/core/instance/index.js 文件中的Vue函数,源码如下:
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')
}
this._init(options) //init方法在哪???-----竟然是initMixin初始化的时候,声明了_init
}
initMixin(Vue) //分支流程吗?
stateMixin(Vue) //分支流程吗?
eventsMixin(Vue) //分支流程吗?
lifecycleMixin(Vue) //分支流程吗?
renderMixin(Vue) //分支流程吗?
export default Vue
可以看到在初始化后又调用了 this._init(options) 方法,该方法其实在 src/core/instance/init.js 中,源码如下:
export function initMixin (Vue: Class<Component>) { //----------竟然是initMixin
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 //看名字,根据vue官方文档猜方法用途
initLifecycle(vm) //看名字,感觉是:初始化生命周期
initEvents(vm) //看名字,感觉是:初始化事件
initRender(vm) //看名字,感觉是:初始化渲染
callHook(vm, 'beforeCreate') //看名字,跟生命周期有关
//以下是个人找的相关资料,加的注解
//provide/inject,这个是Vue在2.2.0版本新增的一个属性,按照Vue官网的说法,它的作用是:
//这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,
//并在起上下游关系成立的时间里始终生效。
//---------------------------这解释,真是。。。,还好有代码---------------------------------
// 父级组件提供 'foo'
// var Provider = {
// provide: {
// foo: 'bar'
// },
// ...
// }
// 子组件注入 'foo'
// var Child = {
// inject: ['foo'],
// created () {
// console.log(this.foo) // => "bar"
// }
// ...
// }
//看到这里,大概知道provide/inject是干什么的了
initInjections(vm) // resolve injections before data/props
initState(vm) //看到上下给的英文注解,明白这里是,初始化data/props
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created') //看名字,跟生命周期有关-----beforeCreate与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) //看着名字,应该是挂载、渲染了
}
}
}
上面源码大体分为了三个大步骤:
options为 new Vue(options) 时传入的选项,并通过mergeOptions方法把 options 合并到vm.$options中。- 调用
initLifecycle(vm)、initEvents(vm)、initRender(vm)、initState(vm)函数进行生命周期的初始化、事件中心的初始化、渲染的初始化、data props computed watcher的初始化等等。 - 调用
vm.$mount(vm.$options.el)进行挂载渲染成真实dom。
总结: 首先 new Vue(opt);经历了什么?
this._init(options) //执行了它
_init(options) // 内部包含了:调用 initLifecycle(vm)、initEvents(vm)、
//initRender(vm)、initState(vm) 函数进行生命周期的初始化、事件中心的初始化
//、渲染的初始化、data props computed watcher的初始化等等。
让我们欣赏一下官网给我们的图片:
疑问:mounted这些生命周期去哪了?从刚刚的源码中并未体现