虚拟DOM原理与diff算法:面试高频追问与性能真相
2026/9/17 7:04:48 网站建设 项目流程

最近有个朋友在准备前端面试,找我聊到一个特别常见的题:“虚拟DOM到底是个啥?”他说自己看了不少博客,能把“用一个JS对象来描述真实DOM结构”这句话背下来,结果面试官追问了一句“那它一定比直接操作真实DOM快吗?”人直接卡住了。我发现这个问题确实挺有意思:虚拟DOM几乎是所有前端面试绕不开的点,但也是被误解得最深的点之一。这篇文章我就想用做技术的思路,把虚拟DOM从“为什么会出现”到“底层怎么运行”再到“面试怎么答”一次说透。

这篇文章能帮你搞清楚三个层面的东西:第一,虚拟DOM要解决的实际问题是啥,它到底凭什么成为React、Vue这类框架的基石;第二,虚拟DOM内部的工作流程到底长什么样,diff算法怎么找差异、怎么更新页面;第三,遇到面试官追问“快与慢”“key为什么不能用index”“和Svelte比有什么差别”这类问题时,怎么给出有深度的回答。不管你是准备面试,还是学框架学了很久但对原理还模糊,这篇都值得耐心看完。

1. 先搞清楚一件事:虚拟DOM到底解决什么问题

1.1 慢的不是DOM,而是“乱操作DOM”

我不知道你有没有认真想过,浏览器渲染一个页面,从HTML和CSS变成像素,中间其实要经过好几个阶段。DOM树构建完了之后,还需要计算样式,接着做布局(layout,老说法叫reflow),再绘制(paint),最后合成图层展示到屏幕上。

真正让页面卡顿的,往往是“布局”和“绘制”这两步被反复触发。比如你把一个节点的宽度改了,浏览器得重新算它周围一堆节点的位置,特别是当你在一个很复杂的页面上频繁改样式、增删节点,性能问题会特别明显。

而早年间我们写页面,是怎么操作DOM的呢?直接上一堆document.getElementById拿节点,再一层一层往里面塞内容、改class、绑事件。一个稍微复杂的页面,更新一次可能牵扯到几十个节点的修改,浏览器就得跟着反复计算样式和布局。

这还不是最夸张的。有些代码喜欢在循环里改DOM,每次改都强制浏览器同步走一遍布局流程。你想想,每循环一次就触发一次reflow,循环一百次就是一百次,页面不卡才怪。所以业内才有一句老话:DOM操作是昂贵的

1.2 从jQuery到数据驱动:开发模式的历史转向

jQuery时代,我们的开发模式是以DOM为中心的。页面有个待办事项列表,你要新增一条数据,得自己写逻辑:找到列表的ul节点,创建一个li,再把内容塞进去,最后还得记得把它追加到页面上。反过来,如果列表是空的,你还得处理“没有数据”的空状态。

这种写法的最大问题是:把数据和界面之间的同步逻辑,全部交给了开发者。数据一变,你得自己决定改哪个DOM、怎么改、改完要不要清理旧状态。代码一多,状态一复杂,这种代码根本维护不了——典型的意大利面条式代码。

后来React、Vue这类框架火起来,核心是换了一种思路:你只要描述“数据是什么样,界面就应该是什么样”,剩下的更新过程交给框架。业界管这叫“声明式UI”。

可是问题来了:框架怎么知道数据变化之后,界面上哪些地方需要更新呢?如果每次数据一变,它就把整个页面重新渲染一遍,那性能肯定崩了。于是虚拟DOM这个“翻译官”就登场了。

1.3 虚拟DOM的核心价值:让“声明式UI”成为可能

这里我要特别强调一个观点:虚拟DOM最重要的价值,不是“比直接操作DOM快”,而是让声明式UI成为可能

在数据驱动的框架里,组件重新执行一次渲染函数,拿到的是“新状态下的完整界面描述”。框架需要拿这个新的界面描述,去对比之前的状态,算出到底哪些地方变了,再精准地去更新真实DOM。

如果不经过虚拟DOM,框架直接拿新状态的片段去操作DOM,又回到了命令式的老路:更新逻辑散落在框架内部各处,同时还得依赖DOM上下文,跨平台就没戏了。

