Skip to content显式导入控制:
通过
概述
在 Vite 应用中,图片、字体、JSON、样式等静态资源的处理方式与以 webpack 为代表的传统打包工具有所不同。Vite 将这些资源纳入 ES 模块体系,通过简单的 import 语句即可使用,同时在构建阶段自动完成哈希命名、内联判断和路径重写。本章说明这些能力的运作方式、配置方法以及不同场景下的选择依据。
静态资源的引入方式
图片、字体与 JSON
对于常见的图像、媒体和字体文件,Vite 将其视为资源模块。import 一张图片,模块默认导出的是构建后的 URL 字符串,而不是文件内容。这一行为与 webpack 的 file-loader 类似。
js
import logo from './logo.png'
console.log(logo)
// 开发环境:/src/logo.png
// 生产构建:/assets/logo.8f9a8b.png开发时路径直接指向源文件;生产构建阶段 Vite 会给文件名加上内容哈希,用于长久缓存。
字体文件(.woff、.woff2、.ttf 等)的处理逻辑相同——导入得到的也是最终 URL,可以在 CSS 的 @font-face 中使用,也可以通过 JavaScript 动态创建 FontFace 对象。
JSON 文件的导入结果则是一个完整的 JavaScript 对象。既可以导入整个对象,也可以解构需要的字段:
js
import pkg from './package.json' // 整个对象
import { version } from './package.json' // 仅提取 version解构的过程由打包工具(构建时使用 Rollup)完成 tree-shaking,未使用的字段不会出现在产物中。想要让 TypeScript 项目识别 .png、.svg 这类非代码文件的导入,需要在 tsconfig.json 中加入类型声明,或者在项目任意 .d.ts 文件中添加:
ts
/// <reference types="vite/client" />否则编辑器会提示“找不到模块”或“无法解析的模块说明符”。
显式导入控制:?url、?inline、?raw
默认的资源处理行为之外,Vite 在导入路径上提供了一组后缀参数,允许按文件精确控制处理方式。
?url:强制将文件作为 URL 导入。即便文件类型不在默认资源列表中也可以使用。适用于 Web Workers 的import.meta.url或 Houdini Paint Worklet 等场景。?inline:将文件内容转为 base64 data URL 并内联到 JS 中,忽略assetsInlineLimit的大小限制。?no-inline:强制不内联,即使文件很小也生成独立文件并保留哈希。?raw:以字符串形式返回文件的原始内容,不经过任何转换。适合加载着色器代码、GLSL 或需要以字符串形式传递的配置文件。
js
import workletURL from './paint.js?url'
import inlineSvg from './icon.svg?inline'
import noInline from './tiny.png?no-inline'
import shader from './shader.glsl?raw'?inline 与 ?no-inline 的处理入口在 Vite 源码的 packages/vite/src/node/plugins/asset.ts 中。SVG 内联时会先通过 svgToDataURL 生成 data:image/svg+xml 格式;其他文件则根据 MIME 类型拼接出标准的 base64 data URL。对于 Git LFS 占位符(文件内容是指针而非真实数据),Vite 会跳过内联并输出警告。
?raw 只是把文件内容以字符串形式读出,不做任何转换,适合将代码传给第三方编译器或解释器。
通过 new URL 动态引用资源
如果资源文件名需要根据运行时变量拼接,例如 assets/user-${id}.avif,import 语句无法直接处理动态路径。此时可以使用浏览器原生支持的 import.meta.url 配合 new URL:
js
function getUserAvatar(id) {
return new URL(`../assets/user-${id}.avif`, import.meta.url).href
}开发模式下这段代码不经 Vite 转换,由浏览器直接执行。构建时 Vite 会分析模板字符串中的静态前缀(例如 ../assets/user-),并将匹配到的文件替换为带哈希的最终路径。因此,模板字符串必须满足一个条件:除变量部分之外的前缀是静态且可分析的路径片段,不能跨目录层级或完全动态生成。如果写成 new URL(name, import.meta.url),Vite 无法在构建时推断文件来源,代码会被原样保留,目标环境不支持 import.meta.url 时可能导致运行时错误。
public 目录
项目根目录下默认存在一个 public 文件夹(可通过 publicDir 配置修改或关闭)。该目录中的文件不参与 Vite 的构建处理。开发期间,它们直接映射到 / 根路径;生产构建时,整个目录被原样复制到输出目录(一般为 dist/)的根。
常见用例包括 favicon.ico、robots.txt 或者不需要压缩与哈希的静态 HTML 页面。
引用 public 文件时使用绝对路径:
html
<link rel="icon" type="image/x-icon" href="/favicon.ico" />如果应用部署在非根路径(例如 base: '/my-app/'),上面的写法就会失效。正确的做法是通过 import.meta.env.BASE_URL 拼接路径:
html
<link rel="icon" type="image/x-icon" href="<%= BASE_URL %>favicon.ico" />或在 JavaScript 中:
js
const faviconUrl = new URL('favicon.ico', import.meta.env.BASE_URL).href这样就可以让路径前缀在构建和部署时自动同步。
与 import 引入的行为差异
import 引入的资源会进入 Vite 的依赖图,接受哈希命名、内联、压缩、打包分析等优化处理。public 目录中的文件则完全“透明”,不参与构建优化,文件名和内容都不会被修改。
选择依据很直接:
- 需要构建处理(哈希、内联、压缩、tree-shaking)的资源 → 通过
import引入。 - 不需要任何处理,或者必须保持固定访问路径(如 PWA manifest、
robots.txt) → 放入public目录。
样式处理
全局 CSS 与 CSS Modules
在入口文件或组件中直接 import './style.css',Vite 会将样式注入页面。这种方式引入的 CSS 作用于全局,不加限定,可能与其他样式产生冲突。
CSS 文件内部的 @import 和 url() 同样会被 Vite 接管。@import './theme.css' 会将目标文件内联合并进同一个 CSS 输出块,不会产生额外的网络请求;url('./bg.png') 中的相对路径以当前 CSS 文件所在目录为基准进行解析,资源同样会经过哈希和内联判断。
任何以 .module.css 为后缀的文件会自动启用 CSS Modules。导入后得到一个类名映射对象,原始类名会被编译为全局唯一的标识符。
css
/* Button.module.css */
.base { padding: 8px 16px; }
.primary { background: dodgerblue; color: white; }js
import styles from './Button.module.css'
button.className = `${styles.base} ${styles.primary}`
// 渲染后的类名类似:base_1a2b3c primary_4d5e6f这样每个组件的样式只在自己的作用域内生效,不会污染全局。
如果希望调整类名生成规则或作用范围,可以在 vite.config.js 中通过 css.modules 配置,例如:
js
css: {
modules: {
localsConvention: 'camelCaseOnly',
scopeBehaviour: 'local'
}
}大多数情况下默认配置就够用,.module.css 后缀约定已经能够满足组件化开发需求。
CSS 预处理器
Vite 对 Sass(.scss / .sass)、Less(.less)和 Stylus(.styl)提供内置支持,不需要额外插件。只需安装对应的预处理器包:
sh
npm install -D sass然后直接在代码中导入相应后缀的文件,Vite 会自动调用预处理器进行编译:
js
import './theme.scss'如果需要在每个样式文件头部统一注入一些变量或 mixin(例如主题色、公共函数),可以用 css.preprocessorOptions 中的 additionalData:
js
css: {
preprocessorOptions: {
scss: {
additionalData: `$primary: #42b883; @import "@/styles/mixins.scss";`
}
}
}上面这段配置会被拼接到每个 .scss 文件内容的最前面。实际效果相当于:项目中任何 .scss 文件都可以直接使用 $primary 变量以及 mixin 文件里定义的函数,不需要再手动 @import。例如 style.scss 文件内容为 body { color: $primary; },编译后将自动得到 body { color: #42b883; }。
Less 和 Stylus 同样支持 additionalData,只是注入语法和作用域细节不同,使用时需参考对应预处理器的文档。
base 配置与资源路径
base 决定了应用部署时的公共基础路径。所有由 Vite 生成的资源引用(包括 import 的静态资源、public 文件、JS/CSS 的请求路径)都会自动加上这个前缀。
js
// vite.config.js
export default defineConfig({
base: '/my-app/'
})构建后,HTML 中的 <script> 标签路径变为 /my-app/assets/index.abc123.js;import logo from './logo.png' 得到的 URL 也会自动变为 /my-app/assets/logo.8f9a8b.png。
但 public 目录下的文件不会自动添加前缀,硬编码的 /favicon.ico 在部署到 /my-app/ 后会因为路径错误而请求失败。因此 public 文件的引用始终要通过 BASE_URL 拼接,开发期间 base 默认是 /,构建时则由配置决定,保持引用方式统一很关键。
构建阶段的资源处理
生产构建时,Vite 对静态资源执行以下处理:
- 内容哈希 — 文件名内加入哈希(如
logo.png→logo.8f9a8b.png),利用浏览器强缓存。 - 内联判断 — 小于
assetsInlineLimit(默认 4096 字节)的文件会被转为 base64 data URL 写入 JS/CSS,减少 HTTP 请求数。该阈值可通过build.assetsInlineLimit调整,也可以对单个文件使用?inline或?no-inline覆盖。 - 输出目录组织 — 所有构建后的资源默认放置在
assetsDir指定的目录下(默认为assets),可以改为static等其他名称。
js
build: {
assetsInlineLimit: 0, // 全部不内联
assetsDir: 'static'
}如果想要自定义文件类型也被当作资源处理(即导入后返回 URL 字符串),可以使用 assetsInclude 添加匹配模式:
js
assetsInclude: ['**/*.gltf']这样 .gltf 文件导入后同样会得到带哈希的资源路径。
注意点
在 JavaScript 中通过变量动态设置 CSS 背景图时,若 URL 来自导入的资源,需要将整个
url()值用双引号包裹:jsconst imgUrl = import('./hero.png') element.style.background = `url("${imgUrl}")`缺少引号可能导致部分浏览器解析失败。
CSS 中
url()的相对路径基准是当前 CSS 文件所在目录,而不是最终输出的 HTML 位置。Vite 构建时会重新计算这些路径,使它们指向哈希后的文件。通过
@import引入的 CSS 文件会被合并到同一个输出块中,不会产生多个网络请求;即便在多个组件中重复@import同一文件,也只会保留一份。TypeScript 项目中务必加入
vite/client类型引用,否则静态资源导入会报类型错误。使用 Git LFS 的项目若文件内容在本地是 LFS 指针占位符,构建时 Vite 会跳过内联并给出警告——确保在构建前通过
git lfs pull拉取完整文件内容。
