大家好,我是鼠目。「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」
上一章,大概讲述了初始化过程中的执行,以及初始化时,有哪些全局api被初始化了。new Vue时,会执行Vue.prototype._init()方法,初始化了生命周期和数据后,会执行mounted操作。
那就让我们来看看$mount究竟做了哪些操作
它来自于src/platforms/web/runtime/index.js
// 1.安装补丁函数(更新函数):vdom =》dom,这个函数的主要作用是将虚拟dom转换为真实dom。
Vue.prototype.__patch__ = inBrowser ? patch : noop
// 2.实现$mount方法:调用mountComponent
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
其实,它是返回的是来自于 src/core/instance/lifecycle 的mountComponent函数
// 真正执行了挂载相关的函数
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
//省略 非主要信息
}
// 执行了beforeMount相关的方法,哈哈,又出现了生命周期beforeMount,很明显,现在的元素尚未挂载。
callHook(vm, 'beforeMount')
// 组件更新函数声明
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
// 省略不相关。
} else {
// 更新函数定义
updateComponent = () => {
// 首先执行vm._render(),不过我们并没有找到vm._render是做什么的,不过可以全局搜索可以知道,它的作用是生产虚拟dom
// 然后执行_update函数将,同样不知道_update是做什么的,在同样的文件夹可以找到,它其实是执行了__patch_——方法,生产了真实dom
vm._update(vm._render(), hydrating)
}
}
// 挂载完成,页面有了真实dom
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
// 组件mounted的时候,创建watcher实例,所以每个组件都将至少有一个watcher。与响应式原理相关。暂不具体看,跳过
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
// 此时,进行mounted相关的生命周期。
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
vm._render函数做了些什么?在 /src/instance/render.js内,主要作用是生成虚拟dom vm._update函数做了些生命?在 src/core/instance/lifecycle
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
// 上次计算的虚拟dom
const prevVnode = vm._vnode
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
// 初始化时没有prevVnode,则说明是首次挂载
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// 初始化时有prevVnode,则说明是更新函数,进行虚拟dom比较
// updates
// diff
vm.$el = vm.__patch__(prevVnode, vnode)
}
}
总结,$mount 主要是执行了mountComponent函数。 而在mountComponent函数中,
- 执行beforeMount钩子函数
- 先执行vm._render生产虚拟dom,然后执行vm._update,将虚拟dom转换为真实Dom,挂载
- 创建watcher观察者,响应式原理相关。
- 执行mounted钩子函数,并将isMounted置为true。
new Vue => 初始化数据与api,执行钩子函数beforeCreate,created => 执行$mount => beforeMount => _render,生产虚拟dom => _update, 真实dom挂载 => new Watcher => mounted钩子函数