Vue.js: 运行机制解密
2023-11-21 00:19:42
Vue.js 是一款流行的前端框架,在构建用户界面和单页面应用时受到广泛应用。Vue.js 的运行机制一直备受开发者关注,本文将深入剖析 Vue.js 的运行机制,从初始化到响应式系统、组件树、更新机制,逐层解析其工作原理,帮助你深入理解 Vue.js 的运作方式。
初始化及挂载
在 new Vue()
之后,Vue 会调用 _init
函数进行初始化,也就是这里的 init
方法。
constructor() {
this._init(options)
}
在 _init
函数中,Vue 会完成以下工作:
- 初始化属性
- 初始化数据
- 初始化 computed 和 watch
- 初始化 methods
- 初始化 lifecycle hooks
- 调用
$mount
方法挂载实例
_init(options) {
const vm = this
// a) 初始化 options
vm.$options = options
// b) 初始化 data
callHook(vm, 'beforeCreate')
observe(vm, '_data', vm._data)
callHook(vm, 'created')
// c) 初始化 computed 和 watch
if (vm.$options.computed) {
initComputed(vm)
}
if (vm.$options.watch) {
for (const key in vm.$options.watch) {
const handler = vm.$options.watch[key]
if (typeof handler === 'string') {
handler = vm[handler]
}
watch(vm, key, handler)
}
}
// d) 初始化 methods
if (vm.$options.methods) {
for (const key in vm.$options.methods) {
vm[key] = vm.$options.methods[key].bind(vm)
}
}
// e) 初始化 lifecycle hooks
if (vm.$options.LIFECYCLE_HOOKS) {
for (const key in vm.$options.LIFECYCLE_HOOKS) {
vm.$options.LIFECYCLE_HOOKS[key].forEach(hook => {
vm.$on('hook:' + key, hook)
})
}
}
// f) 调用 $mount 方法挂载实例
callHook(vm, 'beforeMount')
if (vm.$mount) {
vm._isAttached = true
mountComponent(vm, vm.$el)
} else if (vm._renderProxy) {
vm._renderProxy._update(vm._vnode, {})
} else if (vm.$options.template) {
mountTemplate(vm, vm.$options.template)
}
callHook(vm, 'mounted')
// vm._watcherList.forEach(w => w.teardown())
}
响应式系统
Vue.js 的响应式系统是其核心之一。它允许你声明式地定义数据依赖关系,当数据发生变化时,系统会自动更新相关视图。
Vue.js 的响应式系统基于数据劫持。当 Vue 实例被创建时,Vue 会遍历实例的数据对象,使用 Object.defineProperty() 将每个属性转换为 getter/setter。当属性被访问或修改时,getter/setter 会触发相应的更新。
observe(value, key, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
const ob = observe(value, '__ob__', asRootData)
if (isArray(value)) {
for (let i = 0, l = value.length; i < l; i++) {
observe(value[i], key + '[' + i + ']', asRootData)
}
} else {
for (const key in value) {
observe(value[key], key, asRootData)
}
}
return ob
}
组件树
Vue.js 采用组件化的设计,每个组件都是一个独立的可复用单元。组件树是 Vue 实例中组件的层次结构表示。组件树的根节点是 Vue 实例本身,叶子节点是组件实例。
组件树是 Vue.js 更新机制的核心。当数据发生变化时,Vue 会从组件树的根节点开始,逐层向下遍历每个组件,检查每个组件的数据是否发生了变化。如果组件的数据发生了变化,Vue 会更新该组件及其所有子组件的视图。
mountComponent(vm, el) {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
}
callHook(vm, 'beforeMount')
let updateComponent
if (process.env.NODE_ENV !== 'production') {
updateComponent = () => {
callHook(vm, 'beforeUpdate')
const prevVnode = vm._vnode
const prevActiveInstance = vm._activeInstance
const cloned = createComponentInstanceForVnode(
vm,
vm._vnode,
vm._vnode
)
vm._update(cloned._vnode, prevVnode)
callHook(vm, 'updated')
}
} else {
updateComponent = () => {
vm._update(vm._vnode, vm._vnode)
}
}
vm._update(vm._render(), updateComponent)
callHook(vm, 'mounted')
if (vm.$el.dataset.ົMounting === 'true') {
vm.$el.removeAttribute('data-mounting')
}
}
更新机制
Vue.js 的更新机制是基于虚拟 DOM 的。虚拟 DOM 是一个轻量级的 DOM 表示,它只包含 DOM 节点的必要信息。当数据发生变化时,Vue 会先更新虚拟 DOM,然后再将虚拟 DOM 更新应用到实际的 DOM 上。
Vue.js 的更新机制非常高效,因为它只更新那些发生变化的组件及其子组件。这使得 Vue.js 非常适合构建大型和复杂的单页面应用。
updateComponent(prevVnode, nextVnode) {
let componentInstance = vm._component
if (!componentInstance) {
// cached component
// install component instance
componentInstance = vm._component = createComponentInstanceForVnode(
vm,
nextVnode,
prevVnode
)
} else {
componentInstance.update(nextVnode)
}
if (process.env.NODE_ENV !== 'production') {
prevVnode.componentInstance = componentInstance
}
}
总结
Vue.js 的运行机制非常复杂,但其核心原理并不难理解。通过初始化、响应式系统、组件树和更新机制,Vue.js 能够高效地构建和更新用户界面。