背景
vue版本
2.5.21
本篇内容
Vue的生命周期
源码开始
1. package.json
scrpit内,npm run dev的命令:
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"
-
其中rollup是一个打包工具,类似webpack。
rollup: Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。
- 打包的代码在
scripts/config.js
2. scripts/config.js
根据TARGET:web-full-dev
找到如下代码:
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
}
找到入口文件在web/entry-runtime-with-compiler.js
3. src\platforms\web\entry-runtime-with-compiler.js
vue来自于./runtime/index
import Vue from './runtime/index'
4. src\platforms\web\runtime\index.js
Vue来自于core/index
import Vue from 'core/index'
...
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
5. src\core\index.js
Vue来自于./instance/index
import Vue from './instance/index'
6. src\core\instance\index.js
一直跟到这里,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后,接下来的代码:
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
我们一一分析。
6.1 initMixin src\core\instance\init.js
这里代码的作用是给Vue的原型链上定义_init方法。而这个_init方法在Vue对象创建时被调用(回看6里的代码this._init(options)
)。
接下来我们分析_init方法里做了什么,就明白了Vue对象创建时,到底经历了什么。
刚开始是一些参数的初始化,直到merge options。
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
...
// 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
)
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
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)
}
}
}
6.1.1 src\core\util\options.js
mergeOptions的方法做了什么?顾名思义,将options挂到Vue对象上。看下面的图:
merge前vm.$options是空的;merge后,$options已经有值了。
执行完mergeOptions后,我们继续往下看。
6.1.2 initLifecycle src\core\instance\lifecycle.js
initLifecycle顾名思义,肯定与Vue的生命周期有关!真的是吗?同样看图:
发现,只是多了一些参数,还没有到我们熟悉的created,mounted。但是这里是为生命周期做准备,做了一下初始化工作。
6.1.3 initEvents src\core\instance\events.js
这个方法的代码不多,主要是调用了updateComponentListeners
方法。这个方法的作用是更新组件的侦听事件,与生命周期无关,暂不分析。
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)
}
}
6.1.4 initRender src\core\instance\render.js
initRender
方法初始化了渲染的参数和方法(此时还没有渲染)。如下图:
6.1.5 callHook(vm, 'beforeCreate')
终于到了第一个生命周期beforeCreate
!
接下来肯定是create了吧。
6.1.6 initInjections src\core\instance\inject.js
initInjections与provide/inject有关,与生命周期无关,这里暂不介绍。《Vue官方文档:provide/inject》
6.1.7 initState src\core\instance\state.js
代码不多,但是一看就知道是初始化Props,Methods,data,computed,watch的。
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)
}
}
如图,将data(demo中没有methods等)的值注入:
6.1.8 initProvide src\core\instance\inject.js
initProvide与provide/inject有关,与生命周期无关,这里暂不介绍。《Vue官方文档:provide/inject》
initProvide
6.1.9 callHook(vm, 'created')
第二个生命周期created
6.1.10 vm.$mount(vm.$options.el)
回看第4步中,给Vue的原型链上挂上了$mount方法:
import { mountComponent } from 'core/instance/lifecycle'
// public mount method
Vue.prototype.$mount = function (
...
}
6.1.10.1 mountComponent src\core\instance\lifecycle.js
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
...
callHook(vm, 'beforeMount')
...
// updateComponent
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
...
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
这段代码里,有关生命周期的就很多了。一共三个:beforeMount
,beforeUpdate
,mounted
。
真正mount的方法在updateComponent
:
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
那么updateComponent
何时调用?updateComponent
在new Watcher
时传进去,作为getter
方法,在每次获取vm时执行,其中执行了vm._update(vm._render(), hydrating)
,用以mount渲染。
TODO: 这块代码具体会另起文章详细介绍。
原创说明
-
本篇文章参考自
- 根据参考文章,本人结合Vue@2.5.21原创本篇内容(包括文字+图片)。
- 转载请注明出处,谢谢。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。