JS `Vite` `Pre-Bundling` `Dependency Graph` `Invalidation` `Strategies`

admin
Mon, 21 Jul 2025 17:36:27 +0000
Javascript, js, pre, vite

各位靓仔靓女，大家好！我是今天的主讲人，咱们今天聊聊Vite的预构建，这可是Vite快如闪电的关键秘籍之一，搞懂它，你的开发效率起飞不是梦！ 预热：为什么需要预构建？ 想象一下，你正在开发一个大型项目，里面充满了各种各样的第三方依赖。这些依赖通常是CommonJS或者UMD格式的，浏览器可不认识这些“老古董”。而且，很多依赖包内部又会引用其他模块，形成一个错综复杂的依赖关系网。 如果直接把这些依赖丢给浏览器，那浏览器就得： 下载一大堆零碎的文件。 解析这些CommonJS/UMD模块。 解决模块之间的依赖关系。 这一顿操作下来，浏览器直接懵逼，启动速度慢到令人发指。这就是传说中的“冷启动”地狱！ Vite的预构建就是来拯救大家的。它会在开发服务器启动时，就把这些CommonJS/UMD模块转换成浏览器友好的ESM格式，并且把它们打包成一个或几个模块。这样，浏览器就只需要下载几个文件，就能搞定所有的依赖，启动速度自然就蹭蹭蹭地上去了。 主角登场：Vite 的预构建 Vite的预构建主要做了两件事： 依赖发现（Dependency Discovery）： 扫描你的源代码，找出所有的依赖项。 …

继续阅读“JS `Vite` `Pre-Bundling` `Dependency Graph` `Invalidation` `Strategies`”

The post JS `Vite` `Pre-Bundling` `Dependency Graph` `Invalidation` `Strategies` first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

各位靓仔靓女，大家好！我是今天的主讲人，咱们今天聊聊Vite的预构建，这可是Vite快如闪电的关键秘籍之一，搞懂它，你的开发效率起飞不是梦！

预热：为什么需要预构建？

想象一下，你正在开发一个大型项目，里面充满了各种各样的第三方依赖。这些依赖通常是CommonJS或者UMD格式的，浏览器可不认识这些“老古董”。而且，很多依赖包内部又会引用其他模块，形成一个错综复杂的依赖关系网。

如果直接把这些依赖丢给浏览器，那浏览器就得：

1. 下载一大堆零碎的文件。

2. 解析这些CommonJS/UMD模块。

3. 解决模块之间的依赖关系。

这一顿操作下来，浏览器直接懵逼，启动速度慢到令人发指。这就是传说中的“冷启动”地狱！

Vite的预构建就是来拯救大家的。它会在开发服务器启动时，就把这些CommonJS/UMD模块转换成浏览器友好的ESM格式，并且把它们打包成一个或几个模块。这样，浏览器就只需要下载几个文件，就能搞定所有的依赖，启动速度自然就蹭蹭蹭地上去了。

主角登场：Vite 的预构建

Vite的预构建主要做了两件事：

1. 依赖发现（Dependency Discovery）： 扫描你的源代码，找出所有的依赖项。

2. 依赖优化（Dependency Optimization）： 将这些依赖项转换成ESM格式，并进行打包。

1. 依赖发现（Dependency Discovery）

Vite 使用 esbuild 来进行依赖分析，速度那是相当的快。它会扫描你的 index.html 文件，以及所有被 index.html 引用的 JavaScript/TypeScript 文件。

例如，你的 index.html 文件可能是这样的：




My Awesome App







Vite 会从 src/main.js 开始，递归地分析所有 import 语句，找到所有依赖项。

假设 src/main.js 看起来像这样：

import { createApp } from 'vue';
import App from './App.vue';
import { createRouter, createWebHistory } from 'vue-router';
import axios from 'axios';

const router = createRouter({
  history: createWebHistory(),
  routes: []
});

const app = createApp(App);
app.use(router);
app.mount('#app');

axios.get('/api/data').then(response => {
  console.log(response.data);
});

Vite 就会发现以下依赖项：

vue

vue-router

axios

2. 依赖优化（Dependency Optimization）

Vite 使用 esbuild 将这些依赖项转换成 ESM 格式，并进行打包优化。esbuild 的速度非常快，通常比 rollup 快 10-100 倍。

Vite 会将这些依赖项打包到一个或几个文件中，通常放在 node_modules/.vite 目录下。例如，你可能会看到类似这样的文件：

node_modules/.vite/deps/vue.js

node_modules/.vite/deps/vue-router.js

node_modules/.vite/deps/axios.js

这些文件就是已经转换成 ESM 格式的依赖项，浏览器可以直接加载。

Invalidation Strategies (失效策略)

预构建不是一劳永逸的。当你的依赖项发生变化时，Vite 需要重新进行预构建。Vite 使用以下策略来判断是否需要重新预构建：

package.json 文件的 dependencies 字段发生变化： 当你安装、卸载或更新依赖项时，package.json 文件会发生变化。Vite 会监听 package.json 文件的变化，当发现变化时，会重新预构建所有依赖项。

Vite 配置文件（vite.config.js 或 vite.config.ts）发生变化： 你的 Vite 配置可能会影响预构建的结果。例如，你可能配置了 optimizeDeps 选项，或者使用了 Vite 插件。当 Vite 配置文件发生变化时，Vite 会重新预构建所有依赖项。

Vite 版本升级： Vite 版本升级可能会引入新的预构建策略。当 Vite 版本升级时，Vite 会重新预构建所有依赖项。

