在 /src/core/instance/lifecycle.js 的lifecycleMixin函数中往Vue的原型上挂载了$forceUpdate和$destroy方法。
vm.$forceUpdate
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
//Watcher类中有这样处理:
if (isRenderWatcher) {
vm._watcher = this
}
vm._watcher存放了当前实例的渲染watcher。调用渲染watcher的update方法,迫使组件重新渲染。它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
vm.$destory
Vue.prototype.$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
return
}
callHook(vm, 'beforeDestroy')
vm._isBeingDestroyed = true
const parent = vm.$parent
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm)
}
if (vm._watcher) {
vm._watcher.teardown()
}
let i = vm._watchers.length
while (i--) {
vm._watchers[i].teardown()
}
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
vm._isDestroyed = true
vm.__patch__(vm._vnode, null)
callHook(vm, 'destroyed')
vm.$off()
if (vm.$el) {
vm.$el.__vue__ = null
}
if (vm.$vnode) {
vm.$vnode.parent = null
}
}
为了防止$destroy被反复执行,所以当vm._isBeingDestroyed为true时表示实例已经开始销毁,直接return。
先调用beforeDestroy生命周期函数,将_isBeingDestroyed置为true,表示开始销毁实例。如果父组件没有被销毁,那么就将自己从父组件的children列表中移除,解除与父组件的关系。将当前实例的_watcher和_watchers中的watcher都通过teardown()移除监听。
_isDestroyed置为true表示实例已经销毁,调用 __patch__,销毁节点。调用destroyed生命周期函数。调用$off移除所有事件。
vm.$nextTick
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
nextTick方法请看第Vue2.x源码学习笔记(七)
vm.$mount
function query (el: string | Element): Element {
if (typeof el === 'string') {
const selected = document.querySelector(el)
if (!selected) {
process.env.NODE_ENV !== 'production' && warn(
'Cannot find element: ' + el
)
return document.createElement('div')
}
return selected
} else {
return el
}
}
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
第一个参数el,它表示挂载的元素,可以是字符串,也可以是DOM对象,如果是字符串在浏览器环境下会调用query方法转换成DOM对象的。query方法中调用原生的document.querySelector(el)去获取DOM元素。如果没获取到则会创建一个div作为元素返回。
第二个参数是和服务端渲染相关,在浏览器环境下我们不需要传第二个参数。
最终会去调用mountComponent进行挂载。
Runtime+Compiler版本的$mount
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
if (el === document.body || el === document.documentElement) {
//warn
)
return this
}
const options = this.$options
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
if (process.env.NODE_ENV !== 'production' && !template) {
//warn
}
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
template = getOuterHTML(el)
}
if (template) {
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
}
return mount.call(this, el, hydrating)
}
如果是Runtime+Compiler版本时会重写原来的$mount方法。el不能是body和html元素,如果options中没有render函数则会进行模板编译的过程。
如果options中提供的template是‘#id’,则会通过idToTemplate转为元素字符串。如果template是一个元素,则会通过innerHTML去获取元素字符串。如果没有template则会通过el去获取元素字符串。
然后通过compileToFunctions对元素字符串进行编译,编译生成render函数添加到options中。然后调用原来的$mount方法。最终去调用mountComponent方法。
mountComponent
function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
//warn
}
}
callHook(vm, 'beforeMount')
let updateComponent
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
如果options中没有render函数则会创建一个空的注释vnode节点并且抛出警告。调用beforeMount生命周期函数。然后定义一个updateComponent函数,updateComponent函数中会执行vm._update方法,将通过vm._render()生成的虚拟dom渲染成真实dom。
然后将updateComponent函数在new Watcher时作为参数传入。这个watcher是一个渲染watcher,会存放在实例的_watcher上。每当依赖项发生更新时就会派发更新然后调用updateComponent函数更新时图,期间会调用beforeUpdate生命周期函数,当挂载完毕后会调用mounted生命周期函数。