功能特性
功能特性
在最基本的层面上,使用 Vite 进行开发与使用静态文件服务器并没有太大区别。然而,Vite 在原生 ESM 导入的基础上提供了许多增强功能,以支持各种通常在基于打包器的设置中才能见到的特性。
npm 依赖解析与预打包
原生 ES 导入不支持像下面这样的裸模块导入:
js
import { someMethod } from 'my-dep'
上面的导入语句在浏览器中会抛出错误。Vite 会检测所有已提供服务源文件中的此类裸模块导入,并执行以下操作:
-
预打包它们以提升页面加载速度,并将 CommonJS / UMD 模块转换为 ESM。预打包步骤由 Rolldown 执行,这使得 Vite 的冷启动时间比任何基于 JavaScript 的打包器都要快得多。
-
将导入重写为有效的 URL,如
/node_modules/.vite/deps/my-dep.js?v=f3sf2ebd,以便浏览器能够正确地导入它们。
依赖项被强力缓存
Vite 通过 HTTP 头缓存依赖请求,因此如果你希望本地编辑/调试某个依赖,请按照此处的步骤操作。
热模块替换
Vite 通过原生 ESM 提供了 HMR API。具备 HMR 能力的框架可以利用该 API 提供即时、精确的更新,而无需重新加载页面或清除应用状态。Vite 为 Vue 单文件组件和 React Fast Refresh提供了第一方的 HMR 集成。此外,还通过 @prefresh/vite为 Preact 提供了官方集成。
请注意,你无需手动设置这些——当你通过 create-vite 创建应用时,所选模板已经为你预配置好了这些功能。
TypeScript
Vite 开箱即用地支持导入 .ts 文件。
仅转译
请注意,Vite 仅对 .ts 文件执行转译,并不执行类型检查。它假定类型检查由你的 IDE 和构建过程负责。
Vite 不在转换过程中执行类型检查的原因是,这两项工作在根本上有所不同。转译可以基于单个文件进行,与 Vite 的按需编译模型完美契合。相比之下,类型检查需要了解整个模块图。将类型检查硬塞进 Vite 的转换管道中,将不可避免地损害 Vite 的速度优势。
Vite 的职责是尽可能快地将你的源代码模块转换为可以在浏览器中运行的形式。为此,我们建议将静态分析检查与 Vite 的转换管道分开。这一原则同样适用于其他静态分析检查,例如 ESLint。
- 对于生产构建,你可以在 Vite 的构建命令之外额外运行
tsc --noEmit。 - 在开发过程中,如果你需要超出 IDE 提示的功能,我们建议在单独的进程中运行
tsc --noEmit --watch,或者如果你希望直接在浏览器中报告类型错误,可以使用 vite-plugin-checker。
Vite 使用 Oxc Transformer将 TypeScript 转译为 JavaScript,其速度比原生的 tsc 更快,并且 HMR 更新可以在 50ms 内反映到浏览器中。
使用仅类型导入和导出语法可以避免潜在的问题,例如仅类型导入被错误地打包,例如:
ts
import type { T } from 'only/types'
export type { T }
TypeScript 编译器选项
Vite 会遵循 tsconfig.json 中的部分选项,并设置相应的 Oxc Transformer 选项。对于每个文件,Vite 使用与该文件匹配的最近的父级 tsconfig.json,或者由其 references 字段引用的且与该文件匹配的配置。当文件满足配置的 files、include 和 exclude 字段时,Vite 将该配置视为与文件匹配。
当选项同时在 Vite 配置和 tsconfig.json 中设置时,Vite 配置中的值优先。
tsconfig.json 中 compilerOptions 下的一些配置字段需要特别注意。
isolatedModules
应设置为 true。
这是因为 Oxc Transformer 仅执行转译而不包含类型信息,它不支持某些特性,如 const enum 和隐式仅类型导入。
你必须在 tsconfig.json 的 compilerOptions 下设置 "isolatedModules": true,以便 TypeScript 针对那些无法与隔离转译一起工作的特性向你发出警告。
如果某个依赖无法与 "isolatedModules": true 很好地配合,你可以使用 "skipLibCheck": true 来暂时抑制错误,直到该问题在上游得到修复。
useDefineForClassFields
如果 TypeScript 的 target 是 ES2022 或更新版本(包括 ESNext),默认值将为 true。这与 TypeScript 4.3.2+ 的行为一致。其他 TypeScript target 默认值为 false。
true 是标准的 ECMAScript 运行时行为。
如果你使用的库高度依赖类字段,请注意该库对其的预期用途。虽然大多数库期望 "useDefineForClassFields": true,但如果你的库不支持,你可以显式地将 useDefineForClassFields 设置为 false。
target
Vite 忽略 tsconfig.json 中的 target 值,与 esbuild 的行为一致。
要在开发环境中指定 target,可以使用 oxc.target 选项,其默认值为 esnext 以进行最小化转译。在构建中,build.target 选项优先于 oxc.target,如有需要也可以进行设置。
emitDecoratorMetadata
此选项仅得到部分支持。完全支持需要 TypeScript 编译器的类型推断,而这目前并不受支持。详情请参阅 Oxc Transformer 的文档。
paths
可以指定 resolve.tsconfigPaths: true 来告诉 Vite 使用 tsconfig.json 中的 paths 选项来解析导入。
请注意,此功能有性能开销,并且 TypeScript 团队不鼓励使用此选项来改变外部工具的行为。
其他影响构建结果的编译器选项
extendsimportsNotUsedAsValuespreserveValueImportsverbatimModuleSyntaxjsxjsxFactoryjsxFragmentFactoryjsxImportSourceexperimentalDecorators
::: tip skipLibCheck
Vite 起始模板默认设置 "skipLibCheck": true,以避免对依赖进行类型检查,因为它们可能只支持特定版本和配置的 TypeScript。你可以在 vuejs/vue-cli#5688 了解更多信息。
客户端类型
Vite 的默认类型是其 Node.js API 的类型。要填充 Vite 应用中客户端代码的环境,你可以在
tsconfig.json的compilerOptions.types中添加vite/client:
json [tsconfig.json]{ "compilerOptions": { "types": ["vite/client", "some-other-global-lib"] } }请注意,如果指定了
compilerOptions.types,则只有这些包会被包含在全局作用域中(而不是所有可见的“@types”包)。自 TS 5.9 起推荐这样做。 details 使用三斜线指令
或者,你可以添加一个 .d.ts 声明文件:
typescript [vite-env.d.ts]
/// <reference types="vite/client" />
vite/client提供以下类型填充:
例如,要使 *.svg 的默认导入成为 React 组件:
vite-env-override.d.ts(包含你的类型定义的文件):tsdeclare module '*.svg' { const content: React.FC<React.SVGProps<SVGElement>> export default content }- 如果你使用
compilerOptions.types,请确保该文件已包含在tsconfig.json中:json [tsconfig.json]{ "include": ["src", "./vite-env-override.d.ts"] } - 如果你使用三斜线指令,请更新包含对
vite/client引用的文件(通常是vite-env.d.ts):ts/// <reference types="./vite-env-override.d.ts" /> /// <reference types="vite/client" />
HTML
HTML 文件在 Vite 项目中处于核心地位,作为你应用的入口点,使得构建单页应用和多页应用变得简单。
项目根目录中的任何 HTML 文件都可以通过其对应的目录路径直接访问:
<root>/index.html->http://localhost:5173/<root>/about.html->http://localhost:5173/about.html<root>/blog/index.html->http://localhost:5173/blog/index.html被 HTML 元素引用的资源,如
<script type="module" src>和<link href>,会被处理并打包为应用的一部分。支持的完整元素列表如下:
<audio src><embed src><img src>和<img srcset><image href>和<image xlink:href><input src><link href>和<link imagesrcset><object data><script type="module" src><source src>和<source srcset><track src><use href>和<use xlink:href><video src>和<video poster><meta content>
- 仅当
name属性匹配msapplication-tileimage、msapplication-square70x70logo、msapplication-square150x150logo、msapplication-wide310x150logo、msapplication-square310x310logo、msapplication-config或twitter:image时- 或者仅当
property属性匹配og:image、og:image:url、og:image:secure_url、og:audio、og:audio:secure_url、og:video或og:video:secure_url时
html {4-5,8-9}<!doctype html> <html> <head> <link rel="icon" href="/favicon.ico" /> <link rel="stylesheet" href="/src/styles.css" /> </head> <body> <img src="/src/images/logo.svg" alt="logo" /> <script type="module" src="/src/main.js"></script> </body> </html>要对某些元素选择退出 HTML 处理,你可以在该元素上添加
vite-ignore属性,这在引用外部资源或 CDN 时非常有用。框架
所有现代框架都维护着与 Vite 的集成。大多数框架插件由各个框架团队维护,但官方的 Vue 和 React Vite 插件除外,它们由 vite 组织维护:
- 通过 @vitejs/plugin-vue 提供 Vue 支持
- 通过 @vitejs/plugin-vue-jsx 提供 Vue JSX 支持
- 通过 @vitejs/plugin-react 提供 React 支持
- 通过 @vitejs/plugin-react-swc 提供使用 SWC 的 React 支持
- 通过 @vitejs/plugin-rsc 提供 React Server Components (RSC) 支持
查看 插件指南 了解更多信息。
JSX
.jsx和.tsx文件也开箱即用地支持。JSX 转译同样由 Oxc Transformer 处理。你选择的框架通常已经配置好了 JSX(例如,Vue 用户应使用官方的 @vitejs/plugin-vue-jsx 插件,它提供了 Vue 3 特有的功能,包括 HMR、全局组件解析、指令和插槽)。
如果你在自己框架中使用 JSX,可以通过
oxc选项 配置自定义的jsxFactory和jsxFragment。例如,Preact 插件会使用:
js twoslash [vite.config.js]import { defineConfig } from 'vite' export default defineConfig({ oxc: { jsx: { importSource: 'preact', }, }, })更多细节请参阅 Oxc Transformer 文档。
你可以使用
jsxInject(这是 Vite 独有的选项)注入 JSX 辅助函数,以避免手动导入:
js twoslash [vite.config.js]import { defineConfig } from 'vite' export default defineConfig({ oxc: { jsxInject: `import React from 'react'`, }, })CSS
导入
.css文件会将其内容通过<style>标签注入到页面中,并支持 HMR。
@import内联与重基Vite 已预先配置为通过
postcss-import支持 CSS@import内联。Vite 别名同样适用于 CSS@import。此外,所有的 CSSurl()引用,即使导入的文件位于不同的目录,也始终会自动重新计算基准路径以确保正确性。
@import别名和 URL 重基同样适用于 Sass 和 Less 文件(参见 CSS 预处理器)。PostCSS
如果项目包含有效的 PostCSS 配置(支持 postcss-load-config 的任何格式,例如
postcss.config.js),它将自动应用于所有导入的 CSS。请注意,CSS 压缩将在 PostCSS 之后运行,并使用
build.cssTarget选项。CSS Modules
任何以
.module.css结尾的 CSS 文件都被视为 CSS modules 文件。导入此类文件将返回相应的模块对象:
css [example.module.css].red { color: red; }
js twoslashimport 'vite/client' // ---cut--- import classes from './example.module.css' document.getElementById('foo').className = classes.redCSS modules 的行为可以通过
css.modules选项进行配置。如果设置了
css.modules.localsConvention以启用 camelCase locals(例如localsConvention: 'camelCaseOnly'),你还可以使用命名导入:
js twoslashimport 'vite/client' // ---cut--- // .apply-color -> applyColor import { applyColor } from './example.module.css' document.getElementById('foo').className = applyColorCSS 预处理器
由于 Vite 仅面向现代浏览器,建议使用原生 CSS 变量,并结合实现 CSSWG 草案的 PostCSS 插件(例如 postcss-nesting),并编写符合未来标准的纯 CSS。
话虽如此,Vite 确实为
.scss、.sass、.less、.styl和.stylus文件提供了内置支持。无需为它们安装 Vite 特定插件,但必须安装相应的预处理器本身:
bash# .scss 和 .sass npm add -D sass-embedded # 或 sass # .less npm add -D less # .styl 和 .stylus npm add -D stylus如果使用 Vue 单文件组件,这也会自动启用
<style lang="sass">等。Vite 改进了 Sass 和 Less 的
@import解析,使得 Vite 别名也能被识别。此外,在导入的 Sass/Less 文件中(与根文件位于不同目录时)的相对url()引用也会自动重新计算基准路径以确保正确性。由于 API 限制,不支持对以变量或插值开头的url()引用进行重基。由于 API 限制,Stylus 不支持
@import别名和 url 重基。你还可以通过在文件扩展名前加上
.module来将 CSS modules 与预处理器结合使用,例如style.module.scss。禁用 CSS 注入页面
可以通过
?inline查询参数关闭 CSS 内容的自动注入。在这种情况下,处理后的 CSS 字符串会像往常一样作为模块的默认导出返回,但样式不会被注入到页面中。
js twoslashimport 'vite/client' // ---cut--- import './foo.css' // 将被注入到页面中 import otherStyles from './bar.css?inline' // 不会被注入 ``` tip 注意
从 Vite 5 开始,CSS 文件的默认导入和命名导入(例如 import style from './foo.css')已被移除。请改用 ?inline 查询参数。
Lightning CSS
Vite 默认使用 Lightning CSS 来压缩生产构建中的 CSS。然而,PostCSS 仍用于其他 CSS 处理。
目前有实验性支持完全使用 Lightning CSS 进行 CSS 处理。你可以通过添加
css.transformer: 'lightningcss'来选择启用它。要进行配置,你可以将 Lightning CSS 选项传递给
css.lightningcss配置选项。要配置 CSS Modules,你应该使用css.lightningcss.cssModules而不是css.modules(后者配置的是 PostCSS 处理 CSS modules 的方式)。静态资源
在 Scrimba 上观看互动课程 导入静态资源将返回其被提供时的解析后的公共 URL:
js twoslashimport 'vite/client' // ---cut--- import imgUrl from './img.png' document.getElementById('hero-img').src = imgUrl特殊查询可以修改资源的加载方式:
js twoslashimport 'vite/client' // ---cut--- // 显式加载资源为 URL(根据文件大小自动内联) import assetAsURL from './asset.js?url'
js twoslashimport 'vite/client' // ---cut--- // 将资源加载为字符串 import assetAsString from './shader.glsl?raw'
js twoslashimport 'vite/client' // ---cut--- // 加载 Web Workers import Worker from './worker.js?worker'
js twoslashimport 'vite/client' // ---cut--- // 构建时将 Web Workers 内联为 base64 字符串 import InlineWorker from './worker.js?worker&inline'更多细节请参阅 静态资源处理。
JSON
JSON 文件可以直接导入——也支持命名导入:
js twoslashimport 'vite/client' // ---cut--- // 导入整个对象 import json from './example.json' // 将根字段作为命名导出导入——有助于 tree-shaking! import { field } from './example.json'Glob 导入
Vite 支持通过特殊的
import.meta.glob函数从文件系统导入多个模块:
js twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob('./dir/*.js')上面的代码会被转换为以下形式:
js// vite 生成的代码 const modules = { './dir/bar.js': () => import('./dir/bar.js'), './dir/foo.js': () => import('./dir/foo.js'), }然后你可以遍历
modules对象的键来访问相应的模块:
jsfor (const path in modules) { modules[path]().then((mod) => { console.log(path, mod) }) }匹配的文件默认通过动态导入进行惰性加载,并在构建时分割为独立的块。如果你希望直接导入所有模块(例如,依赖这些模块中的副作用首先被执行),你可以传入
{ eager: true }作为第二个参数:
js twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob('./dir/*.js', { eager: true })上面的代码会被转换为以下形式:
js// vite 生成的代码 import * as __vite_glob_0_0 from './dir/bar.js' import * as __vite_glob_0_1 from './dir/foo.js' const modules = { './dir/bar.js': __vite_glob_0_0, './dir/foo.js': __vite_glob_0_1, }多个模式
第一个参数可以是一个 glob 数组,例如:
js twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob(['./dir/*.js', './another/*.js'])否定模式
也支持否定 glob 模式(以
!开头)。要从结果中忽略某些文件,你可以向第一个参数添加排除 glob 模式:
js twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob(['./dir/*.js', '!**/bar.js'])
js// vite 生成的代码 const modules = { './dir/foo.js': () => import('./dir/foo.js'), }命名导入
可以通过
import选项仅导入模块的一部分。
ts twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob('./dir/*.js', { import: 'setup' })
ts// vite 生成的代码 const modules = { './dir/bar.js': () => import('./dir/bar.js').then((m) => m.setup), './dir/foo.js': () => import('./dir/foo.js').then((m) => m.setup), }当与
eager结合使用时,甚至可以为这些模块启用 tree-shaking。
ts twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob('./dir/*.js', { import: 'setup', eager: true, })
ts// vite 生成的代码: import { setup as __vite_glob_0_0 } from './dir/bar.js' import { setup as __vite_glob_0_1 } from './dir/foo.js' const modules = { './dir/bar.js': __vite_glob_0_0, './dir/foo.js': __vite_glob_0_1, }将
import设置为default以导入默认导出。
ts twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob('./dir/*.js', { import: 'default', eager: true, })
ts// vite 生成的代码: import { default as __vite_glob_0_0 } from './dir/bar.js' import { default as __vite_glob_0_1 } from './dir/foo.js' const modules = { './dir/bar.js': __vite_glob_0_0, './dir/foo.js': __vite_glob_0_1, }自定义查询
你还可以使用
query选项为导入提供查询参数,例如,将资源导入为字符串或为 URL:
ts twoslashimport 'vite/client' // ---cut--- const moduleStrings = import.meta.glob('./dir/*.svg', { query: '?raw', import: 'default', }) const moduleUrls = import.meta.glob('./dir/*.svg', { query: '?url', import: 'default', })
ts// vite 生成的代码: const moduleStrings = { './dir/bar.svg': () => import('./dir/bar.svg?raw').then((m) => m['default']), './dir/foo.svg': () => import('./dir/foo.svg?raw').then((m) => m['default']), } const moduleUrls = { './dir/bar.svg': () => import('./dir/bar.svg?url').then((m) => m['default']), './dir/foo.svg': () => import('./dir/foo.svg?url').then((m) => m['default']), }你还可以为其他插件提供自定义查询以供其消费:
ts twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob('./dir/*.js', { query: { foo: 'bar', bar: true }, })基准路径
你还可以使用
base选项为导入提供基准路径:
ts twoslashimport 'vite/client' // ---cut--- const modulesWithBase = import.meta.glob('./**/*.js', { base: './base', })
ts// vite 生成的代码: const modulesWithBase = { './dir/foo.js': () => import('./base/dir/foo.js'), './dir/bar.js': () => import('./base/dir/bar.js'), }
base选项只能是相对于导入者的目录路径,或相对于项目根的绝对路径。不支持别名和虚拟模块。只有相对路径的 glob 才会被解释为相对于解析后的基准路径。
所有生成的模块键在提供
base选项时都会基于该路径进行修改,使其相对于基准路径。区分大小写的匹配
默认情况下,glob 模式匹配是区分大小写的。你可以使用
caseSensitive选项来改变这种行为:
ts twoslashimport 'vite/client' // ---cut--- const modules = import.meta.glob('./dir/module*.js', { caseSensitive: false, })使用
caseSensitive: false时,glob 将不区分大小写地匹配文件(例如,Module.js、module.js、MODULE.js都将被module*.js匹配)。Glob 导入注意事项
请注意:
- 这是 Vite 独有的功能,不是 Web 或 ES 标准。
- glob 模式被视为导入说明符:它们必须是相对路径(以
./开头)、绝对路径(以/开头,相对于项目根解析)或别名路径(参见resolve.alias选项)。- glob 匹配通过
tinyglobby完成——请查看其文档以了解支持的 glob 模式。- 你还应该注意,
import.meta.glob中的所有参数必须作为字面量传递。你不能在它们中使用变量或表达式。动态导入
与 glob 导入类似,Vite 也支持带变量的动态导入。
tsconst module = await import(`./dir/${file}.js`)请注意,变量仅表示一层深的文件名。如果
file是'foo/bar',则导入会失败。对于更高级的用法,你可以使用 glob 导入 功能。另外请注意,动态导入必须满足以下规则才能被打包:
- 导入必须以
./或../开头:import(`./dir/${foo}.js`)是有效的,但import(`${foo}.js`)是无效的。- 导入必须以文件扩展名结尾:
import(`./dir/${foo}.js`)是有效的,但import(`./dir/${foo}`)是无效的。- 对自身目录的导入必须指定文件名模式:
import(`./prefix-${foo}.js`)是有效的,但import(`./${foo}.js`)是无效的。强制这些规则是为了防止意外导入不打算被打包的文件。例如,如果没有这些规则,
import(foo)将打包文件系统中的所有内容。WebAssembly
Vite 支持以两种方式导入预编译的
.wasm文件:当你只需要模块的导出时,直接作为 ES 模块导入;或者当你需要对实例化进行显式控制时,使用?init导入。ESM 集成
可以直接导入
.wasm文件。Vite 从二进制文件中读取模块的导入和导出,实例化它,并将其导出重新暴露为命名的 ES 模块导出:
jsimport { add } from './add.wasm' console.log(add(1, 2)) // 3如果 WebAssembly 模块声明了自己的导入,Vite 会从 JavaScript 模块中解析它们。每个导入的模块名被视为导入说明符(相对于
.wasm文件解析),并且请求的成员会自动连接到实例中。这遵循 WebAssembly/ES Module 集成提案。由于 WebAssembly 模块是异步实例化的,直接导入的
.wasm文件表现为异步模块,需要顶级await支持。 tip TypeScript 支持
由于 .wasm 文件的类型是未知的,TypeScript 会报告诸如 Module '"*.wasm"' has no exported member 'add' 的错误。要解决此问题,请在 tsconfig.json 中启用 allowArbitraryExtensions,并在 .wasm 文件旁边创建一个声明文件。启用 allowArbitraryExtensions 后,TypeScript 在解析 .wasm 导入时会查找名为 {filename}.d.wasm.ts 的声明文件。例如,对于 add.wasm,创建 add.d.wasm.ts:
ts [add.d.wasm.ts]
export function add(a: number, b: number): number
手动初始化
当你需要控制模块实例化的时间和方式时,使用
?init导入它。默认导出将是一个初始化函数,返回一个WebAssembly.Instance的 Promise:
js twoslashimport 'vite/client' // ---cut--- import init from './example.wasm?init' init().then((instance) => { instance.exports.test() })
init函数还可以接受一个 importObject,它作为第二个参数传递给WebAssembly.instantiate:
js twoslashimport 'vite/client' import init from './example.wasm?init' // ---cut--- init({ imports: { someFunc: () => { /* ... */ }, }, }).then(() => { /* ... */ })在生产构建中,小于
assetsInlineLimit的.wasm文件将作为 base64 字符串内联。否则,它们将被视为静态资源并按需获取。 warning 对于 SSR 构建,仅支持 Node.js 兼容运行时
由于缺乏通用的文件加载方式,直接导入 .wasm 和 .wasm?init 的内部实现都依赖于 node:fs 模块。这意味着这些特性仅适用于 SSR 构建中的 Node.js 兼容运行时。
访问 WebAssembly 模块
如果你需要访问
Module对象,例如要多次实例化它,请使用显式 URL 导入来解析资源,然后执行实例化:
js twoslashimport 'vite/client' // ---cut--- import wasmUrl from 'foo.wasm?url' const main = async () => { const responsePromise = fetch(wasmUrl) const { module, instance } = await WebAssembly.instantiateStreaming(responsePromise) /* ... */ } main()Web Workers
使用构造函数导入
可以使用
new Worker()和new SharedWorker()导入 Web Worker 脚本。与 worker 后缀相比,这种语法更接近标准,是创建 worker 的推荐方式。
tsconst worker = new Worker(new URL('./worker.js', import.meta.url))Worker 构造函数也接受选项,可用于创建“模块”worker:
tsconst worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module', })只有在
new Worker()声明中直接使用new URL()构造函数时,worker 检测才会生效。否则,它将被视为静态资源 URL。此外,所有选项参数必须是静态值(即字符串字面量)。使用查询后缀导入
可以通过在导入请求后附加
?worker或?sharedworker来直接导入 Web Worker 脚本。默认导出将是一个自定义的 worker 构造函数:
js twoslashimport 'vite/client' // ---cut--- import MyWorker from './worker?worker' const worker = new MyWorker()Worker 脚本也可以使用 ESM
import语句,而不是importScripts()。注意:在开发环境中,这依赖于浏览器原生支持,但在生产构建中会被编译掉。默认情况下,worker 脚本在生产构建中会作为单独的块输出。如果你希望将 worker 作为 base64 字符串内联,请添加
inline查询:
js twoslashimport 'vite/client' // ---cut--- import MyWorker from './worker?worker&inline'如果你希望将 worker 作为 URL 获取,请添加
url查询:
js twoslashimport 'vite/client' // ---cut--- import MyWorker from './worker?worker&url'有关配置所有 worker 打包的详细信息,请参阅 Worker 选项。
内容安全策略 (CSP)
要部署 CSP,由于 Vite 的内部机制,必须设置某些指令或配置。
'nonce-{RANDOM}'当设置了
html.cspNonce时,Vite 会向所有<script>和<style>标签,以及用于样式表和模块预加载的<link>标签添加带有指定值的 nonce 属性。此外,当设置此选项时,Vite 会注入一个 meta 标签(<meta property="csp-nonce" nonce="PLACEHOLDER" />)。具有
property="csp-nonce"的 meta 标签的 nonce 值将在开发和生产构建后被 Vite 在必要时使用。warning
确保你为每个请求替换占位符为唯一值。这对于防止绕过资源的策略很重要,否则很容易被绕过。
data:默认情况下,在构建期间,Vite 会将小的资源内联为 data URI。需要为相关指令(例如
img-src、font-src)允许data:,或者通过设置build.assetsInlineLimit: 0禁用它。warning
不要为script-src允许data:。这将允许注入任意脚本。
:::
许可证
Vite 可以使用 build.license 选项生成一个包含构建中使用的所有依赖许可证的文件。它可以被托管以展示和确认应用所使用的依赖。
js twoslash [vite.config.js]
import { defineConfig } from 'vite'
export default defineConfig({
build: {
license: true,
},
})
这将生成一个 .vite/license.md 文件,输出可能如下所示:
md
# Licenses
The app bundles dependencies which contain the following licenses:
## dep-1 - 1.2.3 (CC0-1.0)
CC0 1.0 Universal
...
## dep-2 - 4.5.6 (MIT)
MIT License
...
要将文件提供到其他路径,你可以传入例如 { fileName: 'license.md' },这样它会在 https://example.com/license.md 被提供。有关更多信息,请参阅 build.license 文档。
构建优化
下面列出的功能会在构建过程中自动应用(实验性的块导入映射功能除外),除非你想禁用它们,否则无需显式配置。
CSS 代码分割
Vite 会自动提取异步块中模块使用的 CSS,并为其生成单独的文件。当关联的异步块被加载时,该 CSS 文件会通过 <link> 标签自动加载,并且保证异步块在 CSS 加载完成之后才会被求值,以避免 FOUC。
如果你希望将所有 CSS 提取到单个文件中,可以通过将 build.cssCodeSplit 设置为 false 来禁用 CSS 代码分割。
预加载指令生成
Vite 会自动为构建后 HTML 中的入口块及其直接导入生成 <link rel="modulepreload"> 指令。
异步块加载优化
在真实世界的应用中,Rollup 通常会生成“公共”块——即两个或多个其他块之间共享的代码。结合动态导入,出现以下场景是很常见的:
在未优化的场景中,当异步块 A 被导入时,浏览器必须先请求并解析 A,然后才能发现它还需要公共块 C。这会导致额外的网络往返:
Entry ---> A ---> C
Vite 会自动重写代码分割的动态导入调用,加入预加载步骤,这样当请求 A 时,C 会并行获取:
Entry ---> (A + C)
C 可能还有进一步的导入,这在未优化的场景中会导致更多的往返。Vite 的优化会追踪所有直接导入,以完全消除这些往返,无论导入深度如何。
块导入映射优化
为了提高块的缓存命中率,Vite 可以为块创建导入映射。这可以防止级联缓存失效问题,这是 ES Modules 的一个问题。
例如,考虑以下场景:
Entry --> A ---> C
如果 C 被更新,本来就只需要使 C 失效。然而,如果 A 通过静态导入中的普通 URL 引用 C(即 C 的哈希包含在 URL 中),那么 A 的内容就会改变,因此 A 也需要失效。Entry 也是如此。
通过利用导入映射功能,可以避免此问题。启用此优化后,Vite 会创建一个导入映射,将每个块的 ID 映射到其 URL,并在导入语句中使用块 ID 而不是 URL。这样,当某个块更新时,只有被更新的块需要失效,而引用它的块不会失效。
请注意,此优化目前不适用于 CSS 和资源。如果你更新了资源,引用它的块会失效。也就是说,失效不会级联,导入已失效块的块不会被失效。
要启用此功能,请将 build.chunkImportMap 设置为 true。
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
