<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>前端 on 吾爱主机</title><link>https://blog.waihost.com/categories/%E5%89%8D%E7%AB%AF/</link><description>Recent content in 前端 on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sun, 26 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/categories/%E5%89%8D%E7%AB%AF/index.xml" rel="self" type="application/rss+xml"/><item><title>Vue 3 响应式系统：Proxy、依赖收集与 effect 调度工程实践</title><link>https://blog.waihost.com/posts/vue3-reactivity-proxy-effect-scheduling/</link><pubDate>Sun, 26 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/vue3-reactivity-proxy-effect-scheduling/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/vue3-reactivity-proxy-effect-scheduling.svg" alt="Featured image of post Vue 3 响应式系统：Proxy、依赖收集与 effect 调度工程实践" /&gt;&lt;p&gt;改了数据视图却不更新、解构后“丢响应”、深度监听里 &lt;code&gt;newValue === oldValue&lt;/code&gt;、异步里创建的 &lt;code&gt;watch&lt;/code&gt; 内存泄漏——这些坑多半不是“Vue 坏了”，而是 &lt;strong&gt;依赖收集（track）与派发更新（trigger）&lt;/strong&gt; 的契约被绕开了。本文基于 Vue 官方 &lt;em&gt;Reactivity in Depth&lt;/em&gt;、&lt;em&gt;Reactivity Fundamentals&lt;/em&gt;、&lt;em&gt;Watchers&lt;/em&gt; 与 &lt;em&gt;Reactivity API&lt;/em&gt; 文档，并结合 MDN 的 &lt;code&gt;Proxy&lt;/code&gt; 说明，把 Vue 3 响应式从原理讲到可复现的工程实践。&lt;/p&gt;
&lt;h2 id="问题背景为什么改了变量-ui-不跟着变"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af%e4%b8%ba%e4%bb%80%e4%b9%88%e6%94%b9%e4%ba%86%e5%8f%98%e9%87%8f-ui-%e4%b8%8d%e8%b7%9f%e7%9d%80%e5%8f%98" class="header-anchor"&gt;&lt;/a&gt;问题背景：为什么“改了变量 UI 不跟着变”
&lt;/h2&gt;&lt;p&gt;普通 JavaScript 赋值不会自动重算依赖值。官方用表格公式类比：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;A0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;A1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;A2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;A0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;A1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;A0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// A2 仍然是 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要让 &lt;code&gt;A2&lt;/code&gt; 随依赖变化，需要三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;读依赖时登记订阅&lt;/strong&gt;（track）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;写依赖时通知订阅者&lt;/strong&gt;（trigger）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;把“重算逻辑”包装成 effect&lt;/strong&gt;，在依赖变化时重跑&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vue 做不到拦截局部变量读写（语言层面没有钩子），但能拦截 &lt;strong&gt;对象属性&lt;/strong&gt; 的读/写。Vue 2 主要靠 &lt;code&gt;Object.defineProperty&lt;/code&gt; 的 getter/setter；Vue 3 对 &lt;strong&gt;响应式对象使用 &lt;code&gt;Proxy&lt;/code&gt;&lt;/strong&gt;，对 &lt;strong&gt;ref 使用 getter/setter&lt;/strong&gt;。MDN 指出 &lt;code&gt;Proxy&lt;/code&gt; 可自定义对象的基础操作（属性查找、赋值、枚举、函数调用等），这正是 Vue 运行时收集依赖的入口。&lt;/p&gt;
&lt;h2 id="核心原理track--trigger--effect"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8e%9f%e7%90%86track--trigger--effect" class="header-anchor"&gt;&lt;/a&gt;核心原理：track / trigger / effect
&lt;/h2&gt;&lt;h3 id="1-proxy-拦截对象属性"&gt;&lt;a href="#1-proxy-%e6%8b%a6%e6%88%aa%e5%af%b9%e8%b1%a1%e5%b1%9e%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;1. Proxy 拦截对象属性
&lt;/h3&gt;&lt;p&gt;官方伪代码把机制压缩得很清楚：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;refObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;含义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;读路径&lt;/strong&gt;调用 &lt;code&gt;track&lt;/code&gt;：当前正在执行的 effect 订阅该属性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;写路径&lt;/strong&gt;调用 &lt;code&gt;trigger&lt;/code&gt;：通知订阅该属性的 effect 重跑。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ref&lt;/code&gt; 把任意类型装进带 &lt;code&gt;.value&lt;/code&gt; 的容器，用 getter/setter 完成同一套 track/trigger。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-依赖存储结构"&gt;&lt;a href="#2-%e4%be%9d%e8%b5%96%e5%ad%98%e5%82%a8%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;2. 依赖存储结构
&lt;/h3&gt;&lt;p&gt;effect 订阅存在全局结构（概念上）为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;WeakMap&amp;lt;target, Map&amp;lt;key, Set&amp;lt;effect&amp;gt;&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;target&lt;/code&gt;：原始对象（或 ref 对象）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;key&lt;/code&gt;：属性名（ref 固定为 &lt;code&gt;'value'&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Set&amp;lt;effect&amp;gt;&lt;/code&gt;：依赖该属性的副作用集合&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;track&lt;/code&gt; 时若存在 &lt;code&gt;activeEffect&lt;/code&gt;，把它加入对应 Set；&lt;code&gt;trigger&lt;/code&gt; 时遍历该 Set 并执行。&lt;code&gt;WeakMap&lt;/code&gt; 以对象为键，目标对象不再被引用时依赖图可被 GC，这是设计上的关键点。&lt;/p&gt;
&lt;h3 id="3-activeeffect-与-reactive-effect"&gt;&lt;a href="#3-activeeffect-%e4%b8%8e-reactive-effect" class="header-anchor"&gt;&lt;/a&gt;3. activeEffect 与 Reactive Effect
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;activeEffect&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;whenDepsChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;activeEffect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;activeEffect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 首次运行即可完成依赖收集
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;首次执行 effect 时，内部读到的响应式属性都会把“当前 effect”登记为订阅者；之后依赖变更再 &lt;code&gt;trigger&lt;/code&gt;。Vue 对外暴露的 &lt;strong&gt;&lt;code&gt;watchEffect()&lt;/code&gt;&lt;/strong&gt; 就是创建这种 &lt;strong&gt;Reactive Effect&lt;/strong&gt; 的 API：立即运行、自动收集依赖、依赖变化后重跑。组件渲染本身也是一个（更复杂的）reactive effect——状态与 DOM 同步的底层模型。&lt;/p&gt;
&lt;h3 id="4-computed-是带缓存的-effect"&gt;&lt;a href="#4-computed-%e6%98%af%e5%b8%a6%e7%bc%93%e5%ad%98%e7%9a%84-effect" class="header-anchor"&gt;&lt;/a&gt;4. computed 是带缓存的 effect
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vue&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;A0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;A1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;A2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;A0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;A1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;computed&lt;/code&gt; 内部用 reactive effect 管理失效与重算：依赖不变时读缓存，依赖变更后标记脏并在下次读取时重算。适合 &lt;strong&gt;派生状态&lt;/strong&gt;；需要发请求、写 DOM 等 &lt;strong&gt;副作用&lt;/strong&gt; 时用 &lt;code&gt;watch&lt;/code&gt; / &lt;code&gt;watchEffect&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="api-选型refreactiveshallowreadonly"&gt;&lt;a href="#api-%e9%80%89%e5%9e%8brefreactiveshallowreadonly" class="header-anchor"&gt;&lt;/a&gt;API 选型：ref、reactive、shallow、readonly
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;API&lt;/th&gt;
					&lt;th&gt;本质&lt;/th&gt;
					&lt;th&gt;默认深度&lt;/th&gt;
					&lt;th&gt;典型用途&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ref(x)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;.value&lt;/code&gt; 容器；对象值会再走 &lt;code&gt;reactive&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;深&lt;/td&gt;
					&lt;td&gt;基本类型与统一写法的首选&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reactive(obj)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;ES Proxy，&lt;strong&gt;≠&lt;/strong&gt; 原对象&lt;/td&gt;
					&lt;td&gt;深&lt;/td&gt;
					&lt;td&gt;对象/数组状态（注意解构与身份）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;shallowRef(x)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;只跟踪 &lt;code&gt;.value&lt;/code&gt; 替换&lt;/td&gt;
					&lt;td&gt;浅&lt;/td&gt;
					&lt;td&gt;大对象、不可变快照、外部状态库&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;shallowReactive(obj)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;仅根属性响应式&lt;/td&gt;
					&lt;td&gt;浅&lt;/td&gt;
					&lt;td&gt;根级大表/性能边界（慎嵌套）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;readonly(x)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;只读深代理，仍可被 track&lt;/td&gt;
					&lt;td&gt;深&lt;/td&gt;
					&lt;td&gt;对外暴露不可变视图&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;toRaw&lt;/code&gt; / &lt;code&gt;markRaw&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;逃逸/标记永不代理&lt;/td&gt;
					&lt;td&gt;—&lt;/td&gt;
					&lt;td&gt;读原对象、第三方实例、性能 opt-out&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;关键事实（官方 API 文档）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;reactive()&lt;/code&gt; &lt;strong&gt;深转换&lt;/strong&gt;嵌套属性；嵌套 ref 在作为对象属性访问时会 &lt;strong&gt;自动解包&lt;/strong&gt;；作为 &lt;strong&gt;数组元素或 &lt;code&gt;Map&lt;/code&gt; 值&lt;/strong&gt; 访问时 &lt;strong&gt;不解包&lt;/strong&gt;，仍需 &lt;code&gt;.value&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;对同一对象多次 &lt;code&gt;reactive()&lt;/code&gt; 返回 &lt;strong&gt;同一 proxy&lt;/strong&gt;；对已有 proxy 再 &lt;code&gt;reactive()&lt;/code&gt; 仍返回该 proxy。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reactive&lt;/code&gt; 代理与原对象用 &lt;code&gt;===&lt;/code&gt; 比较为 &lt;strong&gt;false&lt;/strong&gt;。务必始终通过 proxy 访问，不要混用原对象与代理。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shallowRef&lt;/code&gt;：只改 &lt;code&gt;state.value.count&lt;/code&gt; &lt;strong&gt;不会&lt;/strong&gt;触发依赖；替换 &lt;code&gt;state.value = {...}&lt;/code&gt; 才会。需要在浅层 ref 上做深变更后强制更新时，用 &lt;code&gt;triggerRef(shallow)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="实践一最小可运行依赖图"&gt;&lt;a href="#%e5%ae%9e%e8%b7%b5%e4%b8%80%e6%9c%80%e5%b0%8f%e5%8f%af%e8%bf%90%e8%a1%8c%e4%be%9d%e8%b5%96%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;实践一：最小可运行依赖图
