这是一段用 JavaScript 概括前端核心运行原理的代码,把渲染流水线、响应式、虚拟 DOM 和事件循环串成了一个小故事。
```javascript
/* ================================================================
* 前端原理速写:一段代码跑完「状态 → 视图」的一生
* ================================================================ */
// ── 1. 渲染引擎:浏览器把 HTML/CSS 变成像素的流水线 ──────────
const RenderPipeline = {
parse: html => ({ dom: html }), // HTML → DOM Tree
style: (dom, css) => ({ ...dom, cssom: css }), // CSS → CSSOM,合成 Render Tree
layout: tree => ({ ...tree, box: '几何位置' }), // 重排:算位置和尺寸
paint: tree => ({ ...tree, list: '绘制指令' }), // 重绘:生成绘制列表
composite: tree => '🎨 图层合成 → 屏幕像素', // GPU 合成上屏
render(html, css) {
const { parse, style, layout, paint, composite } = this;
return composite(paint(layout(style(parse(html), css))));
}
};
// ── 2. 响应式:Proxy 拦截读写,读时收集依赖,写时触发更新 ────
let activeEffect = null;
const bucket = new WeakMap(); // target → key → Set<effect>
function reactive(obj) {
return new Proxy(obj, {
get(t, k) {
if (activeEffect) { // 谁在读我?记下来
const deps = bucket.get(t) ?? (bucket.set(t, new Map()), bucket.get(t));
const set = deps.get(k) ?? (deps.set(k, new Set()), deps.get(k));
set.add(activeEffect);
}
return tk;
},
set(t, k, v) {
tk = v;
bucket.get(t)?.get(k)?.forEach(effect => effect()); // 我变了,通知他们
return true;
}
});
}
const effect = fn => { activeEffect = fn; fn(); activeEffect = null; };
// ── 3. 虚拟 DOM:用 JS 对象描述 UI,diff 后只改差异 ──────────
const h = (tag, props, ...children) => ({ tag, props: props ?? {}, children });
const isSame = (a, b) => a && b && a.tag === b.tag;
function render(vnode) {
if (typeof vnode !== 'object') return document.createTextNode(vnode);
const el = document.createElement(vnode.tag);
for (const k in vnode.props) elk = vnode.propsk;
vnode.children.forEach(c => el.append(render(c)));
return el;
}
function patch(parent, i, oldV, newV) { // 简化版 diff + patch
const el = parent.childNodesi;
if (!oldV) return newV && parent.append(render(newV)); // 新增
if (!newV) return el.remove(); // 删除
if (!isSame(oldV, newV)) return parent.replaceChild(render(newV), el); // 替换
for (const k in newV.props) elk = newV.propsk; // 复用节点,只更新属性
const max = Math.max(oldV.children.length, newV.children.length);
for (let j = 0; j < max; j++) patch(el, j, oldV.childrenj, newV.childrenj);
}
// ── 4. 事件循环:JS 单线程,靠宏/微任务把异步切成小片 ────────
const loop = {
macro: \[\], // setTimeout / I/O / 用户事件
micro: \[\], // Promise.then / queueMicrotask
tick() { // 一个 tick 的一生
while (this.micro.length) this.micro.shift()(); // 1. 清空微任务
RenderPipeline.render('<div>', 'css'); // 2. 渲染一帧
this.macro.shift()?.(); // 3. 取一个宏任务
}
};
// ── 5. 组装:数据一变,视图自动跟上(单向数据流) ─────────────
const state = reactive({ count: 0 });
let vdom = null;
effect(() => { // 副作用 = 渲染函数
const next = h('div', { textContent: `count = ${state.count}` });
patch(document.body, 0, vdom, next);
vdom = next; // 记下这次的结果,供下次 diff
});
state.count++; // 一行赋值 → effect 重跑 → diff → patch → 上屏
state.count++; // 前端的一切,本质上都是这一句的回声
```
把前端原理跑成一条「状态 → 视图」的流水线
这段代码用五个小模块把浏览器里的核心机制串了起来,每一块都对应着真实前端体系里的一个关键环节。
· 渲染引擎:RenderPipeline 从解析 HTML、拼接 CSSOM,到重排、重绘,最后交给合成器上屏,是一条完整的像素生产线。
· 响应式系统:reactive 和 effect 合作,读取数据时默默记下依赖,数据一改就自动通知所有关心它的副作用函数。
· 虚拟 DOM 与 diff:h 生成轻量的 JS 对象树,patch 对比新旧节点的差异,只做必要的 DOM 操作,避免整棵树重建。
· 事件循环:loop 模拟了一个 tick 的节奏------先清空微任务,再渲染一帧,最后取一个宏任务,展示了单线程下的调度策略。
· 单向数据流:最后用一个 count 计数器收尾,赋值操作触发 effect 重跑,视图自动更新,完整走通"状态驱动视图"的闭环。
优化建议: 第 5 部分的 state.count++ 是演示用的,您可以把 count 换成实际业务里的响应式数据,同时把 patch 的挂载点从 document.body 改成页面上真实存在的容器。
文章仅供参考学习用。