wxuande 2019-11-04
2.5.21
Vue的生命周期
package.jsonscrpit内,npm run dev的命令:
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"
其中rollup是一个打包工具,类似webpack。
rollup: Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。
scripts/config.jsscripts/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
src\platforms\web\entry-runtime-with-compiler.jsvue来自于./runtime/index
import Vue from './runtime/index'
src\platforms\web\runtime\index.jsVue来自于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)
}src\core\index.jsVue来自于./instance/index
import Vue from './instance/index'
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)
我们一一分析。
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)
}
}
}src\core\util\options.jsmergeOptions的方法做了什么?顾名思义,将options挂到Vue对象上。看下面的图:
merge前vm.$options是空的;merge后,$options已经有值了。
执行完mergeOptions后,我们继续往下看。
src\core\instance\lifecycle.jsinitLifecycle顾名思义,肯定与Vue的生命周期有关!真的是吗?同样看图:
发现,只是多了一些参数,还没有到我们熟悉的created,mounted。但是这里是为生命周期做准备,做了一下初始化工作。
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)
}
}src\core\instance\render.jsinitRender方法初始化了渲染的参数和方法(此时还没有渲染)。如下图:
callHook(vm, 'beforeCreate')终于到了第一个生命周期beforeCreate!
接下来肯定是create了吧。
src\core\instance\inject.jsinitInjections与provide/inject有关,与生命周期无关,这里暂不介绍。《Vue官方文档:provide/inject》
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等)的值注入:
src\core\instance\inject.jsinitProvide与provide/inject有关,与生命周期无关,这里暂不介绍。《Vue官方文档:provide/inject》
initProvide
callHook(vm, 'created')第二个生命周期created
vm.$mount(vm.$options.el)回看第4步中,给Vue的原型链上挂上了$mount方法:
import { mountComponent } from 'core/instance/lifecycle'
// public mount method
Vue.prototype.$mount = function (
...
}src\core\instance\lifecycle.jsexport 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: 这块代码具体会另起文章详细介绍。
本篇文章参考自
background-color: blue;background-color: yellow;<input type="button" value="变蓝" @click="changeColorT