&lt;/h2&gt;&lt;p&gt;下面用 Node + Vue 运行时演示 track/trigger（无需浏览器）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 任选一种安装方式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm init -y
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm i vue@3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// lab-reactivity.mjs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;watchEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;shallowRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;triggerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toRaw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isReactive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vue&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;watchEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`effect: count=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;, doubled=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doubled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 首次 + 两次变更，共 3 条（具体实现细节可能批量，但依赖关系清晰）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Proxy 身份
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;proxy===raw?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;toRaw(state)===raw?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;isReactive(state)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isReactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 浅层 ref
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallowRef&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;shallowHits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;watchEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;shallowHits&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 仅读取 .value 建立依赖；内部字段变更不自动 trigger
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bag size hint&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;bag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 通常不会再次触发 effect
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;triggerRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 手动 trigger
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;shallowHits after triggerRef&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shallowHits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node lab-reactivity.mjs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;观察点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;watchEffect&lt;/code&gt; 首次即执行，读 &lt;code&gt;count&lt;/code&gt; / &lt;code&gt;doubled&lt;/code&gt; 完成订阅。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;state === raw&lt;/code&gt; 为 false，但 &lt;code&gt;toRaw(state) === raw&lt;/code&gt; 为 true。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shallowRef&lt;/code&gt; 内部 &lt;code&gt;push&lt;/code&gt; 不自动更新依赖，&lt;code&gt;triggerRef&lt;/code&gt; 后才会。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="实践二组件里常见写法与正确边界"&gt;&lt;a href="#%e5%ae%9e%e8%b7%b5%e4%ba%8c%e7%bb%84%e4%bb%b6%e9%87%8c%e5%b8%b8%e8%a7%81%e5%86%99%e6%b3%95%e4%b8%8e%e6%ad%a3%e7%a1%ae%e8%be%b9%e7%95%8c" class="header-anchor"&gt;&lt;/a&gt;实践二：组件里常见写法与正确边界
&lt;/h2&gt;&lt;h3 id="解构与丢响应"&gt;&lt;a href="#%e8%a7%a3%e6%9e%84%e4%b8%8e%e4%b8%a2%e5%93%8d%e5%ba%94" class="header-anchor"&gt;&lt;/a&gt;解构与“丢响应”
&lt;/h3&gt;&lt;p&gt;把 &lt;code&gt;reactive&lt;/code&gt; 对象的属性赋给局部变量后，再读写局部变量 &lt;strong&gt;不再经过 Proxy trap&lt;/strong&gt;，因此不再响应式。若局部变量指向对象，&lt;strong&gt;修改该对象内部&lt;/strong&gt;仍可能触发（因为仍是代理对象），但“换绑”到另一个值不会：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toRefs&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vue&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Ada&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 丢：基本类型解构成局部变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="c1"&gt;// 不触发视图
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 保：保持在 proxy 上访问
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 保：需要解构时用 toRefs，得到一堆 ref
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;countRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toRefs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;countRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="watch-vs-watcheffect"&gt;&lt;a href="#watch-vs-watcheffect" class="header-anchor"&gt;&lt;/a&gt;&lt;code&gt;watch&lt;/code&gt; vs &lt;code&gt;watchEffect&lt;/code&gt;
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;&lt;code&gt;watchEffect&lt;/code&gt;&lt;/th&gt;
					&lt;th&gt;&lt;code&gt;watch&lt;/code&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;首次执行&lt;/td&gt;
					&lt;td&gt;立即&lt;/td&gt;
					&lt;td&gt;默认懒执行（&lt;code&gt;immediate: true&lt;/code&gt; 可立刻跑）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;依赖声明&lt;/td&gt;
					&lt;td&gt;执行时自动收集&lt;/td&gt;
					&lt;td&gt;显式 source（ref / getter / reactive / 数组）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;新旧值&lt;/td&gt;
					&lt;td&gt;无&lt;/td&gt;
					&lt;td&gt;回调拿到 &lt;code&gt;newValue&lt;/code&gt; / &lt;code&gt;oldValue&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;深度&lt;/td&gt;
					&lt;td&gt;读到即收集&lt;/td&gt;
					&lt;td&gt;直接 watch reactive 对象时隐式 deep；getter 默认浅&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;watchEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vue&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;nested&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 自动收集 id
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;watchEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;load&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 精确 source + 新旧值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id changed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 直接 watch reactive → 深监听；深层 mutation 时 new===old
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ov&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;deep mutate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nv&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ov&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// true（对象引用未换）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// getter 默认只在返回值“变”时触发；要深层需 { deep: true }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nested replaced or deep&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;deep&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="flush-时序pre--post--sync"&gt;&lt;a href="#flush-%e6%97%b6%e5%ba%8fpre--post--sync" class="header-anchor"&gt;&lt;/a&gt;flush 时序：&lt;code&gt;pre&lt;/code&gt; / &lt;code&gt;post&lt;/code&gt; / &lt;code&gt;sync&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;状态变更可能同时触发组件更新与用户 watcher。默认会 &lt;strong&gt;批量&lt;/strong&gt;，避免数组连续 &lt;code&gt;push&lt;/code&gt; 一千次就回调一千次。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;默认 &lt;code&gt;flush: 'pre'&lt;/code&gt;&lt;/strong&gt;：在父组件更新之后、&lt;strong&gt;拥有者组件 DOM 更新之前&lt;/strong&gt;调用。此时读自己的 DOM 仍是更新前状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;flush: 'post'&lt;/code&gt;&lt;/strong&gt;（或 &lt;code&gt;watchPostEffect&lt;/code&gt;）：在 DOM 更新后执行，适合量尺寸、聚焦输入框。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;flush: 'sync'&lt;/code&gt;&lt;/strong&gt;（或 &lt;code&gt;watchSyncEffect&lt;/code&gt;）：依赖一变立刻同步跑；官方提醒可能带来性能与一致性问题，仅在少数缓存失效场景使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;post&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;组件渲染本身也不是同步刷 DOM：变更后会缓冲到更新周期的 &lt;strong&gt;next tick&lt;/strong&gt;，保证同一轮同步代码里多次改状态只渲染一次。需要等 DOM 落地时用 &lt;code&gt;nextTick()&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="异步创建-watcher-必须手动-stop"&gt;&lt;a href="#%e5%bc%82%e6%ad%a5%e5%88%9b%e5%bb%ba-watcher-%e5%bf%85%e9%a1%bb%e6%89%8b%e5%8a%a8-stop" class="header-anchor"&gt;&lt;/a&gt;异步创建 watcher 必须手动 stop
&lt;/h3&gt;&lt;p&gt;在 &lt;code&gt;setup&lt;/code&gt; / &lt;code&gt;&amp;lt;script setup&amp;gt;&lt;/code&gt; &lt;strong&gt;同步&lt;/strong&gt;创建的 &lt;code&gt;watch&lt;/code&gt; / &lt;code&gt;watchEffect&lt;/code&gt; 会绑定组件实例，卸载时自动停止。若在 &lt;code&gt;setTimeout&lt;/code&gt;、&lt;code&gt;then&lt;/code&gt; 等异步回调里创建，&lt;strong&gt;不会&lt;/strong&gt;自动绑定，需保存返回句柄并 &lt;code&gt;stop()&lt;/code&gt;，否则泄漏：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 危险
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;watchEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 正确：尽量同步创建；或
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;watchEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 不再需要时
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;副作用清理（取消上一次请求）可用回调第三个参数 &lt;code&gt;onCleanup&lt;/code&gt;，或 Vue 3.5+ 的 &lt;code&gt;onWatcherCleanup&lt;/code&gt;（须在 watcher 同步执行阶段注册，不能写在 &lt;code&gt;await&lt;/code&gt; 之后）。&lt;/p&gt;
&lt;h3 id="外部状态--大列表shallowref-模式"&gt;&lt;a href="#%e5%a4%96%e9%83%a8%e7%8a%b6%e6%80%81--%e5%a4%a7%e5%88%97%e8%a1%a8shallowref-%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;外部状态 / 大列表：shallowRef 模式
&lt;/h3&gt;&lt;p&gt;对接 Immer、XState、不可变快照时，官方推荐 &lt;strong&gt;用 &lt;code&gt;shallowRef&lt;/code&gt; 持有外部状态&lt;/strong&gt;，状态变更时 &lt;strong&gt;替换 &lt;code&gt;.value&lt;/code&gt;&lt;/strong&gt; 触发更新，而不是深代理整棵树：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shallowRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vue&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;produce&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;immer&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useImmer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallowRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updater&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;produce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updater&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="实践三排错清单从现象到机制"&gt;&lt;a href="#%e5%ae%9e%e8%b7%b5%e4%b8%89%e6%8e%92%e9%94%99%e6%b8%85%e5%8d%95%e4%bb%8e%e7%8e%b0%e8%b1%a1%e5%88%b0%e6%9c%ba%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;实践三：排错清单（从现象到机制）
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;现象&lt;/th&gt;
					&lt;th&gt;可能原因&lt;/th&gt;
					&lt;th&gt;排查/修法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;改了字段 UI 不动&lt;/td&gt;
					&lt;td&gt;改的是原对象而非 proxy；或 Options API 里运行时往 &lt;code&gt;this&lt;/code&gt; 上动态加未在 &lt;code&gt;data&lt;/code&gt; 声明的字段&lt;/td&gt;
					&lt;td&gt;始终通过 &lt;code&gt;reactive&lt;/code&gt;/&lt;code&gt;ref&lt;/code&gt;/组件状态访问；预声明字段&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;解构后失效&lt;/td&gt;
					&lt;td&gt;基本类型离开 proxy trap&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;toRefs&lt;/code&gt; / 保持 &lt;code&gt;state.x&lt;/code&gt; / 用 &lt;code&gt;ref&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;===&lt;/code&gt; 意外 false&lt;/td&gt;
					&lt;td&gt;proxy 与 raw 身份不同&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;toRaw&lt;/code&gt; 比对；不要缓存 raw 再与 proxy 混比&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;深 watch 里 new===old&lt;/td&gt;
					&lt;td&gt;同一对象引用上的深层 mutation&lt;/td&gt;
					&lt;td&gt;预期行为；需要 diff 时自行对比字段或换不可变更新&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;watch 不跑&lt;/td&gt;
					&lt;td&gt;默认 lazy；或 getter 只返回同一对象引用&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;immediate: true&lt;/code&gt;；&lt;code&gt;deep: true&lt;/code&gt; 或 watch reactive 本体&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;大列表卡顿&lt;/td&gt;
					&lt;td&gt;深代理整表&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;shallowRef&lt;/code&gt; + 替换引用；&lt;code&gt;markRaw&lt;/code&gt; 固定第三方实例&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;卸载后仍请求&lt;/td&gt;
					&lt;td&gt;异步创建的 effect 未 stop / 未 onCleanup&lt;/td&gt;
					&lt;td&gt;同步创建或手动 stop；请求取消挂到 cleanup&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Dev 难查谁触发渲染&lt;/td&gt;
					&lt;td&gt;依赖图不透明&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;onRenderTracked&lt;/code&gt; / &lt;code&gt;onRenderTriggered&lt;/code&gt;；&lt;code&gt;computed&lt;/code&gt;/&lt;code&gt;watch&lt;/code&gt; 的 &lt;code&gt;onTrack&lt;/code&gt;/&lt;code&gt;onTrigger&lt;/code&gt;（仅开发模式）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;调试钩子事件形状（开发模式）大致包含：&lt;code&gt;effect&lt;/code&gt;、&lt;code&gt;target&lt;/code&gt;、&lt;code&gt;type&lt;/code&gt;（track: &lt;code&gt;get|has|iterate&lt;/code&gt;；trigger: &lt;code&gt;set|add|delete|clear&lt;/code&gt;）、&lt;code&gt;key&lt;/code&gt;、新旧值等，便于在 DevTools 里下断点。&lt;/p&gt;
