Vite 的工作流程、底层原理和构建打包机制。

方木先生
    分享互动规则

    一、核心思想:为什么 Vite 快?

    Vite 快的根本原因是用原生 ES Modules 替代了 Bundle-first 的思路。传统工具(Webpack)启动时要把整个项目打包成一个 bundle 才能运行;Vite 开发期间根本不打包,浏览器直接按需请求模块。---

    二、Vite 开发服务器底层原理

    Vite Dev Server 的核心由三个子系统组成,协同工作:三大子系统说明:

    ① 依赖预构建(Dep Pre-bundling):用 esbuild(Go 写的,比 Babel 快 10-100x)将 node_modules 里的 CJS/UMD 包转成 ESM,并把有大量内部模块的包(如 lodash-es)合并成单文件,避免浏览器发出数百个请求。结果缓存在 node_modules/.vite/deps/

    ② 插件转换管道:完全复用 Rollup 的插件接口,每个请求进来后按 resolveId → load → transform 顺序流转,官方插件 @vitejs/plugin-vue 就挂在这里把 .vue SFC 拆成 <script><template><style> 三块分别处理。

    ③ HMR 引擎chokidar 监听文件系统变化 → 查找模块依赖图找到受影响的模块边界 → 通过 WebSocket 推送 update 事件 → 浏览器端运行时拉取新模块、执行 HMR API 回调,整个过程不刷页面


    三、一次 HTTP 请求的完整生命周期

    浏览器加载 main.ts 之后,每遇到一个 import 就向 Vite Dev Server 发请求,下面是单个请求的处理链路:关键细节:

    • 裸模块重写:浏览器不认识 import vue from 'vue',Vite 把它改成 import vue from '/@modules/vue.js' 再返回给浏览器。
    • .vue SFC 处理:一个 App.vue 文件实际上会被拆成多次请求:App.vue(script block)、App.vue?type=template(模板编译成 render 函数)、App.vue?type=style&index=0(样式注入),这些都是按需懒编译的。
    • 协商缓存:Vite 对每个模块加 etag,文件不变则 304,避免重复传输。

    四、HMR 热更新的精确流程

    HMR 是 Vite 最核心的体验,原理并不是"整页刷新",而是外科手术式地替换变更模块HMR 边界(Boundary)是关键概念:Vite 从变更文件开始,沿模块依赖图向上遍历 importers,找到第一个调用了 import.meta.hot.accept() 的模块——这就是更新边界。@vitejs/plugin-vue 为每个 .vue 文件自动注入这段代码,所以修改一个 Vue 组件,只有该组件的模块被替换,父组件、路由、全局 store 都不受影响。


    五、生产构建:Rollup 打包流程

    开发用 ESM 按需加载,生产必须打包——Vite 用 Rollup 做生产构建(不用 esbuild,因为 Rollup 的 tree-shaking、代码分割、chunk 策略更成熟):构建关键机制:

    • Tree Shaking:Rollup 基于 ES Module 静态导入分析,凡是没有被引用的 export 都会被丢弃。注意 sideEffects: false 要在 package.json 中正确配置,否则带副作用的模块不会被摇掉。
    • Chunk 分割策略:默认把动态 import() 分割成独立 chunk,vendor 中的第三方库单独打包(利于长效缓存)。可通过 build.rollupOptions.output.manualChunks 自定义分组。
    • 资源指纹:所有产物文件名带内容 hash,如 index.a1b2c3d4.js,浏览器可设置极长 Cache-Control,内容变了 hash 变了,缓存自动失效。
    • CSS 处理:开发时 CSS 被转成 JS 模块注入;生产时通过 vite:css-post 插件提取成独立的 .css 文件,避免 FOUC(无样式闪烁)。

    六、开发 vs 生产的核心差异对比

    维度开发模式(Dev)生产模式(Build)
    模块处理浏览器原生 ESM,按需加载Rollup 全量打包
    编译器esbuild(极快,不压缩)Rollup + esbuild/Terser(压缩优化)
    Tree Shaking不做Rollup 静态分析
    代码分割不做动态 import / manualChunks
    Source Map内联,便于调试可选独立 .map 文件
    CSS 处理JS 注入,支持 HMR提取独立 CSS 文件
    启动速度毫秒级(只预构建 deps)秒到分钟(视项目大小)

    七、插件系统与扩展点

    Vite 的插件格式是 Rollup 插件的超集,新增了几个 Vite 独有的钩子:

    export default function myPlugin(): Plugin {
      return {
        name: 'my-plugin',
    
        // ① Rollup 通用钩子
        resolveId(id, importer) { /* 自定义模块解析 */ },
        load(id) { /* 自定义模块内容加载 */ },
        transform(code, id) { /* 代码转换 */ },
    
        // ② Vite 独有钩子(Dev Server)
        configureServer(server) {
          // 往 Connect 实例添加自定义中间件
          server.middlewares.use((req, res, next) => { ... })
        },
        handleHotUpdate(ctx) {
          // 自定义 HMR 行为,可过滤或追加受影响模块
          return ctx.modules.filter(m => !m.id.includes('ignore'))
        },
    
        // ③ 构建钩子
        generateBundle(options, bundle) {
          // 操作最终产物,如注入 license 注释
        }
      }
    }
    

    enforce: 'pre' / enforce: 'post' 控制插件在管道中的执行顺序;apply: 'serve' / apply: 'build' 控制插件仅在开发或生产时生效。


    总结

    Vite 的设计哲学是把正确的工具用在正确的场景

    • 开发期:信任浏览器原生 ESM,用 esbuild 做极速编译,不打包、按需转换,换来毫秒级 HMR 体验。
    • 生产期:回归 Rollup 做严肃的代码优化,tree-shaking + chunk 分割 + 资源 hash,产物质量与 Webpack 持平甚至更优。

    这两套路径都基于同一套插件系统,让你写一个插件、同时覆盖两个阶段——这是 Vite 区别于其他工具的最精妙设计。

    评论 0

    支持 @用户名 提醒对方(需为站内已注册用户名);回复仅支持一层楼中楼。

    登录后发表评论、回复与 @ 提及。

    举报

    举报会匿名发送给管理员审核。

    • 暂无评论,来发表第一条。

    码谱 · The Digital Atelier · 技术内容社区