Skip to content

2025.8月:byd 怎么版本升级的这么快,才过了3个多月就已经vite7了...

https://vitejs.cn/vite5-cn/guide/why.html

vite相比webpack等来说算是一个比较新的构建工具了

为什么vite这么快,在当下这么受欢迎

  • bundless思想,这个还得基于现代浏览器支持esm了,在开发时期不打包而是通过浏览器来加载esm模块,所以vite冷启动很快。
    • 浏览器ESM:script声明type=module后,可以利用import,export来模块化开发而无需打包
    • 传统打包器需要抓取并构建你的整个应用才能提供服务
    • vite将应用模块区分为依赖源码
      • 依赖:大多为在开发时不会变动的纯 JavaScript,用esbuild预构建
      • 源码:并非直接是 JavaScript 的文件(例如 JSX、CSS 、Vue组件),经常会被编辑。并不是所有的源码都需要同时被加载(比如懒加载,路由划分等)
      • 让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。
  • HMR热更新,当然很多构建工具现在也支持这个,基于ws通信。
    • 传统开发服务器将构建产物缓存到内存,但是在文件更改时,即使部分模块失活,仍需要进行整个项目的重新构建,并且会丢状态。
    • HMR允许一个模块 “热替换” 它自己,而不会影响页面其余部分,不过热更新速度也会随着应用规模的增长而显著下降。
    • Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界之间的链失活(甚至大多数时候只是模块本身),也就是依赖链。
    • 内置插件:SFCReactFastRefresh
  • 本地开发,只打包必要的内容,并且有缓存产物。
    • 预构建的依赖项缓存到 node_modules/.vite
      • 什么时候重新运行预构建?
        • 包管理器的锁文件
        • vite.config.js
        • NODE_ENV
        • 补丁文件夹的修改时间
    • 通过HTTP头部来加快重载
      • 源码是被HTTP过来的,可以利用304协商缓存一部分
      • 依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存
  • 开发模式使用esbuild从而极速构建,在生产环境下使用rollup来打包
思考:既然支持ESM,为什么要打包

嵌套导入会导致额外的网络往返,并且打包中还可以进行摇树,代码分割以提升加载性能。

思考:为什么生产和开发的打包器不一样

开发要求迅速,esbuild通过go编写,性能高,目前也是最快的

Rollup 灵活的插件 API 和基础建设,为vite提供了更好的性能与灵活性,也增强了社区支持

关注:rollup进化,swc,rolldown

Rollup 已经开始着手改进性能,在 v4 中将其解析器切换到 SWC。同时还有一个正在进行中的工作,即构建一个名为 Rolldown 的 Rust 版本的 Rollup。一旦 Rolldown 准备就绪,它就可以在 Vite 中取代 Rollup 和 esbuild,显著提高构建性能,并消除开发和构建之间的不一致性。

+ 插件➡微内核

脚手架

powershell
pnpm create vite

配置文件

vite.config.ts包含下面内容

  • root 项目根目录,默认为项目所在目录
typescript
import {defineConfig} from 'vite';
import react from '@vitejs/
export default defineConfig({
  base: '/', // 公共路径,如果你的应用部署到根目录就默认这样
  plugins:[react()], // 插件配置列表
  server:{ // 开发配置
    port: 5174,
    proxy:{
      '/api': 'http://localhost:8080',
    },
  },
  build:{ // 生产配置
    outDir: 'dist',
    chunkSizeWarningLimit: 30, // 单产物文件警告限额
  }
});

环境变量

定义

VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码,解析时会返回一个字符串。

tsx
VITE_SOME_KEY=123
DB_PASSWORD=foobar

类型

typescript
/// <reference types="vite/client" />
interface ImportMetaEnv {
  // 环境变量
}
interface ImportMeta {
  env: ImportMetaEnv
}

加载

vite通过dotenv来加载

  • .env所有情况下都会加载
  • .env.local所有情况下都会加载,但会被 git 忽略
  • .env.[mode]只在指定模式下加载
  • .env.[mode].local只在指定模式下加载,但会被 git 忽略

vite启动时可以加上--mode [mode]参数指定模式。

默认情况下,dev 命令使用 development,build 命令production

库模式中,所有 import.meta.env.* 的使用都会在构建生产版本时被静态替换

使用

.env文件,通过import.meta.env访问,ts开发需要注意vite-env.d.ts声明环境变量,另外要注意是否将环境变量声明添加到ts的编译路径不然爆红的。

HTML文件中也可以使用%NAME%

html
<h1>Vite is running in %MODE%</h1>
<p>Using data from %VITE_API_URL%</p>

区别NODE_ENV和mode

插件

安装

powershell
$ npm add -D @vitejs/plugin-legacy

然后导入vite.config.ts,并添加到插件列表,有的插件可以传入配置参数。

创作

创作vite插件

vite插件社区

使用

**按需应用:**使用 apply 属性指明它们仅在 'build' 或 'serve' 模式时调用:

typescript
// vite.config.js
import typescript2 from 'rollup-plugin-typescript2'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    {
      ...typescript2(),
      apply: 'build',
    },
  ],
})

内置插件

  • SFC
  • ReactFastRefresh
  • vite-plugin-compress

插件思想

vite插件的设计体现了微内核的设计思想,vite核心只复杂项目启动,构建流等基础功能,而扩展功能被解耦出来,(比如对vue、react语法的支持,压缩混淆等)通过插件实现,满足个性化构建。