&lt;h2 id="与-signals--编译期响应式的位置"&gt;&lt;a href="#%e4%b8%8e-signals--%e7%bc%96%e8%af%91%e6%9c%9f%e5%93%8d%e5%ba%94%e5%bc%8f%e7%9a%84%e4%bd%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;与 Signals / 编译期响应式的位置
&lt;/h2&gt;&lt;p&gt;官方把 Vue &lt;code&gt;ref&lt;/code&gt; 与 Solid/Angular/Preact 等 &lt;strong&gt;signals&lt;/strong&gt; 归为同类原语：值容器 + 访问时收集依赖 + 变更时触发副作用。Vue 目前渲染仍以 Virtual DOM 为主，并借助编译优化；运行时响应式的优点是可无构建步骤、边界情况相对少；代价是受 JS 语法限制，需要 &lt;code&gt;.value&lt;/code&gt; 这类容器。团队曾探索 &lt;em&gt;Reactivity Transform&lt;/em&gt;，最终未作为项目方向推进——工程上仍应以稳定的 runtime API 为准。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;机制&lt;/strong&gt;：&lt;code&gt;Proxy&lt;/code&gt;（对象）与 getter/setter（ref）在读时 &lt;code&gt;track&lt;/code&gt;、写时 &lt;code&gt;trigger&lt;/code&gt;；订阅表概念上是 &lt;code&gt;WeakMap → Map → Set&lt;/code&gt;；&lt;code&gt;activeEffect&lt;/code&gt; 把“当前执行的副作用”挂到读路径上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选型&lt;/strong&gt;：日常优先 &lt;code&gt;ref&lt;/code&gt;；对象图用 &lt;code&gt;reactive&lt;/code&gt; 但警惕身份与解构；大对象/外部库用 &lt;code&gt;shallowRef&lt;/code&gt;/&lt;code&gt;markRaw&lt;/code&gt;；对外暴露用 &lt;code&gt;readonly&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;副作用&lt;/strong&gt;：派生用 &lt;code&gt;computed&lt;/code&gt;；带新旧值与精确 source 用 &lt;code&gt;watch&lt;/code&gt;；自动依赖用 &lt;code&gt;watchEffect&lt;/code&gt;；注意 &lt;code&gt;flush&lt;/code&gt;、批量更新与 &lt;code&gt;nextTick&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生命周期&lt;/strong&gt;：同步创建的 watcher 随组件卸载；异步创建必须手动 &lt;code&gt;stop&lt;/code&gt;，请求类副作用要 &lt;code&gt;onCleanup&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;排错&lt;/strong&gt;：先问“这次读写有没有经过 trap？依赖是不是期望的那一个 key？触发的是替换引用还是原地 mutation？”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;理解 track/trigger 之后，很多“玄学不更新”会变成可定位的契约问题，而不是框架黑盒。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://vuejs.org/guide/extras/reactivity-in-depth.html" target="_blank" rel="noopener"
 &gt;Vue.js — Reactivity in Depth&lt;/a&gt;（机制伪代码、WeakMap 依赖结构、调试钩子、外部状态集成）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://vuejs.org/guide/essentials/reactivity-fundamentals.html" target="_blank" rel="noopener"
 &gt;Vue.js — Reactivity Fundamentals&lt;/a&gt;（ref/reactive、深响应、Proxy 与原对象、DOM 更新时序）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://vuejs.org/guide/essentials/watchers.html" target="_blank" rel="noopener"
 &gt;Vue.js — Watchers&lt;/a&gt;（watch/watchEffect、deep/immediate、flush、停止与泄漏）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://vuejs.org/api/reactivity-core.html" target="_blank" rel="noopener"
 &gt;Vue.js — Reactivity API: Core&lt;/a&gt;（ref/computed/reactive/readonly/watch* 类型与行为）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://vuejs.org/api/reactivity-advanced.html" target="_blank" rel="noopener"
 &gt;Vue.js — Reactivity API: Advanced&lt;/a&gt;（shallowRef、triggerRef、customRef、toRaw、markRaw、effectScope）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" target="_blank" rel="noopener"
 &gt;MDN — Proxy&lt;/a&gt;（属性访问拦截的语言基础）&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Vite 8.1 升级实战：Rolldown、Node 版本与前端构建链检查清单</title><link>https://blog.waihost.com/posts/vite-8-1-upgrade-guide/</link><pubDate>Mon, 06 Jul 2026 22:28:04 +0800</pubDate><guid>https://blog.waihost.com/posts/vite-8-1-upgrade-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/vite-8-1-upgrade-guide.svg" alt="Featured image of post Vite 8.1 升级实战：Rolldown、Node 版本与前端构建链检查清单" /&gt;&lt;p&gt;Vite 8 已经进入可用于日常升级评估的 8.1.x 分支。根据 Vite 官方发布记录，&lt;code&gt;v8.1.0&lt;/code&gt; 于 2026-06-23 发布，随后 &lt;code&gt;v8.1.1&lt;/code&gt;、&lt;code&gt;v8.1.2&lt;/code&gt; 与 &lt;code&gt;v8.1.3&lt;/code&gt; 在 6 月底到 7 月初持续修复构建、CSS、SSR、依赖扫描等问题；npm registry 中当前 &lt;code&gt;vite@latest&lt;/code&gt; 指向 &lt;code&gt;8.1.3&lt;/code&gt;，并明确要求 Node.js 版本满足 &lt;code&gt;^20.19.0 || &amp;gt;=22.12.0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这篇文章不是简单翻译 changelog，而是把官方迁移文档、GitHub Release/CHANGELOG 与 npm 包元数据合并成一份适合团队落地的升级指南：哪些变化真正会影响项目，哪些配置应该提前检查，以及如何用最小风险把 Vite 7 或早期 Vite 8 项目推进到 8.1.x。&lt;/p&gt;
