3.9 KiB
id, title, type, domain, stack, tags, level, related, week
| id | title | type | domain | stack | tags | level | related | week | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| frontend.vite.interview | Vite 面试题 | interview | frontend |
|
|
intermediate |
|
|
Vite 面试题
基础
【基础】Vite 为什么开发时启动快?
参考答案:开发时不打包,利用浏览器原生 ES 模块按需请求、按需转换;第三方依赖预构建并强缓存;热更新只更新修改的模块,与项目规模基本无关。
追问:预构建做了什么?→ 把 CommonJS / UMD 依赖转成 ESM,把有很多内部模块的依赖合并成少量文件,减少浏览器请求数。
【基础】既然浏览器支持 ESM,生产环境为什么还要打包?
参考答案:未打包时模块请求多、瀑布式加载慢;打包可以做 tree shaking、压缩、拆包、资源哈希命名以便长期缓存、兼容处理。
追问:Vite 8 生产构建用什么?→ Rolldown(Rust 实现、兼容 Rollup 插件接口);Vite 7 及以前是 Rollup。
【基础】Vite 中的环境变量怎么使用?
参考答案:.env / .env.[mode] 文件;浏览器代码通过 import.meta.env 读取,只有 VITE_ 前缀的变量会暴露;配置文件中用 loadEnv 读取全部变量。
追问:密钥可以放进 VITE_ 变量吗?→ 不可以,会被打包进前端代码。
进阶
【进阶】Vite 插件有哪些常用钩子?
参考答案:通用钩子(与 Rollup 兼容):resolveId、load、transform、buildStart / buildEnd;Vite 专有:config、configResolved、configureServer(给开发服务器加中间件)、transformIndexHtml、handleHotUpdate。可以用 enforce 控制顺序、apply 控制只在开发或构建时生效。
追问:虚拟模块怎么实现?→ resolveId 识别特定 id 返回带 \0 前缀的内部 id,load 返回模块源码。
【进阶】Vite 的 HMR 是如何工作的?
参考答案:开发服务器监听文件变化,通过 WebSocket 通知浏览器;客户端根据模块依赖图找到接受更新的边界模块(例如 Vue 组件),重新请求该模块并替换,组件状态尽量保留;找不到边界时刷新整页。
追问:为什么有时候修改后整页刷新?→ 修改的模块及其上层没有模块接受热更新(例如修改了入口文件或纯工具模块的上层没有 HMR 边界)。
【进阶】怎么做拆包?
参考答案:动态 import() 自动生成独立 chunk(路由懒加载);第三方库按变化频率分组(Vite 8 用 codeSplitting.groups),利于长期缓存;避免拆得过碎。
追问:如何分析产物体积?→ 使用打包分析插件生成可视化报告,找大依赖和重复依赖。
场景题
【场景题】老项目从 webpack 迁移到 Vite,你怎么推进?
参考答案:盘点 webpack 专有用法 → 两套配置并存 → 逐项替换(require.context、环境变量、自定义 loader)→ 冒烟测试和产物对比 → 灰度上线并保留回滚。详见迁移场景。
追问:迁移遇到的最大问题?→ 常见的是 CommonJS 依赖和隐式的 Node 全局变量,以及开发模式正常、生产构建报错。
【场景题】开发时用 Vite 代理调用 AI 流式接口,前端收到的内容是一次性出现的,怎么排查?
参考答案:先用 curl 直连后端确认后端是否真的逐条发送和 flush;再经过代理用 curl -N 验证;检查响应头是否是 text/event-stream、是否被压缩中间件缓冲;最后检查前端是否用流式方式读取(fetch 的 body.getReader() 或 EventSource),而不是 await res.text()。
追问:生产环境同样问题?→ 检查 Nginx 的 proxy_buffering 和 X-Accel-Buffering 响应头、CDN / 网关是否缓冲。