从根本上来说,使用 Vite 进行开发与使用静态文件服务器没有太大区别。但是,Vite 相对于本机 ESM 导入提供了许多增强功能,以支持基于捆绑器的设置中常见的各种功能。
问题描述
原生 ES 导入不支持裸模块(bare module)导入,如下所示:如下代码将在浏览器中引发错误
import { someMethod } from 'my-dep'
vite解决
Vite 将在所有提供的源文件中检测此类裸模块导入并执行以下操作:
(使用 esbuild 执行预捆绑)预捆绑它们以提高页面加载速度并将 CommonJS / UMD 模块转换为 ESM。
这也是 Vite 的冷启动时间明显快于任何基于 JavaScript 的捆绑器的原因之一
将导入重写为有效的 URL,例如 /node_modules/.vite/deps/my-dep.js?v=f3sf2ebd
,以便浏览器可以正确导入它们。
Vite 通过 HTTP 标头缓存依赖项请求,已解析的依赖项请求使用 HTTP 标头 max-age=31536000,immutable
进行强缓存,以提高开发期间的页面重新加载性能。
一旦缓存,这些请求将永远不会再次到达开发服务器。
如果安装了不同的版本(在包管理器锁定文件中有新的版本反映),缓存会因为新附加的不同版本请求而自动失效。
如果您想通过进行本地编辑来调试依赖项,您可以:
通过浏览器开发工具的“网络”选项卡暂时禁用缓存;
使用 --force
标志重新启动 Vite 开发服务器以重新捆绑 deps;
重新加载页面。
Vite 在原生 ESM 上提供 HMR API。
具有 HMR 功能的框架可以利用 API 提供即时、精确的更新,而无需重新加载页面或破坏应用程序状态。
Vite 为 Vue 单文件组件和 React Fast Refresh 提供第一方 HMR 集成。
vite也提供通过 @prefresh/vite 的 Preact 官方集成。
注意
当您通过 create-vite
创建应用程序时,您就不再需要手动设置热更新集成,,所选模板已经为您预先配置了
Vite 支持直接导入 .ts
文件。
什么是仅转译
Vite 仅对 .ts
文件执行转译,不执行类型检查。它假设类型检查由您的 IDE 和构建过程负责。
为什么仅转译
Vite 不在转换过程中执行类型检查的原因是这两个作业的工作方式根本不同
转译可以在每个文件的基础上工作,并与 Vite 的按需编译模型完美契合
类型检查需要了解整个模块图,对 Vite 的转换管道进行类型检查将不可避免地损害 Vite 的速度优势。
我们如何做仅转译
Vite 的工作就是将你的源模块转化为可以在浏览器中尽可能快地运行的形式。因此,建议:
将静态分析检查(例如 ESLint)与 Vite 的转换管道分开
对于生产构建,除了 Vite 的构建命令之外,您还可以运行 tsc --noEmit
。
在开发过程中,如果您需要的不仅仅是 IDE 提示,我们建议在单独的进程中运行 tsc --noEmit --watch
,或者如果您希望直接在浏览器中报告类型错误,请使用 vite-plugin-checker。
tsc --noEmit --watch说明
tsc --noEmit --watch
命令是 TypeScript 编译器 (TypeScript Compiler) 的命令行选项之一。这个命令的作用是启动 TypeScript 编译器并监视指定的文件,当文件发生变化时,自动进行增量编译。这意味着编译器会在后台持续监视文件的变化,并在检测到变化时自动重新编译文件,而不需要手动执行编译命令。
--noEmit
选项告诉 TypeScript 编译器不生成任何输出文件,这意味着编译器只会检查代码并报告潜在的错误,而不会生成实际的 JavaScript 文件。
--watch
选项则启用了监视模式,使得编译器能够持续监视指定的文件,并在文件发生变化时自动进行增量编译。
这个命令通常用于开发过程中,以便在代码发生变化时自动进行编译,从而加快开发周期并帮助开发人员及时发现代码错误。
Vite转译
使用 esbuild 将 TypeScript 转译为 JavaScript,速度比普通 tsc
快约 20~30 倍,并且 HMR 更新可以在 50 毫秒内反映在浏览器中。
使用仅类型导入和导出语法可以避免潜在问题,例如错误捆绑仅类型导入,例如
import type { T } from 'only/types'
export type { T }
tsconfig.json
中 compilerOptions
下的一些配置字段需要特别注意。
isolatedModules
应设置为 true
。- 原因: ? ? - `esbuild` 仅执行没有类型信息的转译,它不支持某些功能,例如 const enum 和隐式仅类型导入。 ? ? - 设置 `"isolatedModules": true` ,以便 TS 会针对不适用于隔离转译的功能发出警告。 ? - 注意 ? ? - 某些库(例如 `vue` )不能与 `"isolatedModules": true` 很好地配合。您可以使用 `"skipLibCheck": true` 暂时抑制错误,直到上游修复该错误。
useDefineForClassFields
默认值
从 Vite 2.5.0 开始,如果 TypeScript 目标是 ESNext
或 ES2022
或更新版本,则默认值为 true
。它与 tsc
4.3.2 及更高版本的行为一致。这也是标准的 ECMAScript 运行时行为。
其他 TypeScript 目标将默认为 false
。
注意
如果您使用的库严重依赖类字段,请注意该库的预期用途。
大多数库都需要 "useDefineForClassFields": true
,例如 MobX。
一些库尚未转换为这个新的默认值,包括 lit-element
。在这些情况下,请明确将 useDefineForClassFields
设置为 false
。
target
默认情况
Vite 不会使用配置的 target
值转译 TypeScript,其行为与 esbuild
相同。
如何设置转译目标
可以使用 esbuild.target
选项来代替,默认为 esnext
以最小化转译。在构建中, build.target
选项具有更高的优先级,如果需要也可以设置。
useDefineForClassFields
问题:
如果 target
不是 ESNext
或 ES2022
或更新版本,或者没有 tsconfig.json
文件, useDefineForClassFields
将默认到 false
这对于 esnext
的默认 esbuild.target
值可能会出现问题。它可能会转换为您的浏览器不支持的静态初始化块
建议
将 target
设置为 ESNext
或 ES2022
或更高版本,
或在配置 tsconfig.json
时明确显示将 useDefineForClassFields
设置为 true
[extends](https://www.typescriptlang.org/tsconfig#extends)
[importsNotUsedAsValues](https://www.typescriptlang.org/tsconfig#importsNotUsedAsValues)
[preserveValueImports](https://www.typescriptlang.org/tsconfig#preserveValueImports)
[verbatimModuleSyntax](https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax)
[jsx](https://www.typescriptlang.org/tsconfig#jsx)
[jsxFactory](https://www.typescriptlang.org/tsconfig#jsxFactory)
[jsxFragmentFactory](https://www.typescriptlang.org/tsconfig#jsxFragmentFactory)
[jsxImportSource](https://www.typescriptlang.org/tsconfig#jsxImportSource)
[experimentalDecorators](https://www.typescriptlang.org/tsconfig#experimentalDecorators)
[alwaysStrict](https://www.typescriptlang.org/tsconfig#alwaysStrict)
Vite 入门模板默认具有 "skipLibCheck": "true"
以避免类型检查依赖性,因为它们可能选择仅支持 TypeScript 的特定版本和配置。您可以在 vuejs/vue-cli#5688 了解更多信息。
Vite 的默认类型适用于其 Node.js API。要在 Vite 应用程序中填充客户端代码的环境,
选择1:请添加 d.ts
声明文件:
/// <reference types="vite/client" />
选择2:将 vite/client
添加到 tsconfig.json
内的 compilerOptions.types
:
{
"compilerOptions": {
"types": ["vite/client"]
}
}
这将提供以下类型的垫片:
Asset资产导入(例如导入 .svg
文件)
import.meta.envimport.meta.env
上 Vite 注入的环境变量的类型
import.meta.hotimport.meta.hot
上 HMR API 的类型
要覆盖默认vite/client
类型
请添加包含您的类型的类型定义文件
在 vite/client
之前添加类型引用。
例如,要默认导入 *.svg
一个 React 组件:
vite-env-override.d.ts
(包含您输入内容的文件):declare module '*.svg' {
const content: React.FC<React.SVGProps<SVGElement>>
export default content
}
vite/client
引用的文件:/// <reference types="./vite-env-override.d.ts" />
/// <reference types="vite/client" />
Vite 提供一流的 Vue 支持:
通过 @vitejs/plugin-vue 支持 Vue 3 SFC
通过 @vitejs/plugin-vue-jsx 支持 Vue 3 JSX
通过 @vitejs/plugin-vue2 支持 Vue 2.7 SFC
通过 @vitejs/plugin-vue2-jsx 支持 Vue 2.7 JSX
对jsx的支持情况
.jsx
和 .tsx
文件也受到开箱即用的支持。 JSX 转译也是通过 esbuild 处理的。
相关的jsx插件
Vue 用户应该使用官方的 @vitejs/plugin-vue-jsx 插件,
它提供了 Vue 3 的特定功能,包括 HMR、全局组件解析、指令和槽。
react和vue之外的框架的使用
如果不将 JSX 与 React 或 Vue 一起使用,则可以
使用 esbuild
选项配置自定义 jsxFactory
和 jsxFragment
。以 Preact 为例:
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment',
},
})
//更多详细信息,请参阅 esbuild 文档
导入
您可以使用 jsxInject
(这是仅限 Vite 的选项)注入 JSX 帮助程序,以避免手动导入:
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxInject: `import React from 'react'`,
},
})
导入 .css
文件将通过支持 HMR 的 <style>
标记将其内容注入页面。
@import
内联和变基?@import内联
Vite 已预先配置为通过 postcss-import
支持 CSS @import
内联
Vite 别名也受到 CSS @import
的支持。
所有 CSS url()
引用,即使导入的文件位于不同的目录中,也始终会自动重新设置基准以确保正确性。
变基
Sass 和 Less 文件还支持 @import
别名和 URL 变基(请参阅 CSS 预处理器)。
postcss配置的应用范围
如果项目包含有效的 PostCSS 配置(postcss-load-config 支持的任何格式,例如 postcss.config.js
)
它将自动应用于所有导入的 CSS。
运行postcss之后
CSS minification将在 PostCSS 之后运行,并将使用 build.cssTarget
选项。
?什么是css模块文件
任何以 .module.css
结尾的 CSS 文件都被视为 CSS 模块文件。
导入css模块文件之后会返回什么
导入这样的文件将返回相应的模块对象:
栗子
/* example.module.css */
.red {
color: red;
}
import classes from './example.module.css'
document.getElementById('foo').className = classes.red
配置CSS 模块行为
可以通过 css.modules
选项进行配置。
栗子
如果 css.modules.localsConvention
设置为启用驼峰式本地命名(例如 localsConvention: 'camelCaseOnly'
),您还可以使用命名导入:
// .apply-color -> applyColor
import { applyColor } from './example.module.css'
document.getElementById('foo').className = applyColor
?怎么使用预处理器
与postcss结合
由于 Vite 仅针对现代浏览器,因此建议将原生 CSS 变量与实现 CSSWG 草案(例如 postcss-nesting)的 PostCSS 插件一起使用,并编写符合未来标准的普通 CSS。
安装相应的预处理器
.scss
、 .sass
、 .less
、 .styl
和 .stylus
文件的内置支持。不需要为它们安装 Vite 特定的插件,但必须安装相应的预处理器本身:# .scss and .sass
npm add -D sass
# .less
npm add -D less
# .styl and .stylus
npm add -D stylus
与css模块结合
在文件扩展名前添加 .module
来将 CSS 模块与预处理器结合使用,例如 style.module.scss
。
使用预处理器之后
如果使用 Vue 单文件组件,这也会自动启用 <style lang="sass">
等。
Vite 改进了 Sass 和 Less 的 @import
解析,以便 Vite 别名也受到支持。
此外,导入的 Sass/Less 文件中与根文件位于不同目录中的相对 url()
引用也会自动重新设置基准以确保正确性。
由于 API 限制,Stylus .@import
不支持别名和 URL 变基。
?如何禁用注入css
可以通过 ?inline
查询参数关闭 CSS 内容的自动注入。
在这种情况下,处理后的 CSS 字符串照常作为模块的默认导出返回,但样式不会注入到页面中。
import './foo.css' // will be injected into the page
import otherStyles from './bar.css?inline' // will not be injected
注意
自 Vite 5 起,CSS 文件中的默认导入和命名导入(例如 import style from './foo.css'
)已被删除。请改用 ?inline
查询。
?如何开启Lighting css
从Vite 4.4开始,实验性支持Lightning CSS。
将 css.transformer: 'lightningcss'
添加到配置文件
lightningcss
依赖项来选择加入npm add -D lightningcss
如何使用lighting css
如果启用,CSS 文件将由 Lightning CSS 而不是 PostCSS 处理。
使用 Lightning CSS 时不支持 CSS 预处理器。
要配置它,您可以将 Lightning CSS 选项传递给 css.lightingcss
配置选项。
要配置 CSS 模块,您将使用 css.lightningcss.cssModules
而不是 css.modules
(它配置 PostCSS 处理 CSS 模块的方式)。
css缩小器
默认情况下,Vite 使用 esbuild 来缩小 CSS。 Lightning CSS 还可以与 build.cssMinify: 'lightningcss'
一起用作 CSS 缩小器。
导入静态资源将在提供服务时返回已解析的公共 URL
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
特殊查询可以修改资产的加载方式:
// Explicitly load assets as URL//显式地将资产加载为URL
import assetAsURL from './asset.js?url'
// Load assets as strings//将资产加载为字符串
import assetAsString from './shader.glsl?raw'
// Load Web Workers//加载Web worker
import Worker from './worker.js?worker'
// Web Workers inlined as base64 strings at build time//Web worker在构建时内联为base64字符串
import InlineWorker from './worker.js?worker&inline
导入静态资源将在提供服务时返回已解析的公共 URL:
imgUrl
在开发期间将是 /img.png
,并在生产版本中变为 /assets/img.2d8efhg.png
该行为类似于 webpack 的 file-loader
。不同之处在于导入可以使用绝对公共路径(基于开发期间的项目根)或相对路径。
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
注意
CSS 中的 url()
引用的处理方式相同。
如果使用 Vue 插件,Vue SFC 模板中的资源引用会自动转换为导入。
常见图像、媒体和字体文件类型会自动检测为资产。您可以使用 assetsInclude
选项扩展内部列表。
引用的资产作为构建资产图的一部分包含在内,将获得散列文件名,并且可以由插件处理以进行优化。
小于 assetsInlineLimit
选项的字节数的资源将内联为 base64 数据 URL。
默认情况下,TypeScript 不会将静态资源导入识别为有效模块。要解决此问题,请包含 vite/client
Git LFS 占位符会自动从内联中排除,因为它们不包含它们所代表的文件的内容。要进行内联,请确保在构建之前通过 Git LFS 下载文件内容。
适用场景
未包含在内部列表或 assetsInclude
中的资源
可以使用 ?url
后缀作为 URL 显式导入。
例如
这对于导入 Houdini Paint Worklets 很有用。
import workletURL from 'extra-scalloped-border/worklet.js?url'
CSS.paintWorklet.addModule(workletURL)
可以使用 ?raw
后缀将资源作为字符串导入。
import shaderString from './shader.glsl?raw'
脚本可以作为带有 ?worker
或 ?sharedworker
后缀的 Web Worker 导入。
// Separate chunk in the production build//在生产构建中分离块
import Worker from './shader.js?worker'
const worker = new Worker()
// sharedworker
import SharedWorker from './shader.js?sharedworker'
const sharedWorker = new SharedWorker()
// Inlined as base64 strings//内联为base64字符
import InlineWorker from './shader.js?worker&inline'
JSON 文件可以直接导入 - 还支持命名导入:
// import the entire object//导入整个对象
import json from './example.json'
// import a root field as named exports - helps with tree-shaking!//导入一个名为exports的根字段——有助于树摇!
import { field } from './example.json'