Vue 3’s source architecture has undergone a complete overhaul compared to Vue 2, adopting more modern programming paradigms and underlying implementations. This section provides an in-depth analysis of Vue 3’s core source architecture, covering the reactive system, virtual DOM, and component system, revealing its design philosophy and implementation principles.
Vue Reactive System Source Code
Dependency Collection and Tracking
Vue 3’s dependency collection system is based on the Effect mechanism, offering a simpler and more efficient approach compared to Vue 2’s Dep/Watcher system:
// Simplified effect implementation
let activeEffect = null
function effect(fn) {
activeEffect = fn
fn() // Execute function to trigger getter
activeEffect = null
}
function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => effect())
}
}
Dependency relationship flowchart:
Component render function → effect captures → track collects dependencies → setter triggers → trigger executes updates
Data Hijacking Implementation
Vue 3 uses Proxy instead of Vue 2’s Object.defineProperty, achieving more comprehensive reactive interception:
// Simplified reactive implementation
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key) // Dependency collection
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, key) // Trigger update
return result
},
// Other trap methods...
})
}
Proxy vs. defineProperty Comparison:
| Feature | Proxy | defineProperty |
|---|---|---|
| Interception Capability | Supports all operations | Only get/set |
| Array Handling | Native support | Requires array method overrides |
| Performance | More efficient | Lower |
| Compatibility | ES6+ | ES5+ |
Array Reactive Handling
Vue 3 directly intercepts array operations via Proxy, eliminating the need to override array methods as in Vue 2:
const arr = reactive([1, 2, 3])
arr.push(4) // Automatically triggers reactive update
Underlying implementation principle:
- Proxy intercepts array methods like push
- Executes native array operations
- Triggers setter to notify dependency updates
Computed Properties Implementation
Computed properties are implemented based on effect and caching mechanisms:
function computed(getter) {
let value
let dirty = true
const effectFn = effect(getter, {
lazy: true,
scheduler: () => {
dirty = true
trigger(obj, 'value') // Trigger dependency update
}
})
return {
get value() {
if (dirty) {
value = effectFn()
dirty = false
track(obj, 'value') // Collect dependencies
}
return value
}
}
}
Caching mechanism features:
- Recalculates only when dependencies change
- Reuses results when multiple components share the same computed property
- Automatically tracks dependency relationships
Asynchronous Update Queue
Vue 3’s nextTick implementation is based on a microtask queue:
const callbacks = []
let pending = false
function nextTick(fn) {
callbacks.push(fn)
if (!pending) {
pending = true
Promise.resolve().then(flushCallbacks)
}
}
function flushCallbacks() {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
Update process:
- Data change triggers setter
- Adds component update function to the queue
- Waits for current synchronous code to complete
- Executes updates in the microtask queue
Vue Virtual DOM Source Code
Virtual DOM Creation and Rendering
Virtual DOM creation process:
function h(type, props, children) {
return {
type,
props,
children,
key: props?.key,
el: null // Corresponding real DOM
}
}
// Render function example
render() {
return h('div', { class: 'container' }, [
h('h1', null, 'Hello Vue3'),
h('p', null, 'Virtual DOM example')
])
}
Diff Algorithm Core Logic
Vue 3’s Diff algorithm uses a two-ended comparison strategy:
function patchChildren(n1, n2, container) {
const c1 = n1.children
const c2 = n2.children
if (typeof c2 === 'string') {
// Text node handling
} else if (Array.isArray(c2)) {
// New children are an array
if (Array.isArray(c1)) {
// Old children are also an array, perform Diff
patchKeyedChildren(c1, c2, container)
} else {
// Old children are not an array, directly replace
}
}
}
function patchKeyedChildren(c1, c2, container) {
let i = 0
const l2 = c2.length
let e1 = c1.length - 1
let e2 = l2 - 1
// Compare from the start
while (i <= e1 && i <= e2) {
const n1 = c1[i]
const n2 = c2[i]
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container)
} else {
break
}
i++
}
// Compare from the end
while (i <= e1 && i <= e2) {
const n1 = c1[e1]
const n2 = c2[e2]
if (isSameVNodeType(n1, n2)) {
patch(n1, n2, container)
} else {
break
}
e1--
e2--
}
// Handle new nodes
if (i > e1 && i <= e2) {
// ...
}
// Handle deleted nodes
if (i > e2 && i <= e1) {
// ...
}
}
Patch Algorithm Implementation
Node update strategy:
function patch(n1, n2, container) {
if (n1 && !isSameVNodeType(n1, n2)) {
// Different types, directly replace
unmount(n1)
mount(n2, container)
} else if (n2.patchFlag) {
// Targeted updates based on patchFlag
switch (n2.patchFlag) {
case PatchFlags.TEXT:
// Only update text content
if (n2.el.textContent !== n2.children) {
n2.el.textContent = n2.children
}
break
case PatchFlags.CLASS:
// Only update class
patchClass(n2.el, n2.props.class)
break
// Other optimization flags...
}
} else {
// Full comparison
mount(n2, container, n1)
}
}
Static Node Hoisting
Static node hoisting optimization example:
// Before compilation
<div>
<h1>Static Title</h1>
<p>{{ dynamicContent }}</p>
</div>
// After compilation
const _hoisted_1 = /*#__PURE__*/h('h1', null, 'Static Title')
function render() {
return h('div', [
_hoisted_1,
h('p', null, dynamicContent)
])
}
Benefits of hoisting:
- Static nodes are created only once
- Skips Diff comparison process
- Reduces memory usage
Virtual DOM Performance Optimization
Key optimization strategies:
- Patch Flags: Marks dynamic nodes during compilation
- Block Tree: Skips comparison for static subtrees
- Longest Increasing Subsequence: Optimizes node movement operations
- Cached Event Handlers: Avoids repeated function creation
Vue Component System Source Code
Component Creation and Mounting Process
Core component mounting process:
function mountComponent(vnode, container) {
// 1. Create component instance
const instance = createComponentInstance(vnode)
// 2. Set up component state
setupComponent(instance)
// 3. Create render proxy
setupRenderEffect(instance, container)
}
function setupRenderEffect(instance, container) {
instance.update = effect(() => {
if (!instance.isMounted) {
// Initial render
const subTree = (instance.subTree = instance.render())
patch(null, subTree, container, instance)
instance.isMounted = true
} else {
// Update render
const nextTree = instance.render()
patch(instance.subTree, nextTree, container, instance)
instance.subTree = nextTree
}
})
}
Component Communication Implementation
Props passing mechanism:
function initProps(instance, rawProps) {
instance.props = reactive(rawProps)
}
// When parent updates props
function updateProps(instance, newProps) {
const oldProps = instance.props
instance.props = reactive(newProps)
// Trigger dependency update...
}
Event emission system:
function emit(instance, event, ...args) {
const { props } = instance
const handlerName = `on${capitalize(event)}`
const handler = props[handlerName]
if (handler) {
callWithAsyncErrorHandling(handler, instance, ErrorCodes.COMPONENT_EMIT, args)
}
}
Slot Rendering Mechanism
Slot implementation principle:
function renderSlot(slots, name, props) {
const slot = slots[name]
if (slot) {
if (typeof slot === 'function') {
return createVNode(Fragment, {}, slot(props))
} else {
return slot
}
}
return createCommentVNode('v-slot')
}
Scoped slot implementation:
// Compile-time processing
const _ctx = this
h('div', [
_ctx.$slots.default({
text: _ctx.text
})
])
// Runtime processing
function renderSlot(slots, name, props) {
const slot = slots[name]
if (typeof slot === 'function') {
return createVNode(Fragment, {}, slot(props))
}
// ...
}
Dynamic Component Implementation
Dynamic component switching logic:
function resolveDynamicComponent(component) {
if (typeof component === 'string') {
return resolveAsset(COMPONENTS, component)
} else if (component) {
return component
}
return null
}
// When switching components
function patchComponent(n1, n2, container) {
const instance = (n2.component = n1 ? n1.component : createComponentInstance(n2))
if (shouldUpdateComponent(n1, n2)) {
updateComponentPreRender(instance, n2)
} else {
instance.update()
}
}
Component Lifecycle Implementation
Lifecycle hook registration:
const componentLifecycleHooks = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeUnmount',
'unmounted'
]
function callHooks(instance, hook) {
const handlers = instance.hooks[hook]
if (handlers) {
for (let i = 0; i < handlers.length; i++) {
handlers[i].call(instance.proxy)
}
}
}
// Call at appropriate times
function mountComponent() {
callHooks(instance, 'beforeCreate')
// ...initialization logic
callHooks(instance, 'created')
// ...mounting logic
callHooks(instance, 'mounted')
}
Composition API lifecycle mapping:
| Options API | Composition API |
|---|---|
| beforeCreate | setup() |
| created | setup() |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
By deeply analyzing Vue 3’s source architecture, we gain a profound understanding of its design philosophy and implementation principles. This foundational knowledge is crucial for writing high-performance, maintainable Vue applications and provides a solid basis for contributing to the Vue ecosystem.



