Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Core Architecture and Minimal Vue 3 Implementation

Tech 2

Frontend Framework Architecture & Vue 3 Motivattions

Modern frontend frameworks like Vue emphasize simplicity and a data-driven approach. By reducing direct DOM manipulation, frameworks leverage reactivity systems, declarative rendering, and virtual DOM diffing to efficiently synchronize the UI with the application state. Vue's progressive nature allows developers to adopt it incrementally, integrating state management, routing, and UI libraries as needed.

The shift to Vue 3 introduced several key architectural changes:

  1. Functional API over Classes: Unlike Angular or Vue 2 (which relied on class decorators), Vue 3 and React favor functions. Function signatures provide explicit inputs and outputs, drastically improving TypeScript type inference and overall code predictability.
  2. Composition API: This paradigm eliminates the ambiguous this context found in Options API. By explicitly declaring reactive variables and composing logic within setup(), code becomes more reusable, readable, and maintainable.
  3. Improved Tree-shaking: By exposing instance methods and properties as stanadlone functions (e.g., import { ref } from 'vue'), bundlers can eliminate unused code more effectively.
  4. API Simplification: Vue 3 unified and streamlined APIs. For instance, v-model and .sync were merged into a single v-model syntax. The render function arguments were also flattened.
  5. Extensibility: Custom renderers (createRenderer) allow Vue's core to operate independently of the browser DOM, enabling rendering to native mobile platforms, terminals, or canvas.
  6. Performance via Proxy: Vue 3 replaced Object.defineProperty with Proxy for reactivity. This solves recursive efficiency issues, removes the need for special array handling, natively supports dynamic property addition/deletion and collection types (Map, Set), and avoids the legacy API workarounds like Vue.set.

Constructing a Minimal Vue 3

1. Application Initialization & Mounting

The core of Vue initialization involves creating an application instance, locating the host element, processing the template or render function, and appending the resulting DOM.

javascript const VueLib = { buildApp(options) { return { attach(selector) { const hostEl = document.querySelector(selector) if (!options.render) { options.render = this.transpile(hostEl.innerHTML) } const renderedEl = options.render.call(options.data ? options.data() : {}) hostEl.innerHTML = '' hostEl.appendChild(renderedEl) }, transpile(markup) { return function render() { const node = document.createElement('h3') node.textContent = this.title return node } } } } }

When handling setup alongside data, Vue 3 prioritizes setup. A proxy can manage this access layer, routing property lookups and mutations to the correct source.

javascript const contextProxy = new Proxy(instance, { get(target, key) { if (target.setupState && key in target.setupState) { return Reflect.get(target.setupState, key) } return Reflect.get(target.dataState, key) }, set(target, key, value) { if (target.setupState && key in target.setupState) { return Reflect.set(target.setupState, key, value) } return Reflect.set(target.dataState, key, value) } })

To decouple the framework from the browser, platform-specific operations should be abstracted into a custom renderer factory.

javascript const VueLib = { initRenderer({ getElement, appendNode }) { return { buildApp(options) { return { attach(selector) { const hostEl = getElement(selector) // ... render logic ... appendNode(vnode, hostEl) } } } } }, buildApp(options) { const webRenderer = VueLib.initRenderer({ getElement(sel) { return document.querySelector(sel) }, appendNode(el, parent) { parent.appendChild(el) } }) return webRenderer.buildApp(options) } }

2. Reactivity System

Vue 3 employs Proxy to intercept object access and mutations. To decouple state changes from the UI update mechanism, a dependency tracking system is required.

javascript const dependencyGraph = new WeakMap() let activeEffects = []

function watchEffect(fn) { const execute = () => { activeEffects.push(execute) try { fn() } finally { activeEffects.pop() } } execute() }

function recordDep(target, key) { const currentEffect = activeEffects[activeEffects.length - 1] if (!currentEffect) return let depsMap = dependencyGraph.get(target) if (!depsMap) { depsMap = new Map() dependencyGraph.set(target, depsMap) } let depSet = depsMap.get(key) if (!depSet) { depSet = new Set() depsMap.set(key, depSet) } depSet.add(currentEffect) }

function notifyDeps(target, key) { const depsMap = dependencyGraph.get(target) if (!depsMap) return const depSet = depsMap.get(key) if (depSet) { depSet.forEach(effect => effect()) } }

function makeReactive(obj) { return new Proxy(obj, { get(target, key) { recordDep(target, key) return Reflect.get(target, key) }, set(target, key, value) { const result = Reflect.set(target, key, value) notifyDeps(target, key) return result } }) }

The application's update function is wrapped in watchEffect, ensuring DOM updates occur automatically when reactive state mutates.

javascript this.update = watchEffect(() => { const elemant = options.render.call(contextProxy) appendNode(element, hostEl) })

3. Virtual DOM & Rendering

To avoid expensive full DOM replacements during updates, a Virtual DOM represents the UI as JavaScript objects.

javascript function createVNode(tag, attrs, children) { return { tag, attrs, children } }

The compile step generates createVNode calls instead of real DOM elements. A mounting function recursively constructs the real DOM tree from these virtual nodes.

javascript function constructElement(vnode) { const el = document.createElement(vnode.tag) if (typeof vnode.children === 'string') { el.textContent = vnode.children } else if (Array.isArray(vnode.children)) { vnode.children.forEach(child => { el.appendChild(constructElement(child)) }) } vnode.el = el // Store real DOM reference for diffing return el }

4. Diffing & Patching

During an update, the new vnode tree is compared against the old one. If the component is already mounted, a patching process takes over.

javascript function syncPatch(prevVNode, nextVNode) { const el = nextVNode.el = prevVNode.el

if (prevVNode.tag === nextVNode.tag) { const oldChildren = prevVNode.children const newChildren = nextVNode.children

if (typeof oldChildren === 'string') {
  if (typeof newChildren === 'string') {
    if (oldChildren !== newChildren) el.textContent = newChildren
  } else {
    el.textContent = ''
    newChildren.forEach(c => el.appendChild(constructElement(c)))
  }
} else {
  if (typeof newChildren === 'string') {
    el.textContent = newChildren
  } else {
    reconcileChildren(el, oldChildren, newChildren)
  }
}

} else { // Node replacement logic } }

A simplified child reconciliation algorithm compares nodes by index, updates matching pairs, and handles leftover additions or deletions.

javascript function reconcileChildren(parentEl, oldChildren, newChildren) { const commonLength = Math.min(oldChildren.length, newChildren.length)

for (let i = 0; i < commonLength; i++) { syncPatch(oldChildren[i], newChildren[i]) }

if (newChildren.length > oldChildren.length) { newChildren.slice(commonLength).forEach(c => { parentEl.appendChild(constructElement(c)) }) } else if (newChildren.length < oldChildren.length) { oldChildren.slice(commonLength).forEach(c => { parentEl.removeChild(c.el) }) } }

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.