🔧 卷⑤ 构建工具与框架
20 条。适用场景:构建配置、依赖升级、框架选型。
「要点」「我的补充」为本人撰写,非原文摘录。这一卷与这个博客本身关系最紧,踩过的坑最多。
一、Vite:演进脉络
81. Announcing Vite 8.1
来源:Vite 官方博客 · vite.dev 读它解决什么:了解当前 Vite 小版本带来的改进与注意事项。
要点
- 小版本通常包含性能改进与实验特性的推进,破坏性变更集中在大版本
- Vite 的核心架构是 dev 用原生 ESM 按需编译、build 用打包器,两个模式行为可能有差异
- 版本公告会列出弃用项,这是升级前的必读部分
我的补充:Vite 的一个长期陷阱是 dev 与 build 行为不一致——dev 下不打包、依赖走 esbuild 预构建,build 走 Rollup(新版本转向 Rolldown)。所以"本地开发正常但构建后出错"是常见现象,典型原因是 CommonJS 依赖的互操作、import.meta 用法、以及 SSR 时访问了浏览器全局对象。规矩是每次提交前至少跑一次 build,不要只依赖 dev。
82. Announcing Vite 8
来源:Vite 官方博客 · vite.dev 读它解决什么:判断 8.0 的破坏性变更影响自己项目多少。
要点
- 大版本升级要重点看:最低 Node 版本要求、默认打包器变更、插件 API 变更、默认构建目标
- 插件生态的兼容性通常是主要阻力,尤其小众插件
- 官方通常提供迁移指南
我的补充:升级 Vite 的检查顺序:先看 Node 版本要求 → 再逐个查 vite.config.ts 里用的插件是否已支持新版 → 最后跑 build 对比产物。产物对比很值得做:把升级前的 dist 存一份,升级后 diff -rq 看变化是否符合预期,页面数量、关键资源是否都在。我在重构这个博客时就是这么做的,能抓到静默的行为变化。
83. Announcing Vite 8 Beta
来源:Vite 官方博客 · vite.dev 读它解决什么:正式版之前了解方向,提前评估。
要点
- Beta 阶段主要变更已确定,适合在分支上试跑
- 插件作者应在此阶段完成适配
- 使用者可以据此判断升级窗口
我的补充:如果你的项目依赖较多社区插件,Beta 期就该试一遍,把不兼容的插件列出来。常见的处理选项:等上游更新、找替代插件、或者用 overrides/resolutions 临时锁版本。VitePress 这类上层框架通常会滞后于 Vite 主线,所以用 VitePress 的项目不应该单独升 Vite,等 VitePress 跟进即可。
84. Announcing Vite 7
来源:Vite 官方博客 · vite.dev 读它解决什么:了解 7 这一代的定位,很多项目还在这个版本。
要点
- 关注默认构建目标(
build.target)的变化,它直接影响产物的语法降级程度和体积 - Node 版本要求提升通常伴随大版本
- 环境 API(Environment API)这类架构性改动为 SSR 与多运行时打基础
我的补充:build.target 值得专门确认一次——默认目标偏新时,老浏览器上会直接语法报错白屏,而这类问题在开发机上永远看不到。查现有产物的语法级别可以用 es-check。如果需要支持较老的环境,显式设 build.target: 'es2018' 之类,或者用 @vitejs/plugin-legacy(代价是产物变大)。
85. Announcing Vite 6
来源:Vite 官方博客 · vite.dev 读它解决什么:了解 Environment API 的引入背景,它影响 SSR 类框架。
要点
- Environment API 让 Vite 能统一描述多个运行环境(浏览器、Node SSR、边缘运行时)
- 这对部署到 Cloudflare Workers、Deno 这类非 Node 环境的框架很关键
- 对普通应用开发者透明,但会影响上层框架的能力
我的补充:这条与这个博客的技术栈相关——部署到 Cloudflare Pages 时,Functions 跑在 Workers 运行时而不是 Node,没有 fs、path 这些模块,全局对象也不同。所以 functions/ 目录里的代码不能随便 import 前端侧的模块。我在重构时保留了 functions/api/images.js 里独立的一份壁纸列表,就是因为跨运行时 import 不干净,这是有意的重复。
86. Announcing Vite 5
来源:Vite 官方博客 · vite.dev 读它解决什么:了解 5 这一代的变化,作为历史升级路径的参考。
要点
- 每代都在收紧默认配置与提升底层依赖版本
- CSS 处理、静态资源处理的默认行为在版本间有过调整
- 老项目跨多个大版本升级时,建议逐版本升而不是一步跳
我的补充:跨多个大版本时逐级升级是稳妥做法:5 → 6 → 7 → 8,每级跑一次 build 和测试。一步跳的问题是出错时无法定位是哪一级引入的。每级之间提交一次,出问题好回退。这个原则对所有基础依赖都适用(Node、TypeScript、框架本身)。
87. Announcing Vite 5.1
来源:Vite 官方博客 · vite.dev 读它解决什么:了解 Runtime API 等实验能力的早期形态。
要点
- 小版本里的实验特性是后续大版本能力的前身,看它能预判方向
- 实验 API 不保证稳定,生产不应依赖
- 对框架作者价值更大
我的补充:对应用开发者的实用建议:vite.config.ts 里别用带 experimental 前缀的选项,升级时它们可能改名或消失。如果确实需要某个实验能力,在配置里写注释说明为什么用、什么条件下可以移除——半年后你自己也想不起来。
88. Announcing Vite 4.3
来源:Vite 官方博客 · vite.dev 读它解决什么:了解一次以性能为主题的版本更新做了什么。
要点
- 这个版本的重点是启动与热更新速度优化
- 优化手段包括减少不必要的文件解析、改进依赖预构建的缓存
- 性能优化型版本通常无破坏性,可以放心升
我的补充:如果你的 dev 启动慢,先排查几个常见原因:node_modules 太大导致预构建耗时(首次慢、之后走缓存)、barrel 文件(index.ts 里 re-export 一堆东西)导致过度解析、以及 optimizeDeps 配置不当。清预构建缓存的方法是删 node_modules/.vite,遇到诡异的依赖问题先试这一步,很多"玄学问题"就是缓存。
89. Announcing Vite 4
来源:Vite 官方博客 · vite.dev 读它解决什么:了解 Vite 4 时代的基线能力。
要点
- 这一代确立了 Rollup 3 作为构建基础
- 各框架模板(Vue、React、Svelte 等)的官方支持在此时较为完整
- 对遗留项目,了解它有助于评估升级跨度
我的补充:维护老项目时先确认当前 Vite 版本与对应的 Rollup 版本,因为 Rollup 插件的兼容性是按 Rollup 版本算的。pnpm why rollup 能看实际解析到的版本。如果项目里同时有 Rollup 插件和 Vite 插件,版本冲突是常见问题来源。
90. Cloudflare supports Vite's mission
来源:Vite 官方博客 · vite.dev 读它解决什么:了解 Vite 与边缘运行时生态的结合方向。
要点
- 构建工具与部署平台的协作让"本地开发环境与生产运行时一致"成为可能
- 对部署到边缘的应用,本地能模拟真实运行时是很大的开发体验改善
- 生态投入也是选型时的稳定性参考
我的补充:实际收益是 wrangler dev 加上 Vite 插件后,本地就能在真实 Workers 运行时里跑 Functions,不必等部署到线上才发现 fs 不存在这类问题。这个博客的 functions/ 目录如果要继续开发,用这套本地调试会省很多时间。注意本地模拟的 R2、KV 绑定是内存或本地文件模拟的,行为与线上有细微差别。
二、Vue 与 VitePress
91. Announcing Vue 3.5
来源:Vue 官方博客 · blog.vuejs.org 读它解决什么:了解 3.5 的新能力,这是当前广泛使用的版本。
要点
- 响应式系统的内部优化带来内存与性能改进,对大型应用有实际收益
- 新增的组合式 API 能力简化了常见模式(如 props 解构保持响应性)
- SSR 相关改进对静态站生成有影响
我的补充:这个博客用的就是 Vue 3.5。一条与本站直接相关的踩坑:readonly() 包装的状态不能从外部赋值——写 state.isLoading = true 在开发环境会警告,生产环境静默失败。我在重构时发现天气组件的加载态从未生效,就是这个原因。composable 要暴露可写状态就该提供方法,而不是让调用方直接改。
92. Announcing Vue 3.4
来源:Vue 官方博客 · blog.vuejs.org 读它解决什么:了解 3.4 的编译器改进与破坏性调整。
要点
- 模板编译器的重写带来解析速度提升
defineModel等宏的稳定化改变了双向绑定的推荐写法- 部分废弃 API 在此版本移除
我的补充:defineModel() 是值得迁移的——替代了 props + emit('update:xxx') 的样板代码,可读性明显好。迁移时注意它要求 <script setup>。另外 Vue 的编译器宏(defineProps、defineEmits、defineModel)都是编译期处理的,不能在条件语句里调用,也不能从别处 import,这是新手常见困惑。
93. Announcing Vue 3.3
来源:Vue 官方博客 · blog.vuejs.org 读它解决什么:了解 3.3 在类型支持上的增强。
要点
- 泛型组件与更好的
defineProps类型推断是这一代的重点 - 支持从外部文件导入类型用于 props 声明,此前有限制
- 类型能力的增强直接影响 TS 项目的开发体验
我的补充:泛型组件(<script setup lang="ts" generic="T">)解决了列表类组件的类型传递问题——以前只能用 any 或复杂的类型断言。如果你在写组件库,这是必须知道的能力。注意类型只在编译期存在,运行时的 props 校验仍需要显式写,别指望 TS 类型能挡住运行时传错的数据。
94. Announcing VitePress 1.0
来源:Vue 官方博客 · blog.vuejs.org 读它解决什么:了解 VitePress 的定位与能力边界——这个博客就建在它上面。
要点
- VitePress 是基于 Vite 的静态站生成器,定位偏文档但可做博客
- 核心机制:Markdown 编译为 Vue 组件,构建时预渲染 HTML,客户端 hydration 后成为 SPA
- 主题系统允许深度定制,也支持在 Markdown 里直接写 Vue 组件
我的补充:几条实践经验。一是首屏内容是 SSR 渲染的,但依赖客户端数据的部分不是——这个博客的文章列表在构建产物里是空的 <ul>,靠 JS 填充,所以不能用"构建产物里搜不到"来判断功能是否正常。二是插件可能改写源文件:vitepress-plugin-auto-frontmatter 会把生成的 permalink 写回 Markdown,这些改动必须提交,否则每次 CI 构建都生成新 URL 导致链接失效。三是 _headers、_redirects 这类平台配置文件要放在 public/ 目录才会进构建产物根目录。
95. Vue 2 is Approaching End Of Life
来源:Vue 官方博客 · blog.vuejs.org 读它解决什么:仍在维护 Vue 2 项目的话,需要知道时间线与迁移选项。
要点
- EOL 意味着不再有安全修复,继续用的风险随时间上升
- 迁移路径有几条:升到 Vue 3、用商业延长支持、或保持现状承担风险
- 生态库也会逐步停止支持 Vue 2
我的补充:Vue 2 → 3 迁移的实际难点排序:选项式到组合式不是必须的(Vue 3 仍支持选项式),真正的阻力是第三方 UI 库(Element UI → Element Plus 之类要整体换)、以及依赖 Vue 2 响应式实现细节的代码(Vue.set、数组索引赋值)。评估工作量先跑一遍官方的迁移构建(@vue/compat),它会把不兼容点以警告形式列出来,比人工排查快得多。
三、周边工具链
96. React Compiler Linting Just Got a Rust-Native Speedup in Oxlint
来源:Frontend Masters · frontendmasters.com 读它解决什么:ESLint 在大项目上慢得难受,想知道 Rust 系工具的现状。
要点
- Oxlint 用 Rust 实现,速度比 ESLint 快一到两个数量级
- 规则覆盖度是主要考量:能否替换取决于你依赖哪些规则和插件
- 渐进策略是两者并行:Oxlint 跑快速检查,ESLint 跑它独有的规则
我的补充:在 CI 里加一道 Oxlint 作为快速失败关口很划算——几秒内就能挡掉明显问题,不必等完整的 ESLint 跑完。本地 pre-commit 钩子同理。完全替换 ESLint 目前对多数项目还不现实,尤其依赖框架专属插件(eslint-plugin-vue 之类)的项目。同类工具还有 Biome,它同时管 lint 和格式化。
97. React Server Components in TanStack
来源:Frontend Masters · frontendmasters.com 读它解决什么:理解服务端组件这个模型,即使你不用 React。
要点
- 服务端组件的核心思想是部分组件只在服务端执行,产出的是渲染结果而不是 JS bundle
- 收益是客户端 JS 变少;代价是心智模型复杂化(哪些代码在哪边跑、如何传数据)
- 这个模型正在被多个框架采纳,不限于 React
我的补充:这个模型的本质是回归"服务端渲染 + 局部交互",而这正是静态站 + 岛屿架构(Islands)在做的事。VitePress、Astro 走的是同一方向的另一条路:默认静态,需要交互的部分才 hydrate。对内容型站点,静态生成通常比服务端组件更简单也更快——没有服务端运行时,CDN 直接命中。选型时先问:内容是否需要按请求动态生成,不需要就别引入这层复杂度。
98. The Fundamentals and Dev Experience of CSS @function
来源:Frontend Masters · frontendmasters.com 读它解决什么:CSS 有了自定义函数之后,预处理器还有多少价值。
要点
@function允许定义带参数、可返回值的 CSS 函数,是自定义属性能力的延伸- 与 Sass 函数的关键差别是运行时求值,能响应变量变化和媒体查询
- 预处理器剩下的独占能力越来越少:嵌套、变量、混入都已原生化
我的补充:这条对这个博客有直接影响——主题目前用 SCSS,但用到的功能(嵌套、变量)现在原生 CSS 都有了。不必急着迁移,但新写的样式可以直接用原生嵌套和自定义属性,减少对预处理器的依赖。预处理器现在真正的价值主要剩下:循环生成大量规则、复杂的编译期计算、以及既有代码库的惯性。
99. Static Site Generation (SSG) with Next.js
来源:MDN Blog · developer.mozilla.org 读它解决什么:理解静态生成的适用边界与常见模式。
要点
- SSG 在构建时生成 HTML,请求时直接由 CDN 返回,性能与成本都最优
- 适用前提是内容在构建时可确定;用户相关内容需要客户端获取或改用其他渲染模式
- 增量静态再生(ISR)这类机制处理"内容会变但不需要实时"的场景
我的补充:判断渲染策略的顺序:能静态就静态 → 需要按请求变化才 SSR → 用户私有数据用客户端请求。内容站几乎都该是 SSG。这个博客的取舍是个好例子:文章是静态的,而天气、壁纸这类会变的内容走客户端请求 + 边缘函数,既保住了 CDN 命中率又有动态能力。误区是为了少量动态内容把整站改成 SSR,那会丢掉静态站的全部优势。
100. Setting up service workers
来源:MDN Blog · developer.mozilla.org 读它解决什么:要加离线支持或缓存策略,需要正确使用 Service Worker。
要点
- Service Worker 是代理层,能拦截请求并决定用缓存还是网络,生命周期是 install → activate → fetch
- 更新机制有讲究:新 SW 默认等待旧的释放控制,
skipWaiting()会立即接管 - 缓存策略要按资源类型定:带哈希的静态资源可以长期缓存,HTML 必须优先网络
我的补充:Service Worker 是最容易把站点搞坏的一项技术,两个坑我都踩过。一是 skipWaiting() + clients.claim() 会让 controllerchange 在首次访问时也触发,如果监听里无条件 reload(),用户第一次打开站点就会白刷一次——判断条件应该是"之前已经有 controller 才算真更新"。二是缓存了 HTML 却没设失效策略,用户会长期看到旧版本。建议:HTML 用 network-first,静态资源用 cache-first,并且一定要留一条能远程禁用 SW 的退路。
本卷小结
三条:
- 每次提交前跑一次 build。Vite 的 dev 与 build 走不同管线,"dev 正常 build 报错"是常态。
readonly()状态不能从外部赋值,生产环境静默失败——这个博客的天气加载态曾因此从未生效。- Service Worker 首访也会触发
controllerchange。无条件reload()会让新用户白刷一次。