基于rollup的插件设计

  • 生命周期钩子:插件能够在合适的时机工作,比如transform(code, file)钩子,build钩子,兼容了rollup的钩子可以使用。
    • rollup 通用钩子
      • options(options):在构建开始之前调用,允许插件修改 Rollup 的选项。
      • buildStart(options):在每次构建开始时调用,插件可以在此执行构建前的初始化操作。
      • resolveId(source, importer, options):在模块解析过程中调用,插件可以在此自定义模块 ID 的解析逻辑。
      • load(id):在加载模块时调用,插件可以在此提供模块的内容。
      • transform(code, id):在转换模块时调用,插件可以在此修改模块的代码。
      • buildEnd(error):在构建结束时调用,插件可以在此执行构建后的操作。
      • generateBundle(options, bundle):在生成 bundle 时调用,插件可以在此修改输出的 bundle。
      • closeBundle():在构建完成并关闭 bundle 时调用,插件可以在此执行清理或其他收尾工作。
    • vite 也有自己特有的钩子
      • config(config, env),解析配置之前
      • configResolved(resolvedConfig) 解析配置之后
      • configureServer(server) 配置开发服务器
      • transformIndexHtml(html) 开发阶段修改index.html内容
      • handleHotUpdate(ctx) HMR中调用,处理模块替换的逻辑
  • 插件接口:提供插件接口方便生态开发者开发
  • 插件执行顺序
    • Alias插件处理路径别名
    • enforce: 'pre'的插件
    • vite核心
    • enforce属性插件
    • enforce: 'post'

例如下列的vite-plugin-compress,在build阶段,读取输出文件并压缩以减小文件大小

typescript
import viteCompression from  'vite-plugin-compress'
export default defineConfig({
  plugins:[viteCompression()]
});

你可以试一试写一个生产环境去除console.log的插件

开发

HMR

server.hmr可以自定义行为避免一些性能瓶颈

测试

vitest等

生产

默认情况下,Vite 的目标是能够 支持原生 ESM script 标签、支持原生 ESM 动态导入 和 import.meta 的浏览器

补充:开发环境下默认是ESNext,尽可能的减少语法转译带来消耗

分块策略

build.rollupOptions.output.manualChunks

rollup文档

vite也有自动代码分割,比如当你使用import懒加载时,产物会看到分出来新的块

MPA

开发时与静态文件服务表现一致

但是构建时需要指定多个html作为入口点

typescript
// vite.config.js
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        nested: resolve(__dirname, 'nested/index.html'),
      },
    },
  },
})

补充:在解析输入路径时,__dirname 的值将仍然是 vite.config.js 文件所在的目录。因此,如果你指定了另一个根目录,你需要把对应入口文件的 root 的路径添加到 resolve 的参数中。

库模式

适用于面向浏览器和 JS 框架的库

非面向浏览器的库,或需要高级构建流程,可以直接使用 Rollup 或 esbuild。

build.lib

配置项

typescript
// vite.config.js
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    lib: {
      // entry属性也可以以字典的形式,或者数组的形式
      entry: resolve(__dirname, 'lib/main.js'),
      name: 'MyLib',
      // the proper extensions will be added
      fileName: 'my-lib',
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue',
        },
      },
    },
  },
})
json
{
  "name": "my-lib",
  "type": "module",
  "files": ["dist"],
  "main": "./dist/my-lib.umd.cjs",
  "module": "./dist/my-lib.js",
  "exports": {
    ".": {
      "import": "./dist/my-lib.js",
      "require": "./dist/my-lib.umd.cjs"
    }
  }
}
json
{
  "name": "my-lib",
  "type": "module",
  "files": ["dist"],
  "main": "./dist/my-lib.cjs",
  "module": "./dist/my-lib.js",
  "exports": {
    ".": {
      "import": "./dist/my-lib.js",
      "require": "./dist/my-lib.cjs"
    },
    "./secondary": {
      "import": "./dist/secondary.js",
      "require": "./dist/secondary.cjs"
    }
  }
}

补充:如果 package.json 不包含 "type": "module",Vite 会生成不同的文件后缀名以兼容 Node.js。.js 会变为 .mjs 而 .cjs 会变为 .js 。

vite5变更

vite过程总结

Vite 本地开发服务

  1. 项目初始化:读取并解析 vite.config.js 配置文件。
  2. 启动开发服务器:基于 express 启动 HTTP 服务器。
  3. ESM 支持:利用浏览器的原生 ESM 进行模块加载。
  4. 按需编译:实时编译请求的模块。
  5. 热模块替换(HMR):通过 WebSocket 实现模块的局部更新。
  6. Source Maps:自动生成 Source Maps,便于调试。

Vite 构建过程

  1. 项目初始化:读取并解析 vite.config.js 配置文件。
  2. 入口解析:使用 Rollup 构建模块依赖图。
  3. 插件处理:通过插件系统进行代码转换、压缩和资源处理。
    1. 代码转换
    2. 压缩
    3. CSS处理
    4. 资源处理
  4. Tree Shaking
  5. 代码拆分:将代码拆分成多个模块。
  6. 生成输出:打包生成最终的输出文件。 一般有如下区分
    1. vendor 第三方依赖
    2. main.js 入口js
    3. index.html 入口html
    4. common.js 公共js
  7. 资源优化:优化 CSS 和静态资源。
  8. 缓存策略:为静态资源添加内容哈希,便于缓存管理。

备注

https://vitejs.cn/vite5-cn/guide/cli.html

  • vite 启动开发服务器
  • tsc && vite build配合ts进行打包
  • vite preview生产预览
  • vite optimize依赖预构建

然后是一些常用参数,查文档吧。。。