手动删除 node_modules/.vite 目录： 如果你手动删除了 node_modules/.vite 目录，Vite 会在下次启动时重新预构建所有依赖项。

optimizeDeps 选项：掌控预构建

Vite 提供了 optimizeDeps 选项，让你更精细地控制预构建的行为。

optimizeDeps 选项可以配置以下几个方面：

include： 指定需要强制预构建的依赖项。

exclude： 指定不需要预构建的依赖项。

esbuildOptions： 传递给 esbuild 的选项。

needsInterop: (不推荐使用，通常自动处理)对于一些 CommonJS 模块，可能需要额外的处理才能在 ESM 中正常工作。

下面是一个 vite.config.js 的例子：

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  optimizeDeps: {
    include: ['vue', 'vue-router', 'axios'], // 强制预构建
    exclude: ['some-large-library'], // 排除，比如一些体积庞大的库
    esbuildOptions: {
      // 配置 esbuild 的选项，例如 JSX 支持
      jsxFactory: 'h',
      jsxFragment: 'Fragment',
      loader: {
        '.js': 'jsx'
      }
    }
  }
});

include 选项：强制预构建

有些情况下，Vite 可能无法自动发现某些依赖项。例如，你的代码可能使用了动态 import 语句，或者依赖项是通过字符串字面量引入的。

在这种情况下，你可以使用 include 选项来强制预构建这些依赖项。

例如：

import { defineConfig } from 'vite';

export default defineConfig({
  optimizeDeps: {
    include: ['my-custom-library'] // 强制预构建 my-custom-library
  }
});

exclude 选项：排除不需要预构建的依赖项

有些依赖项可能不需要预构建。例如，你的代码可能已经使用了 ESM 格式的依赖项，或者你希望手动处理这些依赖项。

在这种情况下，你可以使用 exclude 选项来排除这些依赖项。

例如：

import { defineConfig } from 'vite';

export default defineConfig({
  optimizeDeps: {
    exclude: ['lodash'] // 排除 lodash
  }
});

esbuildOptions 选项：定制 esbuild 行为

esbuildOptions 选项允许你传递选项给 esbuild。这可以让你更精细地控制预构建的行为。

例如，你可以使用 esbuildOptions 来配置 JSX 支持：

import { defineConfig } from 'vite';

export default defineConfig({
  optimizeDeps: {
    esbuildOptions: {
      jsxFactory: 'h',
      jsxFragment: 'Fragment'
    }
  }
});

或者设置loader来处理特定文件：

import { defineConfig } from 'vite';

export default defineConfig({
  optimizeDeps: {
    esbuildOptions: {
      loader: {
        '.js': 'jsx'
      }
    }
  }
});

预构建中的常见问题和解决方案

依赖项未被预构建： 检查是否正确配置了 include 选项。

预构建失败： 检查依赖项是否存在，或者依赖项是否存在兼容性问题。尝试更新依赖项到最新版本。

预构建后代码出错： 检查依赖项是否正确导出了 ESM 模块。如果依赖项是 CommonJS 模块，尝试使用 needsInterop 选项。 (通常 Vite 会自动处理)

预构建速度慢： 检查是否排除了不需要预构建的依赖项。

一些实用技巧

清理 node_modules/.vite 目录： 当遇到奇怪的预构建问题时，尝试删除 node_modules/.vite 目录，然后重新启动 Vite。

使用 vite --force 命令： 使用 vite --force 命令可以强制 Vite 重新预构建所有依赖项。

查看 Vite 的日志： Vite 的日志可以提供有关预构建过程的详细信息。

代码示例：一个完整的 Vite 项目

为了更好地理解 Vite 的预构建，我们来看一个完整的 Vite 项目的例子。

my-vite-project/
├── index.html
├── vite.config.js
├── package.json
├── src/
│   ├── main.js
│   ├── App.vue
│   └── components/
│       └── HelloWorld.vue
└── node_modules/
    └── ... (dependencies)

index.html：




My Awesome App







src/main.js：

import { createApp } from 'vue';
import App from './App.vue';
import { createRouter, createWebHistory } from 'vue-router';
import axios from 'axios';

const router = createRouter({
  history: createWebHistory(),
  routes: []
});

const app = createApp(App);
app.use(router);
app.mount('#app');

axios.get('/api/data').then(response => {
  console.log(response.data);
});

vite.config.js：

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  optimizeDeps: {
    include: ['vue', 'vue-router', 'axios'],
    exclude: [],
    esbuildOptions: {
      jsxFactory: 'h',
      jsxFragment: 'Fragment'
    }
  }
});

package.json：

{
  "name": "my-vite-project",
  "version": "1.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "axios": "^1.6.7",
    "vue": "^3.4.15",
    "vue-router": "^4.2.5"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.0.4",
    "vite": "^5.1.4"
  }
}

在这个例子中，Vite 会预构建 vue、vue-router 和 axios 这三个依赖项。

总结：预构建的艺术

Vite 的预构建是提高开发效率的关键。通过理解预构建的原理和配置，你可以更好地控制 Vite 的行为，解决预构建中遇到的问题，让你的项目启动速度更快，开发体验更好。

记住，预构建不是一劳永逸的，需要根据项目的实际情况进行调整。

希望今天的讲座对大家有所帮助！ 预构建这事儿，说白了就是个“先下手为强”的策略，先把那些“老弱病残”的依赖给优化了，让浏览器跑起来更顺畅。 掌握了这门技术，你就能在前端开发的世界里，更加游刃有余啦！ 下课！