虚拟DOM用普通的JS对象来描述界面,这就让框架可以在内存里“计算”界面的差异,算完之后才去碰真实DOM。因为是在内存里操作,成本低、速度快,而且不依赖浏览器API。所以不管你怎么准备面试,一定要先理解这一层逻辑:虚拟DOM是数据驱动框架里,连接“数据状态”和“真实DOM”的一层翻译器,它最大贡献是让UI开发变得可声明、可维护、可跨平台

先说清楚这个,后面讲原理才好理解。

2. 虚拟DOM到底是什么:从一个对象到一棵树

2.1 本质就是一个普通的JS对象

虚拟DOM这个名字听起来高大上,但剥开外壳,它就是一个普普通通的JavaScript对象。这个对象描述的是“界面上的某个元素长什么样”。

在React里,一个按钮的虚拟DOM大概长这样:

{ tag: 'button', // 标签名 props: { // 属性集合 className: 'btn', onClick: handleClick }, children: [ // 子节点列表 '点击我' ] }

我习惯把这种用来描述界面的节点叫“虚拟节点”,英文是vnode。每个vnode都会保留三样核心信息:标签名、属性、子节点。一段JSX写出来,最后会形成一棵由无数个vnode组成的“虚拟DOM树”,它是对真实DOM树的一比一映射描述。

注意一个细节:虚拟节点里的children,既可以是文本字符串,也可以是嵌套的vnode对象。这种递归结构,让它可以完整描述任何层级深度的界面。

2.2 为什么要用JS对象描述界面,而不是直接操作真实DOM

你去浏览器控制台随便打印一个真正的DOM节点,会发现它自带几十上百个属性:ownerDocumentchildNodesclassNamedatasetstyle……多到根本看不完。

真实DOM节点太重了。每次创建一个真实DOM节点,浏览器都要为它在内部生成大量元数据,比如事件监听、样式计算数据、布局数据。所以频繁创建、销毁真实节点,对浏览器来说是件挺吃力的事。

而虚拟DOM只保留“描述界面”的必要信息——标签、属性、子节点,轻量得多。创建一棵虚拟DOM树的开销,远小于创建一棵真实DOM树。框架在状态变化时,先重新生成虚拟DOM树,再跟旧的虚拟DOM树做对比,这个过程全程在内存里运行,根本不碰浏览器底层,所以非常快。

另外,虚拟DOM因为是普通JS对象,不依赖浏览器的DOM API,所以天然具备跨平台能力。同样的组件逻辑,在浏览器里可以渲染成真实DOM,在移动端可以渲染成原生组件,在服务端可以渲染成HTML字符串——这就是为什么React能跑出React Native、能跑出服务端渲染的底层原因。这个点等后面讲到跨平台的时候还会展开。

2.3 JSX和render函数,其实是虚拟DOM的语法糖

很多初学者看到JSX,以为它是某种HTML模版,其实不是。JSX是JavaScript的语法扩展,它会在编译阶段被转成普通的函数调用。

以React为例,你在代码里写的:

<div className="app"> <span>Hello</span> </div>

在编译之后,会变成类似这样的代码:

React.createElement( 'div', { className: 'app' }, React.createElement('span', null, 'Hello') );

而每次createElement调用,返回的就是一个vnode对象。Vue那边其实也是一样的道理,Vue的模板经过编译后,会生成一个render函数,这个函数每次执行,同样会返回一棵虚拟DOM树。

所以你可以把框架的渲染过程理解成两步:第一步,根据当前数据执行渲染逻辑,得到一棵新的虚拟DOM树;第二步,拿这棵新树跟旧树做对比,计算出差异,更新真实DOM。

这个“对比计算差异”的过程,就是diff算法在干的活。咱们下一章就讲它。

3. 核心工作流:渲染、对比、打补丁

3.1 虚拟DOM的三步曲:render、diff、patch

一个框架拿到数据变化,到页面最终更新,通常走三条路:

第一步,render。组件状态发生变化,框架重新执行渲染函数,生成一棵新的虚拟DOM树。这相当于把“目标界面”完整描述了一遍。

第二步,diff。新的虚拟DOM树和旧的虚拟DOM树做对比,找出两者之间的差异。比如标题文本变了,某个按钮样式变了,某段列表多了一个条目。diff的输出是一份“差异清单”,告诉框架“页面上哪些地方需要动”。

第三步,patch。拿着差异清单,精准地更新到真实DOM上。该改文本的改文本,该改属性的改属性,该增删节点的增删节点,绝不无脑全量重建。

用个生活化的类比:你装修房子,render是重新画了一张设计图,diff是拿新旧设计图对比,看哪里墙要拆、哪里电路要改,patch才是真正叫工人进场施工。如果你每次装修都把整栋房子推倒重建,那成本就太高了;但你只改图纸上不同的地方,成本就低很多。

3.2 手写一个mini版虚拟DOM,看看它没那么神秘

光说不练假把式。为了帮你看清虚拟DOM内部到底怎么运作,我写了一个极其简化的版本,去掉边界处理,只保留核心逻辑。你在浏览器控制台里就能跑,打开一个空白页面,把下面这段代码粘进去试试。

// h 函数:创建一个虚拟节点 function h(tag, props, children) { return { tag, props: props || {}, children: Array.isArray(children) ? children : children == null ? [] : [children] }; } // render 函数:把虚拟DOM渲染成真实DOM function render(vnode) { if (typeof vnode === 'string') { return document.createTextNode(vnode); } const el = document.createElement(vnode.tag); // 简单的属性设置 for (const key in vnode.props) { el.setAttribute(key, vnode.props[key]); } vnode.children.forEach((child) => { el.appendChild(render(child)); }); return el; } // patch 函数:对比新旧虚拟节点,做最小化更新 function patch(oldVnode, newVnode) { // 如果标签不同,直接替换整个节点 if (oldVnode.tag !== newVnode.tag) { const newEl = render(newVnode); oldVnode.el.parentNode.replaceChild(newEl, oldVnode.el); return; } // 文本节点:内容变了直接更新文本 if (typeof oldVnode === 'string' && typeof newVnode === 'string') { if (oldVnode !== newVnode) { oldVnode.el.nodeValue = newVnode; } return; } // 这里简化处理:只对比子节点的数量和文本变化 const oldChildren = oldVnode.children || []; const newChildren = newVnode.children || []; const len = Math.min(oldChildren.length, newChildren.length); for (let i = 0; i < len; i++) { patch(oldChildren[i], newChildren[i]); } } // 使用示例 const vnode1 = h('div', { class: 'app' }, [ h('span', null, 'Hello'), ' World' ]); const container = document.getElementById('app'); container.appendChild(render(vnode1));

这段代码写得很粗糙,但看完整个人应该会觉得“哦,原来虚拟DOM不过如此”。框架里真正的diff算法,是在这个基础上加了key、复用、批量更新、各种边界判断和性能优化,核心思路和我这里演示的一脉相承。

3.3 一个容易忽略的点:为什么多次setState只触发一次更新

这其实也是个高频面试点,跟虚拟DOM息息相关。React里你在一个事件处理函数里面连续写三次setState,页面不会更新三次,而是只更新一次。

原因是React做了“批量更新”。它把同一个事件循环里触发的多次状态更新收集起来,合并成一次,等事件处理完,再统一执行一次渲染和diff。这样一来,真实DOM在整个过程中只被碰了一次,性能自然好。

Vue那边也有类似机制,数据变化不是立刻更新DOM,而是把更新任务放进一个异步队列,等当前宏任务走完,再一起执行。nextTick就是让你在DOM真正更新完之后的那个时间点拿到最新DOM用的。

这个设计背后的逻辑其实还是那条:能少碰真实DOM,就尽量少碰。每次碰真实DOM,都可能触发样式计算和布局,省一次就是赚一次性能。

4. diff算法到底在diff什么

4.1 从O(n^3)到O(n):为什么必须做同层比较

你可能在知乎或者博客里看到过一个数字:两棵树做全量对比,时间复杂度是O(n^3)。为什么会这么高?因为朴素的diff算法会去尝试所有可能的节点匹配方式:旧树的树根可能对到新树的任何一个节点,旧树的一个子树也可能跑到任意位置。这种把所有可能性都试一遍的做法,数据量一上来就是天文数字。

框架们不约而同选择了“牺牲一部分准确性,换取性能”:不做跨层级的比较,只比较同一层级。也就是说,旧树的第一层子节点,只跟新树的第一层子节点比较;旧树的某个节点的子节点,只跟新树里相同位置节点的子节点比较。

这个决策背后有个重要的经验观察:真实业务里,把一个DOM节点从页面的一个层级搬到另一个层级的操作非常少,而且几乎都是通过条件渲染、列表重组来实现的,很少真的去移动一个节点到深层级去。所以同层比较是一种非常划算的取舍——它把时间复杂度从O(n^3)降到了接近O(n)。

代价就是:如果某个场景真的发生了跨层级的移动,框架不会去复用那个节点,而是直接把整棵子树删掉重建。这在绝大多数场景下是完全可以接受的。

4.2 key的本质:给节点一个“身份证号”

在实际开发中,我们写列表的时候经常会加一个key属性。React和Vue都用它来判断“哪些节点是同一个节点该被复用”。

我举个最直观的例子。列表是['A', 'B', 'C'],你要在头部插入一个'D',变成['D', 'A', 'B', 'C']

如果不用key,框架对比新旧列表时,按顺序对比:

  • 新列表第0项是D,旧列表第0项是A,两者不同,于是框架会把这个位置的节点更新成D;
  • 新列表第1项是A,旧列表第1项是B,框架又把B更新成A;
  • 依次类推,最后发现新列表多了一项,再在末尾新增一个C。

整个过程是:改D、改A、改B、新增C,相当于把列表里3个节点全部破坏重建了一次。如果这些节点内部还带着输入框、滚动位置、图片加载状态,那用户的输入框内容就全丢了,体验极其糟糕。

有key的时候就不一样了。框架会按照key去匹配新旧节点:A、B、C的key分别是1、2、3,新列表是D、A、B、C,框架发现A、B、C都能在旧列表里找到对应节点,于是直接复用它们,只是给它们在新位置腾出地方,然后把D插在头部。这样一来,三个旧节点几乎零成本被复用,只需要多做一次移动操作。

key就是节点的身份证号,它帮diff算法认人:你是你,我是我,哪怕位置变了,只要key没变,咱们还是同一个节点,可以复用。

4.3 为什么不能用index做key:一个经典面试坑

很多人图省事,直接拿数组的下标当作key:

list.map((item, index) => <li key={index}>{item.name}</li>)

平时列表不增不减、不排序,这么做问题不大。但只要发生“头部插入”或者“排序”,你就踩坑了。

我想了一个最经典的场景:一个列表,每一项都有个输入框,用户在第一行输入了“我爱前端”,输入的内容存在这一行的子组件状态里。现在你在列表头部插入了新数据,因为index从0开始排,原来的第一行变成第二行,它的key也从0变成了1。框架一看,key=1在旧列表里对应的是原来第二行那个节点,于是它复用了第二行的DOM实例,把它拖到第一行重新渲染……

但问题来了:第二行的DOM实例上,本来带着用户在第一行输入的“我爱前端”内容。框架只认key,以为是同一个节点,就复用了它,结果用户输入的内容跑到了另一行上。页面看起来就像“内容串行了”。

记住一个结论:只要列表的顺序可能发生变化(插入、删除、排序),就不要用index当key。尽量用业务里稳定的唯一标识,比如商品ID、用户ID、订单号。如果实在没有唯一标识,那也要保证列表是纯静态的、不会改变顺序。

4.4 主流框架的diff演进:Vue双端、React Fiber、Vue3编译优化

diff算法不是一成不变的,主流框架这些年一直在进化。

Vue 2的diff采用的是“双端比较”策略:新旧两棵子树的头尾指针同时向中间靠拢,依次尝试头头对比、尾尾对比、头尾对比、尾头对比,找到相同key的节点就复用,找不到再去映射表里捞。这种策略对“头部插入”“尾部插入”“倒序”这类场景都比较擅长。

Vue 3在diff上又做了升级,使用了基于key的数组滚动算法,并且结合了“最长递增子序列”来计算出最少的移动次数。简单说,它能更聪明地判断哪些节点该移动、哪些节点原地不动,进一步减少真实DOM操作。

React则走了另一条路。从React 16开始,diff不再是同步一次性做完,而是拆成了可中断的“Fiber”架构。它把整个diff过程切成一个个小单元(fiber),配合浏览器的空闲时间调度,可以在不卡主线程的情况下分段执行。这样即使在超大型项目里,React也不会因为一次diff时间过长而出现明显卡顿。

Vue 3还引入了一种“编译时优化”思路:模板编译阶段,会给静态节点打上标记,diff时直接跳过这些静态子树,只比较动态节点。这个思路很妙,等于把一部分比较工作提前到编译期完成,运行时负担就更小了。

5. 虚拟DOM的快与慢:别被面试题带偏

5.1 虚拟DOM不保证一定比原生DOM快

这是个非常重要的点,我建议每个准备面试的人都把这句话刻在脑子里:虚拟DOM本身不保证一定比直接操作真实DOM快

你想想,虚拟DOM每次更新都要做三件事:生成一棵新树、对比新旧树、打补丁更新真实DOM。这三步本身就有开销。而如果你是个极致优化的老手,知道某个地方只改了一个文本节点,你直接用原生API定位到那个节点,改一下textContent,这个操作比任何框架都快。

那框架为什么要用虚拟DOM?因为它赚的是“批量”和“可维护性”的钱。在高频交互、复杂数据流的大型应用里,开发者很难精准记忆每个状态对应哪个DOM节点,手动操作非常容易写出“乱改一气”的低效代码。而虚拟DOM框架通过批量更新、精准diff、声明式代码,帮你在绝大多数场景下避免了“乱碰DOM”的性能损耗。

所以在面试里,如果有面试官问“虚拟DOM一定快吗”,你可以大大方方说“不一定,单次更新很可能比手动DOM操作慢,但它提供了可持续的、可维护的性能保障”。

5.2 真正被节省的是什么:少碰真实DOM,就是胜利

我们用虚拟DOM,本质上是把“找哪些地方变了”这件事交给了框架,让框架把“要做的改动”最小化。最小化的直接效果就是:真实DOM被操作的次数大大减少。

还记得开头说的吗?真实DOM一旦发生结构或样式变化,可能触发layout和paint。这些才是页面卡顿的元凶。虚拟DOM虽然多了一步内存里的diff,但它换来了“每次状态变化,真实DOM只做必要的小改动”,这比“状态一变动,整片区域重渲染”要高效得多。

尤其对于大型列表、表单、图表这类高频更新场景,效果天差地别。你手动写循环去改每一个列表项,跟框架拍平后只更新那一项,性能差距是数量级的。

5.3 对比innerHTML:为什么“全量替换”不划算

老一代前端对innerHTML应该都不陌生。以前做局部刷新,大家喜欢直接把一段HTML字符串塞进去:

document.getElementById('list').innerHTML = newHtmlString;

这看起来简单,实际上代价很高。innerHTML赋值时,浏览器会被迫把那段HTML字符串从头解析一遍,把旧节点全部销毁,再创建新节点,再重新渲染。如果这段内容里有表单输入框,用户输入的内容会被一起销毁;如果有图片,图片可能会重新加载,造成闪烁。

有个讲性能的经典对比说:如果你频繁地替换整块内容,innerHTML其实不一定比虚拟DOM快,因为它每次都重建整块DOM,而虚拟DOM只更新变了的节点。

把这两个放一起对比,你就能理解为什么现在的框架都选择虚拟DOM方案,因为“知道差异,只更新差异”这件事,在大规模、高交互的应用里太重要了。

5.4 虚拟DOM并非唯一路线:Svelte和编译时优化

聊到这里,等你把视野拉远一点,会看到虚拟DOM并不是前端界面更新技术的唯一答案。有位选手Svelte就直接“反叛”了:它没有虚拟DOM,编译阶段就把组件的状态更新逻辑编译成“精准操作真实DOM”的代码。状态变了,直接生成操作指令,一行多余代码都没有。

Svelte的做法牺牲了一部分的灵活性,但换来了更小的运行时包体积、更低的内存占用和更快的启动速度。这也是它这几年能站住脚的核心原因。

Vue3和Solid等框架则在思考一个折中路线:保留虚拟DOM,但用编译时优化来减少diff的开销。比如刚才提到的Vue3模板静态标记、静态提升,就是让diff只关注动态区域。

所以,你在面试里如果能说出“虚拟DOM不是银弹,它的选择是一种工程权衡,背后还有编译时优化等替代路线”,面试官多半会觉得你比只会背概念的人高出一个段位。

6. 面试高频追问与参考答案

6.1 面试官最常问的几个问题,整理成一张速查表

我把自己带团队面试时最常用来考察候选人前端功底的几个追问整理了一下,做成一张表,方便你对着练:

问题核心答案要点加分回答方向
虚拟DOM是什么用JS对象描述真实DOM结构,元素由tag、props、children组成补充说明它是一棵vnode树,对应组件渲染结果
虚拟DOM一定比真实DOM快吗不一定,单次操作未必快,但能减少重复更新、支持批量操作说明真实DOM操作可能触发layout和paint,这才是性能关键
key的作用是什么让diff算法识别同一节点,从而实现节点复用强调key变了会导致节点被当作新节点,引发重建
为什么key不用index顺序变化时节点身份错位,导致状态复用混乱举例:头部插入列表项时输入框内容串行
虚拟DOM是怎么更新的生成新树、diff找差异、patch更新真实DOM补充批量更新机制,说明多次setState只渲染一次
虚拟DOM解决了什么问题让UI开发从命令式变成声明式,实现数据驱动视图补充跨平台能力,不依赖DOM环境
虚拟DOM有什么缺点内存中维护vnode树有额外开销;复杂场景下diff也有性能损耗说出Svelte/编译时优化方案,说明不是唯一路线

这张表不是让你背答案,而是拿来检验自己有没有真正理解。每个问题,你如果能用自己的话讲明白,并且举出一个场景例子,那就说明真懂了。

6.2 怎么组织一次让面试官满意的回答

面试问“虚拟DOM是什么”的时候,最忌讳直接上来背诵“用一个JS对象描述真实DOM”。这个回答太干,没信息量。我建议用“背景—原理—权衡—演进”四段式来组织答案。

第一句先给背景:虚拟DOM是React和Vue这些声明式框架为了解决“数据和UI同步”问题提出的一种实现方案。接着讲原理:我在组件里声明好结构,框架执行render生成一棵虚拟DOM树,状态变化后重新生成一棵新树,然后diff两棵树,找到最小差异,再patch到真实DOM上。

然后讲权衡:虚拟DOM不是银弹,单次操作未必比原生操作快,但它在复杂状态场景下能大幅减少真实DOM操作次数,还能支撑跨平台渲染。最后补一句演进:现在的Vue3用编译时优化减少diff负担,Svelte干脆不用虚拟DOM,直接编译成命令式代码。

这套回答下来,你会给面试官留下“有体系、有深度、能迁移思考”的印象。就算他继续深挖,只要你的知识结构是完整的,就不会慌。

6.3 如果面试官让你手写,怎么快速过关

说实话,现在不少公司会让候选人现场写一个简化版虚拟DOM。别慌,按我刚才的mini版思路来就行。

第一件事,写一个创建vnode的函数(h或者createElement),返回{ tag, props, children }结构。第二件事,写一个把vnode渲染成真实DOM的函数,递归创建标签和子节点。第三件事,写一个最简patch:先比较标签,不同就整组替换;相同就看文本有没有变化,有就改文本;然后递归对比子节点。能做到这三步,面试官基本就认可你理解了虚拟DOM的核心链路。

如果面试官进一步问你“真实的diff算法还有什么优化”,你可以提key、同层比较、批量更新,再说一下Vue双端diff、React Fiber这些关键词。这些我在前面都写过,你真理解了,现场说出来不会卡壳。

6.4 带团队这段时间,我理解到的“学原理”的正确姿势

我见过太多面试者,能把虚拟DOM的博客文章倒背如流,但问到“如果用虚拟DOM渲染一个超长列表,性能瓶颈可能出现在哪”就哑火。问题出在只背结论、不推导过程。

我的建议是,学这类原理知识时,一定花半天时间自己动手写一个mini版本。不用追求功能完整,只要能实现hrenderpatch这三个函数,把一个小页面跑起来,你对虚拟DOM的理解能超过80%只会背概念的人。

原理这东西,说白了就是一个“问题—方案—取舍”的闭环。你每学一个框架机制,都问自己三个问题:它解决了什么问题?它为什么用这种方案?这个方案有什么代价?想通这三个问题,你就不是在背知识,而是在建立自己的技术判断力。

我在这几年的实际面试和带项目里,最深的体会是:候选人能不能说清楚“为什么”,比能不能说准“是什么”重要得多。虚拟DOM这片海,你真正游一遍过去,再回头看,会发现它只是前端工程发展路上的一个里程碑,不是终点。理解了它,你就理解了React与Vue的一半设计哲学,后面再看Fiber、看编译优化、看各种新兴框架,都会觉得顺理成章。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询