一、响应式系统的设计:依赖收集与发布订阅
现代前端框架(如 Vue、Solid)的核心是响应式数据。当数据变化时,视图自动更新。这一机制依赖“观察者模式”与“依赖收集”的设计。我们可以从零实现一个极简响应式系统,理解其底层逻辑。
首先,定义一个 Dep 类,作为依赖收集的容器。每个响应式属性都有一个 Dep 实例,用于存储所有依赖于该属性的 Watcher(观察者)。
class Dep {constructor() {this.subscribers = new Set();}depend() {if (Dep.target) {this.subscribers.add(Dep.target);}}notify() {this.subscribers.forEach(watcher => watcher.update());}
}
Dep.target = null; // 全局静态变量,指向当前正在计算的 Watcher
接下来,实现 Watcher 类。它负责在某个表达式或 render 函数执行时,将自身注册到所有被访问的响应式属性的 Dep 中。
class Watcher {constructor(getter) {this.getter = getter; // 获取值的函数,比如 () => state.countthis.value = this.get();}get() {Dep.target = this;const value = this.getter();Dep.target = null;return value;}update() {console.log('数据已更新,执行副作用');this.value = this.getter(); // 实际中会触发重新渲染}
}
最后,通过 reactive 函数将普通对象转换为响应式对象。使用 Object.defineProperty 或 Proxy 拦截属性的 get 和 set 操作。
function reactive(obj) {const keys = Object.keys(obj);keys.forEach(key => {let value = obj[key];const dep = new Dep();Object.defineProperty(obj, key, {get() {dep.depend();return value;},set(newVal) {if (newVal !== value) {value = newVal;dep.notify();}}});});return obj;
}
设计亮点:
- 依赖收集在 get 时进行,避免了不必要的计算。
- 通过 Dep.target 实现单例模式,确保当前 Watcher 能正确收集依赖。
- 发布订阅机制让数据变化时精准通知所有相关 Watcher。
这就是 Vue 2 响应式系统的核心设计。理解它,你就掌握了前端框架设计中最基础也最关键的“数据驱动”模型。
二、虚拟DOM的差异更新设计
除了响应式,另一个关键设计是虚拟DOM。它通过 JS 对象模拟真实 DOM,在数据变化时计算最小更新量,提升渲染性能。下面我们实现一个简易的虚拟DOM引擎,包含 h 函数(创建虚拟节点)和 patch 函数(对比新旧节点并更新真实 DOM)。
首先,定义虚拟节点结构:
function h(tag, props, children) {return {tag,props,children: Array.isArray(children) ? children : [children],el: null // 对应的真实 DOM 节点};
}
然后,实现 patch 函数,处理首次渲染和更新。
function patch(oldVNode, newVNode) {// 1. 如果 oldVNode 是真实 DOM,则首次渲染if (oldVNode.nodeType) {const parent = oldVNode.parentNode;const el = createElement(newVNode);parent.replaceChild(el, oldVNode);return el;}// 2. 对比两个虚拟节点if (oldVNode.tag !== newVNode.tag) {// 标签不同,直接替换const parent = oldVNode.el.parentNode;const el = createElement(newVNode);parent.replaceChild(el, oldVNode.el);return el;}// 3. 标签相同,更新属性const el = oldVNode.el;updateProps(el, oldVNode.props, newVNode.props);// 4. 更新子节点updateChildren(el, oldVNode.children, newVNode.children);return el;
}
辅助函数 createElement 和 updateProps 实现如下:

function createElement(vnode) {const el = document.createElement(vnode.tag);// 设置属性for (const [key, val] of Object.entries(vnode.props || {})) {el.setAttribute(key, val);}// 处理子节点vnode.children.forEach(child => {if (typeof child === 'string') {el.appendChild(document.createTextNode(child));} else {el.appendChild(createElement(child));}});vnode.el = el;return el;
}function updateProps(el, oldProps, newProps) {// 删除旧属性for (const key in oldProps) {if (!(key in newProps)) {el.removeAttribute(key);}}// 设置新属性for (const key in newProps) {if (oldProps[key] !== newProps[key]) {el.setAttribute(key, newProps[key]);}}
}
设计要点:
- patch 函数优先比较 tag,不同则直接替换,成本最低。
- 属性更新采用“先删后改”策略,避免不必要的 DOM 操作。
- 子节点对比(updateChildren)可进一步采用 key 优化,此处略。
通过以上两个核心设计的实现,你已初步掌握了前端框架设计中响应式系统与虚拟DOM的运作原理。实际框架(如 Vue、React)在此基础上增加了更多优化(如异步更新、Fiber 架构),但核心思想一脉相承。理解这些设计模式,能让你在框架选型、性能优化甚至自定义框架时游刃有余。