&lt;h2 id="为什么-vite-81-值得关注"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88-vite-81-%e5%80%bc%e5%be%97%e5%85%b3%e6%b3%a8" class="header-anchor"&gt;&lt;/a&gt;为什么 Vite 8.1 值得关注
&lt;/h2&gt;&lt;p&gt;Vite 8 的核心变化不是“又一个小版本”，而是构建链底层发生了明显转向：官方迁移文档写明，Vite 8 使用 Rolldown 与 Oxc 相关工具替代过去更常见的 esbuild/Rollup 组合。对普通业务项目来说，这通常意味着更快的依赖预构建与生产构建潜力；对有复杂插件、SSR、自定义 Rollup 配置或 monorepo 的项目来说，也意味着需要更认真地验证边界行为。&lt;/p&gt;
&lt;p&gt;Vite 官方的版本策略也说明，当前常规补丁发布集中在 &lt;code&gt;vite@8.1&lt;/code&gt;；重要修复和安全补丁会回补到 &lt;code&gt;vite@7.3&lt;/code&gt; 与 &lt;code&gt;vite@8.0&lt;/code&gt;，安全补丁还会回补到 &lt;code&gt;vite@6.4&lt;/code&gt;。如果项目仍停留在更早版本，虽然短期内可能还能运行，但已经不适合作为长期维护基线。&lt;/p&gt;
&lt;h2 id="升级前先确认-nodejs-版本"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%89%8d%e5%85%88%e7%a1%ae%e8%ae%a4-nodejs-%e7%89%88%e6%9c%ac" class="header-anchor"&gt;&lt;/a&gt;升级前先确认 Node.js 版本
&lt;/h2&gt;&lt;p&gt;npm 元数据给出的 &lt;code&gt;vite@8.1.3&lt;/code&gt; engines 为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;node&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;^20.19.0 || &amp;gt;=22.12.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这意味着团队不要只写“Node 20+”这么粗略的要求。CI、Dockerfile、开发机版本管理工具都应该精确到小版本，至少满足 Node 20.19.0，或者升级到 22.12.0 及以上。&lt;/p&gt;
&lt;p&gt;建议先在项目根目录执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm ls vite
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm view vite version engines
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果项目使用 pnpm 或 yarn，也建议同步检查 lockfile 与 CI 缓存策略：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm why vite
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm env use --global &lt;span class="m"&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 或在 CI 镜像中固定：node:22.12-bookworm / node:22-bookworm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对生产构建来说，Node 版本不一致是最常见的“本地能过、CI 失败”来源之一。升级 Vite 前先统一 Node，能减少很多无效排查。&lt;/p&gt;
&lt;h2 id="核心变化一rolldown-成为默认构建基础"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8f%98%e5%8c%96%e4%b8%80rolldown-%e6%88%90%e4%b8%ba%e9%bb%98%e8%ae%a4%e6%9e%84%e5%bb%ba%e5%9f%ba%e7%a1%80" class="header-anchor"&gt;&lt;/a&gt;核心变化一：Rolldown 成为默认构建基础
&lt;/h2&gt;&lt;p&gt;官方迁移文档提到，Vite 8 使用 Rolldown 和 Oxc based tools。对于多数应用项目，&lt;code&gt;vite build&lt;/code&gt; 命令仍然是同一个命令，但底层打包器行为可能影响以下场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;依赖预构建、动态导入、chunk 拆分结果；&lt;/li&gt;
&lt;li&gt;自定义插件中对 Rollup hook 返回值、sourcemap、虚拟模块 ID 的假设；&lt;/li&gt;
&lt;li&gt;SSR 构建时的错误堆栈、external 模块处理；&lt;/li&gt;
&lt;li&gt;monorepo 中 pnpm workspace、软链接、&lt;code&gt;.modules.yaml&lt;/code&gt; 解析。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vite 8.1.x 的 changelog 正好体现了这些修复重点：&lt;code&gt;8.1.1&lt;/code&gt; 修复了 bundled dev 下 HMR/lazy compile 产物服务、&lt;code&gt;import.meta.hot.invalidate()&lt;/code&gt; 栈溢出、CSS/Sass &lt;code&gt;@import&lt;/code&gt; 中 tsconfig paths 解析、pnpm workspace 根目录解析等问题；&lt;code&gt;8.1.3&lt;/code&gt; 又修复了嵌套动态导入的 CSS preload、SSR 首行 stacktrace 列位置等问题。&lt;/p&gt;
&lt;p&gt;因此，如果你的项目有复杂插件或 SSR，不建议直接从旧版本跳到 8.1 后立即上线，而应该先建立一组构建回归用例。&lt;/p&gt;
&lt;h2 id="核心变化二默认浏览器目标更新"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8f%98%e5%8c%96%e4%ba%8c%e9%bb%98%e8%ae%a4%e6%b5%8f%e8%a7%88%e5%99%a8%e7%9b%ae%e6%a0%87%e6%9b%b4%e6%96%b0" class="header-anchor"&gt;&lt;/a&gt;核心变化二：默认浏览器目标更新
&lt;/h2&gt;&lt;p&gt;Vite 迁移文档说明，&lt;code&gt;build.target&lt;/code&gt; 的默认值 &lt;code&gt;baseline-widely-available&lt;/code&gt; 对应的浏览器版本更新为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Chrome 111
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Edge 111
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firefox 114
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Safari 16.4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这组版本对应 2026-01-01 的 Baseline Widely Available 特性集合。实际影响是：如果你的用户仍包含更老的浏览器，尤其是企业内网老 Safari、嵌入式 WebView 或长期不更新的 Chromium 内核，就不能完全依赖默认值。&lt;/p&gt;
&lt;p&gt;保守项目可以显式设置构建目标：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// vite.config.ts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vite&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;chrome107&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;edge107&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;firefox104&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;safari16&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但从长期维护角度看，建议先用访问日志或前端监控确认真实用户浏览器分布。如果老版本浏览器占比极低，跟随默认 baseline 通常能换来更少 polyfill、更现代的输出和更简单的调试体验。&lt;/p&gt;
&lt;h2 id="核心变化三依赖优化与-css-处理更需要回归"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8f%98%e5%8c%96%e4%b8%89%e4%be%9d%e8%b5%96%e4%bc%98%e5%8c%96%e4%b8%8e-css-%e5%a4%84%e7%90%86%e6%9b%b4%e9%9c%80%e8%a6%81%e5%9b%9e%e5%bd%92" class="header-anchor"&gt;&lt;/a&gt;核心变化三：依赖优化与 CSS 处理更需要回归
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;vite@8.1.3&lt;/code&gt; 的依赖列表包含 &lt;code&gt;rolldown&lt;/code&gt;、&lt;code&gt;lightningcss&lt;/code&gt;、&lt;code&gt;postcss&lt;/code&gt;、&lt;code&gt;picomatch&lt;/code&gt;、&lt;code&gt;tinyglobby&lt;/code&gt; 等。CHANGELOG 中也有多项与 CSS、依赖扫描相关的修复，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inlined CSS 注入时避开 shebang 行；&lt;/li&gt;
&lt;li&gt;嵌套动态导入场景下预加载 CSS；&lt;/li&gt;
&lt;li&gt;使用 lightningcss 时保留 external &lt;code&gt;@import&lt;/code&gt; URL 中的 &lt;code&gt;$&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;CSS 与 Sass &lt;code&gt;@import&lt;/code&gt; 支持解析 tsconfig paths；&lt;/li&gt;
&lt;li&gt;scanner 忽略 &lt;code&gt;ERR_CLOSED_SERVER&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;glob/HMR 匹配支持 &lt;code&gt;caseSensitive&lt;/code&gt; 选项。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这提示我们，升级验证不能只看首页能不能打开，而要覆盖动态路由、懒加载组件、CSS Modules、Sass/Less、别名路径、SSR 页面和生产构建产物。&lt;/p&gt;
&lt;p&gt;一个实用的检查顺序如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 清理旧缓存，避免被 node_modules/.vite 误导&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf node_modules/.vite dist
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 安装并锁定 Vite 版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm add -D vite@8.1.3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 类型检查、单测、构建&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm typecheck
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 4. 本地预览生产产物&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm vite preview --host 0.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果是 monorepo，建议在根目录和子包目录分别验证一次，重点看 workspace 依赖是否被正确解析。&lt;/p&gt;
&lt;h2 id="插件与配置迁移建议"&gt;&lt;a href="#%e6%8f%92%e4%bb%b6%e4%b8%8e%e9%85%8d%e7%bd%ae%e8%bf%81%e7%a7%bb%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;插件与配置迁移建议
&lt;/h2&gt;&lt;p&gt;从 Vite 7 升级时，优先检查下面几类配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/src&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 检查 hmr/ws 相关配置是否仍符合迁移文档
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sourcemap&lt;/span&gt;: &lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;rollupOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 如果这里依赖 Rollup 特定行为，需要重点回归
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果团队之前已经试用过 &lt;code&gt;rolldown-vite&lt;/code&gt;，官方迁移文档建议可把它作为过渡经验参考；迁移到 Vite 8 时，需要撤销 package.json 中对 &lt;code&gt;npm:rolldown-vite@...&lt;/code&gt; 的别名，改回正式的 &lt;code&gt;vite&lt;/code&gt; 依赖。&lt;/p&gt;
&lt;p&gt;插件方面，内部插件或低频维护插件最容易踩坑。建议临时打开 sourcemap，构建后抽查：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm build --debug
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果发现插件 hook 返回值、虚拟模块 ID、动态导入分析异常，应先升级插件版本；仍无法解决时，再考虑在 Vite issue 或插件仓库中查找是否已有兼容性修复。&lt;/p&gt;
&lt;h2 id="推荐的团队升级流程"&gt;&lt;a href="#%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%a2%e9%98%9f%e5%8d%87%e7%ba%a7%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;推荐的团队升级流程
&lt;/h2&gt;&lt;p&gt;对于生产项目，推荐按下面节奏推进：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;新建升级分支，只做 Node 与 Vite 相关变更；&lt;/li&gt;
&lt;li&gt;固定 Node.js 到 &lt;code&gt;20.19.0+&lt;/code&gt; 或 &lt;code&gt;22.12.0+&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;升级 Vite 到 &lt;code&gt;8.1.3&lt;/code&gt;，同步升级官方框架插件；&lt;/li&gt;
&lt;li&gt;清理缓存后执行类型检查、单测、生产构建；&lt;/li&gt;
&lt;li&gt;对动态导入、CSS、SSR、monorepo 子包做专项回归；&lt;/li&gt;
&lt;li&gt;在测试环境跑一轮真实流量或关键页面巡检；&lt;/li&gt;
&lt;li&gt;合并前保留回滚方案：锁回旧 Vite 版本与旧 lockfile。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果项目规模较大，可以先升级到官方仍支持补丁的 Vite 7.3，再评估 Vite 8.1。这样可以把“安全维护”和“构建链迁移”分成两个风险更小的步骤。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;Vite 8.1.x 的重点不是语法糖，而是前端构建底座切换后的稳定化：Rolldown/Oxc 成为核心、默认浏览器目标更新、Node.js 最低版本抬高，同时围绕 CSS、SSR、动态导入和 monorepo 持续修复。对新项目来说，直接从 &lt;code&gt;vite@8.1.3&lt;/code&gt; 起步是合理选择；对存量项目来说，升级前先统一 Node 版本，再用构建、预览、SSR、CSS 与插件回归清单逐项验证，才是更稳妥的做法。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Vite GitHub Release：&lt;a class="link" href="https://github.com/vitejs/vite/releases/tag/v8.1.3" target="_blank" rel="noopener"
 &gt;https://github.com/vitejs/vite/releases/tag/v8.1.3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vite CHANGELOG：&lt;a class="link" href="https://github.com/vitejs/vite/blob/v8.1.3/packages/vite/CHANGELOG.md" target="_blank" rel="noopener"
 &gt;https://github.com/vitejs/vite/blob/v8.1.3/packages/vite/CHANGELOG.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vite 官方迁移文档：&lt;a class="link" href="https://vite.dev/guide/migration" target="_blank" rel="noopener"
 &gt;https://vite.dev/guide/migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vite 官方版本策略：&lt;a class="link" href="https://vite.dev/releases" target="_blank" rel="noopener"
 &gt;https://vite.dev/releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;npm registry &lt;code&gt;vite@latest&lt;/code&gt; 元数据：&lt;a class="link" href="https://registry.npmjs.org/vite/latest" target="_blank" rel="noopener"
 &gt;https://registry.npmjs.org/vite/latest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>2025 前端面试题大全</title><link>https://blog.waihost.com/posts/frontend-interview-2025/</link><pubDate>Thu, 25 Jun 2026 10:00:00 +0000</pubDate><guid>https://blog.waihost.com/posts/frontend-interview-2025/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/frontend-interview-2025.svg" alt="Featured image of post 2025 前端面试题大全" /&gt;&lt;h2 id="前言"&gt;&lt;a href="#%e5%89%8d%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;前言
