从 v4 迁移
Node.js 支持
Vite 不再支持 Node.js 14 / 16 / 17 / 19,因为它们已经到了 EOL。现在需要 Node.js 18 / 20+。
Rollup 4
Vite 现在使用 Rollup 4,它也带来了一些重大的变化,特别是:
- 导入断言(
assertions
属性)已被重命名为导入属性(attributes
属性)。 - 不再支持 Acorn 插件。
- 对于 Vite 插件,
this.resolve
的skipSelf
选项现在默认为true
。 - 对于 Vite 插件,
this.parse
现在只支持allowReturnOutsideFunction
选项。
你可以阅读 Rollup 的发布说明 中的破坏性变更,了解在 build.rollupOptions
中构建相关的变更。
如果你正在使用 TypeScript,请确保设置 moduleResolution: 'bundler'
(或 node16
/nodenext
)因为 Rollup 4 需要它。或者你可以设置 skipLibCheck: true
。
废弃 CJS Node API
CJS 的 Node API 已经被废弃。当调用 require('vite')
时,将会记录一个废弃警告。你应该更新你的文件或框架来导入 Vite 的 ESM 构建。
在一个基础的 Vite 项目中,请确保:
vite.config.js
配置文件的内容使用 ESM 语法。- 最近的
package.json
文件中有"type": "module"
,或者使用.mjs
/.mts
扩展名,例如vite.config.mjs
或者.vite.config.mts
。
对于其他项目,有几种常见的方法:
- 配置 ESM 为默认,如果需要则选择 CJS: 在项目
package.json
中添加"type": "module"
。所有*.js
文件现在都被解释为 ESM,并且需要使用 ESM 语法。你可以将一个文件重命名为.cjs
扩展名来继续使用 CJS。 - 保持 CJS 为默认,如果需要则选择 ESM: 如果项目
package.json
没有"type": "module"
,所有*.js
文件都被解释为 CJS。你可以将一个文件重命名为.mjs
扩展名来使用 ESM。 - 动态导入 Vite: 如果你需要继续使用 CJS,你可以使用
import('vite')
动态导入 Vite。这要求你的代码必须在一个async
上下文中编写,但是由于 Vite 的 API 大多是异步的,所以应该还是可以管理的。
查看 排错指南 获取更多信息。
重新设计 define
和 import.meta.env.*
的替换策略
在 Vite 4 中,define
和 import.meta.env.*
特性在开发和构建中使用的是不同的替换策略:
- 在开发时,这两个特性分别作为全局变量注入到
globalThis
和import.meta
中。 - 在构建时,这两个特性都使用正则表达式进行静态替换。
这导致在尝试访问这些变量时,开发和构建存在一致性问题,有时甚至导致构建失败。例如:
// vite.config.js
export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify('1.0.0'),
},
})
const data = { __APP_VERSION__ }
// 开发:{ __APP_VERSION__: "1.0.0" } ✅
// 构建:{ "1.0.0" } ❌
const docs = 'I like import.meta.env.MODE'
// 开发:"I like import.meta.env.MODE" ✅
// 构建:"I like "production"" ❌
Vite 5 通过在构建中使用 esbuild
来处理替换,使其与开发行为保持一致。
这个改动不应该影响大部分设置,因为已经在文档中说明了 define
的值应该遵循 esbuild 的语法:
为了与 esbuild 行为保持一致,表达式必须是一个 JSON 对象(null、boolean、number、string、array 或 object)或一个单一标识符字符串。
然而,如果你更喜欢对值直接使用静态替换,你可以使用 @rollup/plugin-replace
。
其他一般性变化
SSR 外部模块值现在符合生产环境行为
在 Vite 4 中,服务器端渲染的外部模块被包装为 .default
和 .__esModule
处理,以实现更好的互操作性,但是它并不符合运行时环境(例如 Node.js)加载时的生产环境行为,导致难以捕获的不一致性。默认情况下,所有直接的项目依赖都是 SSR 外部化的。
Vite 5 现在删除了 .default
和 .__esModule
处理,以匹配生产环境行为。在实践中,这不应影响正确打包的依赖项,但是如果你在加载模块时遇到新的问题,你可以尝试以下重构:
// 之前:
import { foo } from 'bar'
// 之后:
import _bar from 'bar'
const { foo } = _bar
// 之前:
import foo from 'bar'
// 之后:
import * as _foo from 'bar'
const foo = _foo.default
注意,这些更改符合 Node.js 的行为,因此也可以在 Node.js 中运行这些导入进行测试。如果你更喜欢坚持使用之前的方式,你可以将 legacy.proxySsrExternalModules
设置为 true
。
worker.plugins
现在是一个函数
在 Vite 4 中,worker.plugins
接受一个插件数组 ((Plugin | Plugin[])[]
)。从 Vite 5 开始,它需要配置为一个返回插件数组的函数 (() => (Plugin | Plugin[])[]
)。这个改变是为了让并行的 worker 构建运行得更加一致和可预测。
允许路径包含 .
回退到 index.html
在 Vite 4 中,即使 appType
被设置为 'SPA'
(默认),访问包含 .
的路径也不会回退到 index.html。从 Vite 5 开始,它将会回退到 index.html。
注意浏览器将不再在控制台中显示 404 错误消息,如果你将图片路径指向一个不存在的文件(例如 <img src="./file-does-not-exist.png">
)。
Align dev and preview HTML serving behaviour
在 Vite 4 中,开发服务器和预览服务器会根据 HTML 的目录结构和尾部斜杠的不同来提供 HTML。这会导致在测试构建后的应用时出现不一致的情况。Vite 5 重构成了一个单一的行为,如下所示,给定以下文件结构:
├── index.html
├── file.html
└── dir
└── index.html
请求 | 过往版本 (dev) | 过往版本 (preview) | 现在 (dev & preview) |
---|---|---|---|
/dir/index.html | /dir/index.html | /dir/index.html | /dir/index.html |
/dir | /index.html (SPA fallback) | /dir/index.html | /index.html (SPA fallback) |
/dir/ | /dir/index.html | /dir/index.html | /dir/index.html |
/file.html | /file.html | /file.html | /file.html |
/file | /index.html (SPA fallback) | /file.html | /file.html |
/file/ | /index.html (SPA fallback) | /file.html | /index.html (SPA fallback) |
Manifest 文件现在默认生成到 .vite
目录中
在 Vite 4 中,manifest 文件(build.manifest
,build.ssrManifest
)默认会生成在 build.outDir
的根目录中。
从 Vite 5 开始,这些文件将默认生成在 build.outDir
中的 .vite
目录中。这个改变有助于解决当公共文件被复制到 build.outDir
时,具有相同 manifest 文件名时的冲突。
对应的 CSS 文件未在 manifest.json 文件中作为顶级入口列出
在 Vite 4 中,JavaScript 入口起点的对应 CSS 文件也被列为了 manifest 文件的顶级入口(build.manifest
)。这些条目是非故意添加的,仅对简单情况有效。
在 Vite 5 中,对应的 CSS 文件只能在 JavaScript 入口起点中找到。 在注入 JS 文件时,对应的 CSS 文件 应被注入。 当需要单独注入 CSS 时,必需将其添加为单独的入口起点。
CLI 快捷功能键需要一个额外的 Enter
按键
CLI 快捷功能键,例如 r
重启开发服务器,现在需要额外的 Enter
按键来触发快捷功能。例如,r + Enter
重启开发服务器。
这个改动防止 Vite 吞噬和控制操作系统特定的快捷键,允许更好的兼容性,当将 Vite 开发服务器与其他进程结合使用时,并避免了之前的注意事项。
Update experimentalDecorators
and useDefineForClassFields
TypeScript behaviour
Vite 5 使用 esbuild 0.19 并移除了 esbuild 0.18 的兼容层,这改变了 experimentalDecorators
和 useDefineForClassFields
的处理方式。
useDefineForClassFields
默认不启用你需要在
tsconfig.json
中设置compilerOptions.experimentalDecorators
为true
来使用装饰器。useDefineForClassFields
默认依赖 TypeScript 的target
值如果
target
不是ESNext
或ES2022
或更新的版本,或者没有tsconfig.json
文件,useDefineForClassFields
将默认为false
,这可能会导致默认的esbuild.target
值esnext
出现问题。它可能会转译为静态初始化块,这在你的浏览器中可能不被支持。因此,建议在配置
tsconfig.json
时将target
设置为ESNext
或ES2022
或更新的版本,或者将useDefineForClassFields
显式地设置为true
。
{
"compilerOptions": {
// 若要使用装饰器就设为 true
"experimentalDecorators": true,
// 如果你在浏览器中看到解析错误,请设置为 true
"useDefineForClassFields": true,
},
}
移除 --https
标志和 https: true
--https
标志会在内部设置 server.https: true
和 preview.https: true
。这个配置本来是为了与自动 https 证书生成功能一起使用的,但是这个功能在Vite 3 中被移除。因此,这个配置已经不再有用,因为它会启动一个没有证书的 Vite HTTPS 服务器。
如果你使用 @vitejs/plugin-basic-ssl
或者 vite-plugin-mkcert
,它们已经在内部设置了 https
配置,所以你可以在你的设置中移除 --https
,server.https: true
,和preview.https: true
。
移除 resolvePackageEntry
和 resolvePackageData
API
resolvePackageEntry
和 resolvePackageData
API 已被移除,因为它们暴露了 Vite 的内部机制,并在过去阻碍了 Vite 4.3 的潜在优化。这些 API 可以被第三方包替代,例如:
resolvePackageEntry
:import.meta.resolve
或者import-meta-resolve
库。resolvePackageData
: 与上述相同,向上爬取包目录以获取根package.json
。或者使用社区的vitefu
库。
import { resolve } from 'import-meta-resolve'
import { findDepPkgJsonPath } from 'vitefu'
import fs from 'node:fs'
const pkg = 'my-lib'
const basedir = process.cwd()
// `resolvePackageEntry`:
const packageEntry = resolve(pkg, basedir)
// `resolvePackageData`:
const packageJsonPath = findDepPkgJsonPath(pkg, basedir)
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
移除部分废弃 API
- CSS 文件的默认导出(例如
import style from './foo.css'
):使用?inline
查询参数代替 import.meta.globEager
:使用import.meta.glob('*', { eager: true })
来代替ssr.format: 'cjs'
和legacy.buildSsrCjsExternalHeuristics
(#13816)server.middlewareMode: 'ssr'
和server.middlewareMode: 'html'
:使用appType
+server.middlewareMode: true
来代替(#8452)
进阶
下列改动仅会影响到插件/工具的作者:
- [#14119] refactor!: merge
PreviewServerForHook
intoPreviewServer
type- The
configurePreviewServer
hook now accepts thePreviewServer
type instead ofPreviewServerForHook
type.
- The
- [#14818] refactor(preview)!: use base middleware
- Middlewares added from the returned function in
configurePreviewServer
now does not have access to thebase
when comparing thereq.url
value. This aligns the behaviour with the dev server. You can check thebase
from theconfigResolved
hook if needed.
- Middlewares added from the returned function in
- [#14834] fix(types)!: expose httpServer with Http2SecureServer union
http.Server | http2.Http2SecureServer
is now used instead ofhttp.Server
where appropriate.
此外,还有其他一些只影响少数用户的破坏性变化。
- [#14098] fix!: avoid rewriting this (reverts #5312)
- 之前顶层
this
将会在构建时被默认地改写为globalThis
,这个行为现在已被移除
- 之前顶层
- [#14231] feat!: add extension to internal virtual modules
- 内置虚拟模块的 id 现在包含一个扩展名(
.js
)
- 内置虚拟模块的 id 现在包含一个扩展名(
- [#14583] refactor!: remove exporting internal APIs
- 移除意外导出的内部 API:
isDepsOptimizerEnabled
和getDepOptimizationConfig
- 移除导出的内部类型:
DepOptimizationResult
,DepOptimizationProcessing
和DepsOptimizer
- 改名
ResolveWorkerOptions
类型为ResolvedWorkerOptions
- 移除意外导出的内部 API:
- [#5657] fix: return 404 for resources requests outside the base path
- 过去,Vite 对于不带
Accept: text/html
的请求,会将其当作带有基础路径的请求来处理。现在 Vite 不再这样做,而是返回 404。
- 过去,Vite 对于不带
- [#14723] fix(resolve)!: remove special .mjs handling
- 在过去,当一个库的
"exports"
字段映射到一个.mjs
文件时,Vite 仍然会尝试匹配"browser"
和"module"
字段,以修复与某些库的兼容性。现在,这种行为已被移除,以便与导出解析算法保持一致。
- 在过去,当一个库的
- [#14733] feat(resolve)!: remove
resolve.browserField
resolve.browserField
已从 Vite 3 开始被弃用,而是使用resolve.mainFields
的更新默认值['browser', 'module', 'jsnext:main', 'jsnext']
。
- [#14855] feat!: add isPreview to ConfigEnv and resolveConfig
- 在
ConfigEnv
对象中,重命名ssrBuild
为isSsrBuild
。
- 在
- [#14945] fix(css): correctly set manifest source name and emit CSS file
- CSS 文件名现在是基于 chunk 名生成的。
从 v3 迁移
请先查看 从 v3 迁移指南 文档查看对您的应用所有需要迁移的改动,然后再执行本篇指南所述的改动。