十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

告别Webpack:TypeScript+tsup+Vite+Rolldown构建组合实践

告别Webpack:TypeScript+tsup+Vite+Rolldown构建组合实践 Webpack 配置多了之后每次新加一个功能都要先想清楚 loader、plugin、resolve 之间的关系。项目规模上来以后冷启动和热更新的耗时也在变长这几乎是所有 Webpack 项目的通病。本文想分享一套更轻的构建组合TypeScript tsup Vite Rolldown从工具定位、配置示例到迁移思路做一个系统梳理帮你在实际项目中找到一套“按需使用”的构建方案。这套组合的核心思路并不复杂把“应用”和“库”分开处理。应用层用 Vite 获得极致的开发体验库层用 tsup 快速产出 ESM/CJS 双格式产物底层再用 Rolldown 这类 Rust 工具继续优化性能上限。下面进入正题。1. 告别 Webpack问题与新趋势1.1 Webpack 为什么让人想告别Webpack 诞生于 2012 年它的核心价值是“模块打包”把 JavaScript、CSS、图片、字体全部视为模块通过 loader 做转换通过 plugin 做扩展。这种设计在当时非常先进直到今天Webpack 依然是生态最成熟的打包器之一。但成熟不代表舒适。真实项目里Webpack 的痛点非常具体配置成本高。一个中等规模项目webpack.config.js动辄几百行。module.rules、resolve.alias、optimization.splitChunks、devServer.proxy每一项都需要仔细维护新成员接手成本很高。构建性能瓶颈明显。Webpack 基于 Node.js 运行模块图构建和代码转换是串行进行的。当项目依赖膨胀到几千个模块后冷启动需要几十秒甚至几分钟热更新在大型项目中也经常出现 2~5 秒的延迟。内存占用较高。开发服务器需要维护完整的模块图和缓存内存占用经常超过 2GB在普通配置的办公电脑上体验很差。产物优化参数过于复杂。tree shaking、代码分割、缓存组配置每一项都需要对 Webpack 内部机制有足够理解否则容易“配了但没生效”。这些痛点并不代表 Webpack 不好而是说明“一个工具解决所有问题”的模式在当下已经不够灵活。业务项目需要的是快速反馈工具库需要的是轻量发布这两种场景对构建器的要求完全不同。1.2 新工具组合的定位既然需求不一样那就不应该用同一个构建器解决所有问题。当前前端社区逐渐形成了一套分工明确的新组合工具定位适合场景TypeScript类型系统和语言层能力所有需要类型安全的中大型项目tsup基于 esbuild 的库打包工具npm 包、工具库、组件库、SDKVite开发服务器 应用构建器Web 应用、SPA、SSR 应用RolldownRust 实现的 Rollup 替代内核未来 Vite 的底层打包器优化构建性能简单来说应用用 Vite库用 tsup底层用 Rolldown 继续加速。三者不再重复造轮子而是各自聚焦最擅长的场景。这套组合并不是“必须全部使用”而是可以按项目情况灵活选择。如果只有一个页面应用单独用 Vite 就够了如果只是开发一个给其他项目引用的工具库那么 tsup 是最轻量的选择。1.3 先厘清两个容易混淆的 “ts”在看资料时你可能会搜到两个完全不同的 “ts”TypeScriptTS微软开发的 JavaScript 超集提供静态类型检查编译后输出纯 JavaScript。这是前端开发中常用的 TS。MPEG-TSTransport Stream一种视频封装格式常见后缀是.ts。比如下载视频时会看到一堆xxx.ts文件或者用 ffmpeg 处理视频时出现ts伪装jpg这类骚操作这里的 ts 和 TypeScript 没有任何关系。所以当你看到“下载视频变成了很多 ts 文件”这类问题先确认一下文件后缀是.ts还是 TypeScript 源码的.ts前者是视频流文件后者才是代码文件。这个问题在搜索引擎里经常混淆先有个印象后面不会踩坑。2. 环境准备与版本说明2.1 运行环境本文的示例基于以下环境操作系统Windows 10/11、macOS 或 Linux 均可。Node.js建议使用当前 LTS 版本。历史原因不同 Node 版本对 ESM 的支持差异较大统一使用 LTS 可以减少意外。包管理器npm、pnpm、yarn 均可。本文示例以 npm 为主如果你使用 pnpm命令对应替换即可。编辑器VS Code 或其他支持 TypeScript 的编辑器。具体版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路而不是绑定某个固定版本。安装最新稳定版时以你安装时的实际版本为准。2.2 初始化项目结构为了方便后面的实战演示我先规划一个包含“应用”和“库”两种场景的演示项目结构frontend-workspace/ ├── packages/ │ ├── ui-lib/ # 用 tsup 构建的组件库 │ │ ├── src/ │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── tsup.config.ts │ └── web-app/ # 用 Vite 构建的前端应用 │ ├── src/ │ ├── index.html │ ├── package.json │ ├── tsconfig.json │ └── vite.config.ts └── package.json这个结构模拟了很多企业级前端的 monorepo 布局一个ui-lib负责组件和工具方法一个web-app负责业务页面。两个包使用不同的构建方案但共享 TypeScript 类型规范。2.3 安装依赖先初始化根目录npm init -y然后分别进入子目录初始化或直接手动创建package.json。本文为了减少包管理器的干扰直接在各自目录独立安装依赖。进入packages/ui-lib安装构建相关依赖npm install -D typescript tsup进入packages/web-app安装应用构建相关依赖npm install -D typescript vite vitejs/plugin-vue vue-tsc如果是 React 项目把vitejs/plugin-vue替换为vitejs/plugin-react即可。3. 核心概念拆解3.1 TypeScript类型系统是这一切的基础TypeScript 的作用不只是“给变量加上类型”它更是一种团队协作的契约。在构建工具链中TS 类型的正确性直接影响最终产物的质量。interface 与 typeinterface用于描述对象的结构可扩展适合定义 API 返回结构、组件 Props、配置对象等。// 定义一个用户信息接口 export interface UserInfo { id: number; name: string; email?: string; // 可选属性 readonly createdAt: string; // 只读属性 }type更灵活可以用来定义联合类型、交叉类型、元组等。// 联合类型请求状态 export type RequestStatus idle | loading | success | error; // 交叉类型组合多个类型 export type ButtonWithIcon ButtonProps { icon: string };Partial 等工具类型PartialT可以把接口的所有属性变成可选在表单编辑、局部更新场景非常常用。// 定义一个更新用户的参数类型 export type UpdateUserParams PartialUserInfo; // 等价于{ id?: number; name?: string; email?: string; readonly createdAt?: string; }封装 axios 请求时的泛型TypeScript 在数据请求场景最大的价值就是给 axios 封装一个“可复用的响应类型模板”// src/utils/http.ts import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from axios; export interface ApiResponseT unknown { code: number; message: string; data: T; } export class HttpClient { private instance: AxiosInstance; constructor(baseURL: string) { this.instance axios.create({ baseURL, timeout: 10000, }); this.instance.interceptors.response.use( (response: AxiosResponseApiResponse) { const res response.data; if (res.code ! 200) { return Promise.reject(new Error(res.message)); } return response; }, (error) Promise.reject(error) ); } getT(url: string, config?: AxiosRequestConfig): PromiseApiResponseT { return this.instance .getApiResponseT(url, config) .then((res) res.data); } postT(url: string, data?: unknown, config?: AxiosRequestConfig): PromiseApiResponseT { return this.instance .postApiResponseT(url, data, config) .then((res) res.data); } }调用时只需要传入业务数据类型import { HttpClient } from /utils/http; const http new HttpClient(/api); interface UserListResult { list: UserInfo[]; total: number; } async function fetchUsers() { const res await http.getUserListResult(/user/list); return res.data.list; }这种写法让接口文档和代码强绑定后端字段变动时编译器能在第一时间发现类型不匹配的问题。3.2 tsup为 TS 库打包而生tsup 是基于 esbuild 的 TypeScript 库打包工具。它的核心优势是零配置起步速度快内置 DTS 生成。为什么库项目需要单独的打包器因为应用和库的交付目标完全不同应用要的是“直接跑起来”HTML、CSS、JS、静态资源最后打包成一套产物。库要的是“被其他项目引用”需要同时支持 ESM 和 CJS、声明文件.d.ts、sourcemap方便消费方按自己环境选择入口。如果直接用 Webpack 打包库配置会非常繁琐而且 Webpack 的产物往往带有很多运行时辅助代码不利于库的“纯净度”。tsup 的配置足够轻量// tsup.config.ts import { defineConfig } from tsup; export default defineConfig({ entry: [src/index.ts], // 入口文件 format: [esm, cjs], // 输出 ESM 和 CJS dts: true, // 生成 .d.ts 类型声明 sourcemap: true, // 生成 sourcemap clean: true, // 构建前清空输出目录 treeshake: true, // 启用 tree shaking target: es2020, // 编译目标 outDir: dist, // 输出目录 });format: [esm, cjs]是最常用的配置既能支持现代打包器的 ESM 优先又能兼容旧版 Node 的require。dts: true帮你自动生成类型声明文件这样使用方不需要手动维护声明。3.3 Vite面向应用的开发与构建Vite 的核心设计有两个开发阶段利用浏览器原生 ESM 加载模块不做整包打包启动速度极快。依赖预构建用 esbuild 完成页面加载速度因此大幅提升。生产构建使用 Rollup 做最终的打包和代码分割保证产物有完整的 tree shaking 和分包能力。Vite 的配置天然比 Webpack 更贴合应用场景。下面是一个 Vue 3 TS 项目的典型配置// vite.config.ts import { defineConfig } from vite; import vue from vitejs/plugin-vue; import { fileURLToPath, URL } from node:url; export default defineConfig({ plugins: [vue()], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)), }, }, server: { port: 5173, host: true, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, }, }, }, build: { outDir: dist, sourcemap: true, rollupOptions: { output: { manualChunks: { vue-vendor: [vue, vue-router, pinia], }, }, }, }, });开发阶段server.proxy解决了前后端分离时的跨域问题把/api请求代理到后端服务。构建阶段manualChunks让第三方依赖单独分包利用浏览器缓存提升加载效率。3.4 Rolldown下一代打包内核Rolldown 是 Vite 团队正在推进的 Rust 版 Rollup。它的目标非常明确用 Rust 重写 Rollup 的打包逻辑保留 Rollup 的插件 API 和生态同时获得接近原生代码的性能。理解 Rolldown要先理解 Vite 当前的瓶颈。Vite 生产构建使用的是 RollupJavaScript 实现而开发阶段使用的是 esbuildGo 实现。两种工具之间经常出现“开发环境正常、生产构建结果不一致”的情况Rolldown 的出现就是为了统一 Vite 的底层打包内核。至于 Rolldown 的具体使用方式等它正式进入稳定版后大概率是作为 Vite 的内置替换开发者不需要改变太多配置。目前它仍处于孵化和兼容适配阶段如果你想跟进最新进展关注 Vite 官方仓库和 Rolldown 仓库即可。对于普通业务项目现在不建议直接在生产环境依赖 Rolldown而是保持关注等生态稳定后再评估替换。3.5 四者在实际项目中的分工综合来看四者的关系可以这样理解TypeScript提供类型安全贯穿所有代码 tsup库的构建出口解决 npm 包发布问题 Vite应用的开发服务器和构建器解决页面应用体验问题 Rolldown未来的底层加速器优化 Vite 的构建内核一个常见的组织方式是业务应用使用 Vite公共组件和工具函数放在独立包中用 tsup 构建后发布到私有 npm 仓库。这种做法让业务代码和基础库解耦基础库可以独立迭代版本业务应用通过依赖升级获取更新。4. 实战一用 tsup 打造一个 TS 工具库4.1 定义库的导出内容假设我们要做一个简单的按钮组件和工具函数集合。先明确入口文件要导出什么。在packages/ui-lib/src下创建以下文件// packages/ui-lib/src/components/button.ts export interface ButtonProps { text: string; onClick?: () void; type?: primary | default | danger; } export function createButton(props: ButtonProps): HTMLButtonElement { const btn document.createElement(button); btn.textContent props.text; btn.className btn btn-${props.type ?? default}; btn.addEventListener(click, () props.onClick?.()); return btn; }// packages/ui-lib/src/utils/format.ts export function formatDate(date: Date): string { const y date.getFullYear(); const m String(date.getMonth() 1).padStart(2, 0); const d String(date.getDate()).padStart(2, 0); return ${y}-${m}-${d}; }// packages/ui-lib/src/index.ts export * from ./components/button; export * from ./utils/format;这里用了export *统一导出保证使用方可以从单入口引用所有能力。4.2 编写 tsup 配置在packages/ui-lib下创建tsup.config.tsimport { defineConfig } from tsup; export default defineConfig({ entry: [src/index.ts], format: [esm, cjs], dts: true, sourcemap: true, clean: true, treeshake: true, minify: true, external: [react, vue], // 如果有 peer dependencies 需要排除 target: es2020, });external配置非常关键。当库依赖react、vue这类框架时不能让它们被打进产物而应该让使用方的环境去提供。否则同一个 React 包可能出现多份实例导致 hooks 报错等奇怪问题。4.3 配置 package.jsonpackage.json需要明确入口字段{ name: demo/ui-lib, version: 0.1.0, main: ./dist/index.cjs, module: ./dist/index.js, types: ./dist/index.d.ts, exports: { .: { types: ./dist/index.d.ts, import: ./dist/index.js, require: ./dist/index.cjs } }, files: [dist], scripts: { build: tsup, type-check: tsc --noEmit } }main给 CJS 使用module给 ESM 使用types提供类型声明。exports字段是 Node.js 新版支持的更精细的入口控制打包工具也会优先读取它。files限定发布时只包含dist目录避免把源码和配置文件一起发布到 npm。4.4 构建并验证产物在packages/ui-lib目录执行npm run build预期输出大致如下CLI Building entry: src/index.ts CLI Using tsconfig: tsconfig.json CLI tsup v8.x.x CLI Target: es2020 CLI Cleaning output directory CLI Generating declarations CLI Building: src/index.ts → dist/index.js, dist/index.cjs CLI Building: src/index.ts → dist/index.d.ts构建完成后dist目录下会生成dist/ ├── index.js # ESM 产物 ├── index.cjs # CJS 产物 ├── index.d.ts # 类型声明 └── index.js.map # sourcemap检查一下产物内容可以看到createButton和formatDate都已经被正确导出而且没有把无关代码打包进去。5. 实战二用 Vite 构建前端应用5.1 搭建 Vue 3 TS 应用现在进入packages/web-app创建一个标准 Vite 应用入口。index.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleVite TS Demo/title /head body div idapp/div script typemodule src/src/main.ts/script /body /htmlsrc/main.tsimport { createApp } from vue; import App from ./App.vue; createApp(App).mount(#app);src/App.vuescript setup langts import { ref } from vue; import { formatDate, createButton } from demo/ui-lib; const now ref(new Date()); function handleClick() { alert(按钮被点击了); } // 演示工具库的能力 const btnText formatDate(now.value); const btn createButton({ text: btnText, onClick: handleClick, type: primary, }); /script template div h1Vite TypeScript 应用/h1 pui-lib 输出{{ formatDate(now) }}/p /div /template这里直接引入了demo/ui-lib的组件和工具函数验证了“应用使用 Vite、库使用 tsup”的组合可以无缝协作。5.2 配置开发代理在vite.config.ts中我们配置了/api代理。这个配置解决的核心问题是开发环境的前后端分离。假设后端接口地址是http://localhost:8080前端页面运行在http://localhost:5173直接请求/api会跨域。代理配置让浏览器请求和开发服务器同源开发服务器再把请求转发到后端。server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ), }, }, },rewrite是常见操作把路径前缀/api去掉因为后端接口本身可能不包含这个前缀。这是一个很容易混淆的细节很多问题都是因为后端没有/api前缀而代理配置又没有 rewrite导致 404。5.3 构建优化与代码混淆Vite 的生产构建默认使用 Rollup代码压缩默认使用 esbuild。如果项目对代码混淆有更高要求可以在build中配置terserOptions。build: { outDir: dist, sourcemap: false, minify: terser, // 使用 terser 进行混淆压缩 terserOptions: { compress: { drop_console: true, // 移除 console.log drop_debugger: true, }, format: { comments: false, // 移除注释 }, }, rollupOptions: { output: { manualChunks: { vue-vendor: [vue, vue-router, pinia], }, }, }, },drop_console是生产环境很常用的优化项但要注意如果线上需要监控 console 输出就不应该全局移除而是针对性地移除console.log保留console.error。执行构建npm run build完成后dist目录下会生成应用产物。Vite 会提示输出文件大小这也是观察构建优化效果的重要参考。6. 从 Webpack 迁移到新组合的要点6.1 迁移前评估从 Webpack 迁移到 Vite 不是“改个配置文件”那么简单。迁移前应该先评估以下几个方面评估项说明Webpack loader 依赖是否使用了一些 Webpack 独有的 loader比如sass-loader、url-loader、file-loader插件生态依赖是否有重要的 Webpack plugin 无法替换比如某些自定义 HTML 注入插件CSS 处理方式是否依赖css-loader、style-loaderVite 默认原生支持 CSS Modules 和预处理器代码分割策略splitChunks的缓存组配置需要转换为 Vite 的manualChunks环境变量读取process.env需要替换为import.meta.env如果项目使用了大量 Webpack 特有插件或自定义 loader迁移成本会比较高建议先用小模块试点而不是一次全部替换。6.2 配置对比速查表功能WebpackVite / tsup入口配置entryRollup 的inputVite 默认index.htmlloader 转换module.rulesesbuild自动处理 TS/JSXVite 插件处理 Vue/Svelte路径别名resolve.aliasresolve.alias开发代理devServer.proxyserver.proxy代码分割optimization.splitChunksrollupOptions.output.manualChunks环境变量process.envimport.meta.env库模式output.librarytsup 的format配置这个对照表可以帮助你快速定位迁移时需要的改动点。6.3 分步迁移建议实际迁移不建议一步到位推荐以下顺序先切开发环境把npm run dev从 Webpack 切换到 Vite。开发环境改动成本最低收益最明显冷启动、热更新变快。再切生产构建确认开发环境正常后迁移npm run build。此时重点检查产物是否一致路由懒加载是否正常。最后处理细节处理环境变量、polyfill、一些 Webpack 插件的替代方案。保留回滚方案迁移过程中保留原来的 Webpack 配置文件至少在第一个稳定版本发布之前不要删掉。如果遇到无法解决的问题可以先用vite-plugin-webpack这类兼容插件做过渡但这种方法不建议长期使用因为它牺牲了 Vite 的性能优势。7. 常见问题与排查思路7.1 vite http proxy error开发中经常看到类似这样的报错14:35:43 [vite] http proxy error: /api/form/list?page1pagesize10 aggregate这个错误的直接原因是Vite 开发服务器尝试把/api/form/list代理到 target 地址但代理请求失败了。常见原因有后端服务没有启动。代理目标http://localhost:8080没有任何服务在监听。代理目标地址错误。后端服务实际运行在 8081 端口但配置写成了 8080。HTTPS 证书问题。后端是 HTTPS 且证书不被信任需要在代理中添加secure: false。路径 rewrite 配置错误。请求转发到后端后实际路径不对导致后端返回 404。排查顺序用 curl 或浏览器直接访问http://localhost:8080/api/...确认后端是否可达。检查 Vite 配置中的target和rewrite。查看后端服务日志确认是否有请求进入。如果后端是 HTTPS尝试配置secure: false。proxy: { /api: { target: https://backend.example.com, changeOrigin: true, secure: false, // 开发环境忽略自签名证书 }, },这种方式只适用于开发环境生产环境请通过 Nginx 或网关转发并确保证书是可信的。7.2 error [ERR_MODULE_NOT_FOUND]: Cannot find package vite这个报错通常有两种场景本地没有安装 vite在项目目录执行npm install -D vite或直接安装依赖。全局安装了 vite但项目内没有运行npm run dev时Node 会从当前目录的node_modules向上查找全局安装不在查找路径内。这种情况删除node_modules和package-lock.json后重新安装通常能解决。还有一种情况是 pnpm 的幽灵依赖问题。如果项目使用了 pnpm但某个包直接引用了vite而没有在自己的package.json中声明就会报Cannot find package vite。解决方式是把vite显式加入devDependencies。排查清单检查node_modules中是否存在vite。检查package.json的依赖声明。删除node_modules和锁文件后重装。如果是 monorepo确认是否在正确的包目录下执行命令。7.3 模块解析与类型错误迁移到 Vite TypeScript 后常见的类型错误包括路径别名不识别/components/xxx在 TS 中报红。需要在tsconfig.json中配置baseUrl和paths。模块声明缺失导入.vue文件或.css文件时找不到类型声明。需要添加env.d.ts/// reference typesvite/client / declare module *.vue { import { DefineComponent } from vue; const component: DefineComponent{}, {}, any; export default component; }node:前缀模块报错如果代码中使用了 Node.js 内置模块需要确认是否在浏览器环境运行必要时使用vite-plugin-node-polyfills或避免在浏览器代码中使用 Node API。7.4 常见问题汇总表问题现象常见原因解决思路启动白屏入口 HTML 或 main.ts 编译错误打开控制台查看报错按模块排查热更新不生效Vite cache 异常删除node_modules/.vite缓存后重启构建产物过大未做代码分割配置manualChunks拆分第三方依赖代理 404rewrite 或 target 错误确认后端路径调整代理配置TS 类型报错tsconfig 缺少 paths 配置补齐baseUrl和paths环境变量丢失使用process.env改为import.meta.env8. 最佳实践与工程建议8.1 按场景选择构建工具不同项目类型应该有不同的默认选择纯前端应用SPA默认 Vite配置简单开发体验好。SSR 应用Vite 支持 SSR搭配框架的官方插件即可。npm 工具库 / 组件库默认 tsup产物干净构建快。微前端主应用Vite 配合vite-plugin-federation可以实现模块联邦能力。遗留大型 Webpack 项目不建议一次性重写可以先用 Vite 做开发环境过渡逐步替换。对于 Rolldown现阶段可以作为“未来优化项”看待等它成为 Vite 的默认底层后再更新不要在生产环境盲目使用实验版本。8.2 配置管理环境变量命名规范统一使用VITE_前缀例如VITE_API_BASE_URL其他前缀的变量不会暴露给客户端代码。多环境配置使用vite.config.ts中的defineConfig配合导入环境文件避免把 API 地址写死在代码里。TS 配置隔离tsconfig.json用于编辑器类型检查tsconfig.build.json用于构建避免构建时把测试文件也编译进去。8.3 团队协作与 CI 集成在 CI 流水线中建议做以下几件事构建前执行vue-tsc --noEmit或tsc --noEmit确保类型检查通过。使用npm ci或pnpm install --frozen-lockfile安装依赖保证锁文件生效。缓存node_modules和 Vite 缓存.vite加速流水线构建。产物上传到部署平台时注意清理旧的dist目录。8.4 安全与依赖管理构建工具本身是开发依赖但也存在供应链风险。建议锁定依赖版本使用锁文件提交到仓库。定期执行npm audit检查已知漏洞。私有 npm 包发布前注意检查files字段避免把源码、node_modules、测试文件误发布。生产环境不要开启sourcemap如果业务需要线上故障定位可以选择发布到内部监控系统而不是暴露在浏览器中。9. 总结与学习路线这篇文章虽然是“告别 Webpack”的标题但真正的重点不是“Webpack 不好”而是“不同场景用不同工具不要让一个工具承担所有角色”。在具体落地时你可以这样安排先学会 TypeScript 的类型系统这是所有工具链的基础。用 tsup 打一个最简单的库理解 ESM、CJS、DTS 这些概念。用 Vite 搭一个应用跑通 dev、build、预览完整流程。再看 Rolldown理解 Vite 未来的优化方向。如果你正在负责一个存量项目建议先评估迁移成本优先在开发环境使用 Vite 提升日常效率而不是直接推翻重写。构建工具是团队协作的基础设施选型时要考虑维护成本、生态成熟度和团队学习成本而不是追逐最新工具。如果本文对你有帮助可以收藏备用。后续你可能会遇到更多构建层面的细节问题比如性能优化、微前端架构、Rust 工具链等这些都是值得继续深入的方向。
返回列表