&lt;/h2&gt;&lt;p&gt;2025 年前端技术持续演进，面试题目也在不断更新。本文整理了最新的前端面试高频题目，涵盖 JavaScript 基础、框架原理、性能优化、工程化等核心领域，帮助大家系统备战面试。&lt;/p&gt;
&lt;h2 id="一javascript-基础"&gt;&lt;a href="#%e4%b8%80javascript-%e5%9f%ba%e7%a1%80" class="header-anchor"&gt;&lt;/a&gt;一、JavaScript 基础
&lt;/h2&gt;&lt;h3 id="11-闭包的理解"&gt;&lt;a href="#11-%e9%97%ad%e5%8c%85%e7%9a%84%e7%90%86%e8%a7%a3" class="header-anchor"&gt;&lt;/a&gt;1.1 闭包的理解
&lt;/h3&gt;&lt;p&gt;闭包是指函数能够访问其词法作用域中变量的能力，即使该函数在其词法作用域之外执行。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createCounter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;闭包的常见应用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据封装和私有变量&lt;/li&gt;
&lt;li&gt;函数工厂&lt;/li&gt;
&lt;li&gt;回调函数和事件处理&lt;/li&gt;
&lt;li&gt;柯里化（Currying）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="12-原型链与继承"&gt;&lt;a href="#12-%e5%8e%9f%e5%9e%8b%e9%93%be%e4%b8%8e%e7%bb%a7%e6%89%bf" class="header-anchor"&gt;&lt;/a&gt;1.2 原型链与继承
&lt;/h3&gt;&lt;p&gt;每个对象都有一个 &lt;code&gt;__proto__&lt;/code&gt; 指向其构造函数的 &lt;code&gt;prototype&lt;/code&gt;，形成原型链。当访问对象属性时，会沿着原型链向上查找。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; makes a noise.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;constructor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="13-promise-与-asyncawait"&gt;&lt;a href="#13-promise-%e4%b8%8e-asyncawait" class="header-anchor"&gt;&lt;/a&gt;1.3 Promise 与 async/await
&lt;/h3&gt;&lt;p&gt;Promise 是异步编程的解决方案，async/await 是其语法糖。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/api/data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;请求失败:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;手写 Promise.all 的核心思路：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;promiseAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="二css-核心知识"&gt;&lt;a href="#%e4%ba%8ccss-%e6%a0%b8%e5%bf%83%e7%9f%a5%e8%af%86" class="header-anchor"&gt;&lt;/a&gt;二、CSS 核心知识
&lt;/h2&gt;&lt;h3 id="21-bfc块级格式化上下文"&gt;&lt;a href="#21-bfc%e5%9d%97%e7%ba%a7%e6%a0%bc%e5%bc%8f%e5%8c%96%e4%b8%8a%e4%b8%8b%e6%96%87" class="header-anchor"&gt;&lt;/a&gt;2.1 BFC（块级格式化上下文）
&lt;/h3&gt;&lt;p&gt;BFC 是一个独立的渲染区域，内部元素的布局不会影响外部。触发条件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;overflow&lt;/code&gt; 不为 &lt;code&gt;visible&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;display&lt;/code&gt; 为 &lt;code&gt;flex&lt;/code&gt;、&lt;code&gt;grid&lt;/code&gt;、&lt;code&gt;inline-block&lt;/code&gt; 等&lt;/li&gt;
&lt;li&gt;&lt;code&gt;position&lt;/code&gt; 为 &lt;code&gt;absolute&lt;/code&gt; 或 &lt;code&gt;fixed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;float&lt;/code&gt; 不为 &lt;code&gt;none&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="22-flex-布局"&gt;&lt;a href="#22-flex-%e5%b8%83%e5%b1%80" class="header-anchor"&gt;&lt;/a&gt;2.2 Flex 布局
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="23-css-变量与主题切换"&gt;&lt;a href="#23-css-%e5%8f%98%e9%87%8f%e4%b8%8e%e4%b8%bb%e9%a2%98%e5%88%87%e6%8d%a2" class="header-anchor"&gt;&lt;/a&gt;2.3 CSS 变量与主题切换
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--primary-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#1890ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--bg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dark&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--primary-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#177ddc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--bg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#141414&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="三vue-面试题"&gt;&lt;a href="#%e4%b8%89vue-%e9%9d%a2%e8%af%95%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;三、Vue 面试题
&lt;/h2&gt;&lt;h3 id="31-vue-3-响应式原理"&gt;&lt;a href="#31-vue-3-%e5%93%8d%e5%ba%94%e5%bc%8f%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;3.1 Vue 3 响应式原理
&lt;/h3&gt;&lt;p&gt;Vue 3 使用 &lt;code&gt;Proxy&lt;/code&gt; 替代了 Vue 2 的 &lt;code&gt;Object.defineProperty&lt;/code&gt;，解决了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无法检测对象属性的添加和删除&lt;/li&gt;
&lt;li&gt;无法检测数组索引和长度的变化&lt;/li&gt;
&lt;li&gt;性能更好，不需要递归遍历所有属性&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 依赖收集
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 触发更新
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="32-vue-3-composition-api-优势"&gt;&lt;a href="#32-vue-3-composition-api-%e4%bc%98%e5%8a%bf" class="header-anchor"&gt;&lt;/a&gt;3.2 Vue 3 Composition API 优势
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;更好的逻辑复用（Composables）&lt;/li&gt;
&lt;li&gt;更灵活的代码组织&lt;/li&gt;
&lt;li&gt;更好的类型推导&lt;/li&gt;
&lt;li&gt;更小的打包体积（tree-shaking）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="33-虚拟-dom-与-diff-算法"&gt;&lt;a href="#33-%e8%99%9a%e6%8b%9f-dom-%e4%b8%8e-diff-%e7%ae%97%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;3.3 虚拟 DOM 与 Diff 算法
&lt;/h3&gt;&lt;p&gt;Vue 的 Diff 算法核心策略：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同层比较，不跨层级&lt;/li&gt;
&lt;li&gt;同节点类型才进行比较&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;key&lt;/code&gt; 优化列表对比&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vue 3 引入了最长递增子序列算法优化节点移动。&lt;/p&gt;
&lt;h2 id="四react-面试题"&gt;&lt;a href="#%e5%9b%9breact-%e9%9d%a2%e8%af%95%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;四、React 面试题
&lt;/h2&gt;&lt;h3 id="41-react-fiber-架构"&gt;&lt;a href="#41-react-fiber-%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;4.1 React Fiber 架构
&lt;/h3&gt;&lt;p&gt;Fiber 是 React 16 引入的协调引擎，核心特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可中断渲染&lt;/strong&gt;：将渲染工作拆分为小单元&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先级调度&lt;/strong&gt;：高优先级更新可以打断低优先级&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;双缓存树&lt;/strong&gt;：current 树和 workInProgress 树&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="42-hooks-原理"&gt;&lt;a href="#42-hooks-%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;4.2 Hooks 原理
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 简化版 useState 实现
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hookStates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hookIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hookStates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hookIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hookStates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hookIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hookIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hookStates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 触发重新渲染
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hookStates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hookIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="43-react-性能优化"&gt;&lt;a href="#43-react-%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;4.3 React 性能优化
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;React.memo&lt;/code&gt; / &lt;code&gt;useMemo&lt;/code&gt; / &lt;code&gt;useCallback&lt;/code&gt; 避免不必要的渲染&lt;/li&gt;
&lt;li&gt;虚拟列表优化长列表&lt;/li&gt;
&lt;li&gt;代码分割与懒加载&lt;/li&gt;
&lt;li&gt;状态下沉，减少不必要的全局状态&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="五工程化与构建工具"&gt;&lt;a href="#%e4%ba%94%e5%b7%a5%e7%a8%8b%e5%8c%96%e4%b8%8e%e6%9e%84%e5%bb%ba%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;五、工程化与构建工具
&lt;/h2&gt;&lt;h3 id="51-vite-vs-webpack"&gt;&lt;a href="#51-vite-vs-webpack" class="header-anchor"&gt;&lt;/a&gt;5.1 Vite vs Webpack
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;特性&lt;/th&gt;
					&lt;th&gt;Vite&lt;/th&gt;
					&lt;th&gt;Webpack&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;开发启动&lt;/td&gt;
					&lt;td&gt;极快（ESM 原生加载）&lt;/td&gt;
					&lt;td&gt;较慢（打包后启动）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;热更新&lt;/td&gt;
					&lt;td&gt;毫秒级&lt;/td&gt;
					&lt;td&gt;秒级&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;生产构建&lt;/td&gt;
					&lt;td&gt;Rollup&lt;/td&gt;
					&lt;td&gt;自身打包&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;配置复杂度&lt;/td&gt;
					&lt;td&gt;低&lt;/td&gt;
					&lt;td&gt;高&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="52-模块化方案"&gt;&lt;a href="#52-%e6%a8%a1%e5%9d%97%e5%8c%96%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;5.2 模块化方案
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ESM&lt;/strong&gt;：标准模块方案，静态分析&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CJS&lt;/strong&gt;：Node.js 模块方案，运行时加载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UMD&lt;/strong&gt;：兼容多种模块系统&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AMD&lt;/strong&gt;：异步模块定义&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="53-monorepo-方案"&gt;&lt;a href="#53-monorepo-%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;5.3 Monorepo 方案
&lt;/h3&gt;&lt;p&gt;2025 年主流 Monorepo 工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pnpm workspace&lt;/strong&gt;：高效磁盘利用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Turborepo&lt;/strong&gt;：构建缓存与并行执行&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nx&lt;/strong&gt;：增量构建与依赖图分析&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="六性能优化"&gt;&lt;a href="#%e5%85%ad%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;六、性能优化
&lt;/h2&gt;&lt;h3 id="61-首屏性能优化"&gt;&lt;a href="#61-%e9%a6%96%e5%b1%8f%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;6.1 首屏性能优化
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;SSR / SSG 服务端渲染&lt;/li&gt;
&lt;li&gt;关键资源预加载（&lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;图片懒加载与现代格式（WebP、AVIF）&lt;/li&gt;
&lt;li&gt;代码分割与动态导入&lt;/li&gt;
&lt;li&gt;CDN 加速&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="62-运行时性能优化"&gt;&lt;a href="#62-%e8%bf%90%e8%a1%8c%e6%97%b6%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;6.2 运行时性能优化
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;防抖与节流&lt;/li&gt;
&lt;li&gt;Web Worker 处理耗时计算&lt;/li&gt;
&lt;li&gt;requestAnimationFrame 优化动画&lt;/li&gt;
&lt;li&gt;虚拟滚动优化长列表&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="63-core-web-vitals"&gt;&lt;a href="#63-core-web-vitals" class="header-anchor"&gt;&lt;/a&gt;6.3 Core Web Vitals
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LCP（Largest Contentful Paint）&lt;/strong&gt;：&amp;lt; 2.5s&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;INP（Interaction to Next Paint）&lt;/strong&gt;：&amp;lt; 200ms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CLS（Cumulative Layout Shift）&lt;/strong&gt;：&amp;lt; 0.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="七typescript"&gt;&lt;a href="#%e4%b8%83typescript" class="header-anchor"&gt;&lt;/a&gt;七、TypeScript
&lt;/h2&gt;&lt;h3 id="71-高级类型工具"&gt;&lt;a href="#71-%e9%ab%98%e7%ba%a7%e7%b1%bb%e5%9e%8b%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;7.1 高级类型工具
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 条件类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kr"&gt;extends&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 映射类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Readonly&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;readonly&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 模板字面量类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;EventName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`on&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Capitalize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="72-typescript-体操"&gt;&lt;a href="#72-typescript-%e4%bd%93%e6%93%8d" class="header-anchor"&gt;&lt;/a&gt;7.2 TypeScript 体操
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 实现 DeepPartial
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DeepPartial&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kr"&gt;extends&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;DeepPartial&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;T&lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;P&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 实现 ReturnType
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MyReturnType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kr"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;: &lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt; : &lt;span class="kt"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="八网络与安全"&gt;&lt;a href="#%e5%85%ab%e7%bd%91%e7%bb%9c%e4%b8%8e%e5%ae%89%e5%85%a8" class="header-anchor"&gt;&lt;/a&gt;八、网络与安全
&lt;/h2&gt;&lt;h3 id="81-http3-与-quic"&gt;&lt;a href="#81-http3-%e4%b8%8e-quic" class="header-anchor"&gt;&lt;/a&gt;8.1 HTTP/3 与 QUIC
&lt;/h3&gt;&lt;p&gt;HTTP/3 基于 QUIC 协议，优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0-RTT 连接建立&lt;/li&gt;
&lt;li&gt;多路复用无队头阻塞&lt;/li&gt;
&lt;li&gt;连接迁移（网络切换不断连）&lt;/li&gt;
&lt;li&gt;内置 TLS 1.3&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="82-前端安全"&gt;&lt;a href="#82-%e5%89%8d%e7%ab%af%e5%ae%89%e5%85%a8" class="header-anchor"&gt;&lt;/a&gt;8.2 前端安全
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;XSS 防护&lt;/strong&gt;：内容安全策略（CSP）、输入过滤、输出编码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSRF 防护&lt;/strong&gt;：SameSite Cookie、Token 验证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;点击劫持&lt;/strong&gt;：X-Frame-Options、CSP frame-ancestors&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;2025 年前端面试依然重视基础知识，同时对 TypeScript、性能优化、工程化能力的要求更高。建议系统学习原理，结合实际项目经验，才能在面试中脱颖而出。&lt;/p&gt;</description></item></channel></rss>