静态资源处理
原创2026/9/9...大约 6 分钟
静态资源处理 🖼️
Vite 对静态资源(图片、字体、视频、音频等)有非常完善的支持。本章我们系统学习 Vite 的静态资源处理方式。
资源处理方式 💎
Vite 中处理静态资源主要有以下几种方式:
| 方式 | 适用场景 | 特点 |
|---|---|---|
import 导入 | JS / TS 中使用 | 获得 URL 或原始数据 |
public/ 目录 | 不需要处理的静态文件 | 原样拷贝到产物目录 |
CSS 中的 url() | CSS 文件中引用 | 自动处理 hash |
<img src> 字符串路径 | 模板中引用 | 需要放 public 目录 |
一、import 导入资源 💎
通过 ES Module 的 import 语法导入资源,是 Vite 中最推荐的方式。
导入图片 👻
// 导入图片,返回的是图片的 URL
import logoUrl from './assets/logo.png'
// 在 JS 中使用
const img = document.createElement('img')
img.src = logoUrl<!-- Vue 中使用 -->
<template>
<img :src="logoUrl" alt="logo" />
</template>
<script setup lang="ts">
import logoUrl from '@/assets/logo.png'
</script>多种资源格式 👻
Vite 内置支持以下资源的 import:
// 图片
import jpgUrl from './assets/photo.jpg'
import pngUrl from './assets/icon.png'
import gifUrl from './assets/loading.gif'
import svgUrl from './assets/icon.svg'
import webpUrl from './assets/banner.webp'
import avifUrl from './assets/banner.avif'
// 字体
import fontUrl from './assets/fonts/custom-font.woff2'
// 视频
import videoUrl from './assets/video.mp4'
// 音频
import audioUrl from './assets/sound.mp3'
// 文本
import textContent from './assets/data.txt?raw'
// JSON
import jsonData from './assets/data.json'
// 任意文件
import workerUrl from './worker.ts?worker'二、获取资源原始数据 💎
通过特殊的 ? 后缀(导入断言),可以获取资源的不同形式:
获取 URL(默认) 👻
import url from './asset.png'获取原始字符串 👻
import str from './shader.glsl?raw'
console.log(str) // 文件原始内容获取 Base64 👻
import dataUrl from './small-image.png?inline'
// 返回 data:image/png;base64,iVBORw0KGgo...通过
?inline引入的资源会被内联为 base64,适合小图标、小图片(< 4KB),可以减少 HTTP 请求。
显式声明资源为 URL 👻
import url from './asset.png?url'显式声明资源为字符串 👻
import str from './asset.glsl?raw'显式声明资源为 Worker 👻
import Worker from './worker.ts?worker'
const worker = new Worker()三、动态导入 💎
对于运行时才确定的资源路径,可以使用动态导入配合 import.meta.glob:
静态批量导入 👻
// 把指定目录下所有 .png 一次性导入
const images = import.meta.glob('./assets/images/*.png')
// images 是对象:{ './assets/images/a.png': () => Promise<Module>, ... }
// 使用
const loadImage = async (name: string) => {
const loader = images[`./assets/images/${name}.png`]
if (loader) {
const module = await loader()
return module.default // URL
}
}静态批量导入(eager) 👻
// 直接同步导入所有匹配的文件
const modules = import.meta.glob('./modules/*.ts', { eager: true })动态批量导入 👻
const modules = import.meta.glob('./modules/*.ts')
// 每个模块返回一个 Promise高级选项 👻
// 包含 query 参数
const modules = import.meta.glob('./*.ts', { query: '?raw', import: 'default' })
// 递归匹配
const modules = import.meta.glob('./**/*.ts', { eager: true })四、SVG 组件 💎
通过 vite-plugin-svg-icons 或 @vitejs/plugin-vue 的支持,SVG 可以作为组件使用。
方案一:使用 vite-plugin-svg-icons 👻
pnpm add vite-plugin-svg-icons -D// vite.config.ts
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'node:path'
export default defineConfig({
plugins: [
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
symbolId: 'icon-[dir]-[name]',
}),
],
})// main.ts
import 'virtual:svg-icons-register'<!-- 使用 SVG 图标 -->
<template>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-user" />
</svg>
</template>
<style scoped>
.icon {
width: 24px;
height: 24px;
fill: currentColor;
}
</style>方案二:使用 vite-svg-loader 👻
pnpm add vite-svg-loader -D// vite.config.ts
import svgLoader from 'vite-svg-loader'
export default defineConfig({
plugins: [svgLoader()],
})<!-- 直接作为组件使用 -->
<template>
<UserIcon class="icon" />
</template>
<script setup lang="ts">
import UserIcon from '@/assets/icons/user.svg'
</script>五、public 目录 💎
public/ 目录下的文件不会被 Vite 处理,会原样拷贝到产物根目录。
public/
├── favicon.ico
├── robots.txt
├── images/
│ └── banner.jpg
└── assets/
└── static.pdf引用方式:
<!-- 绝对路径引用 -->
<img src="/images/banner.jpg" />
<!-- 注意:不能使用 import 导入 public 中的资源 -->// ❌ 错误:public 下的资源不能 import
import favicon from '/favicon.ico'
// ✅ 正确:直接用字符串路径
document.querySelector('link[rel="icon"]')!.href = '/favicon.ico'public vs import 对比 👻
| 特性 | public/ 目录 | import 导入 |
|---|---|---|
| 处理 | 不处理 | Vite 处理(加 hash) |
| 引用 | 绝对路径 | ES Module 导入 |
| 缓存 | 无 hash | 自动带 hash(永久缓存) |
| 适用 | favicon、robots.txt | 业务资源、组件图标 |
何时用 public
favicon.ico、robots.txt、网站地图等需要放在根路径的文件- 大文件(视频、字体),不想被打包工具处理
- 需要固定 URL 的资源
六、CSS 中的资源 💎
相对路径 👻
Vite 自动处理 CSS 文件中的 url():
/* 自动解析、加 hash */
.logo {
background-image: url('../assets/logo.png');
}
@font-face {
font-family: 'CustomFont';
src: url('../assets/fonts/custom.woff2') format('woff2');
}绝对路径 👻
/* 指向 public 目录 */
.logo {
background-image: url('/images/logo.png');
}七、字体处理 💎
// 导入字体文件
import myFont from './assets/fonts/my-font.woff2'@font-face {
font-family: 'MyFont';
src: url('./assets/fonts/my-font.woff2') format('woff2');
font-display: swap;
}
body {
font-family: 'MyFont', sans-serif;
}八、图片优化 💎
Vite 5+ 内置了图片优化能力(实验性):
// vite.config.ts
export default defineConfig({
build: {
// ...
},
experimental: {
// 实验性:构建时自动优化图片
renderBuiltUrl: (filename) => {
return `https://cdn.yourcompany.com/${filename}`
},
},
})使用 vite-plugin-imagemin 👻
pnpm add -D vite-plugin-imagemin// vite.config.ts
import viteImagemin from 'vite-plugin-imagemin'
export default defineConfig({
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 7 },
mozjpeg: { quality: 75 },
pngquant: { quality: [0.65, 0.8] },
svgo: { plugins: [{ removeViewBox: false }] },
webp: { quality: 75 },
}),
],
})九、CDN 加载 💎
对于大型库(如 monaco-editor、echarts),可以考虑通过 CDN 加载:
方式一:index.html 中直接引入 👻
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>// 在 TS 中通过 window 访问
declare global {
interface Window {
echarts: any
}
}
const chart = window.echarts.init(document.getElementById('chart'))方式二:使用 vite-plugin-cdn-import 👻
pnpm add -D vite-plugin-cdn-import// vite.config.ts
import { cdnImport } from 'vite-plugin-cdn-import'
export default defineConfig({
plugins: [
cdnImport({
modules: [
{
name: 'echarts',
var: 'echarts',
path: 'https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js',
},
{
name: 'vue',
var: 'Vue',
path: 'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js',
},
],
}),
],
})实战案例 💎
图片懒加载 👻
Vite 内置了 imgLazyload 提示,结合原生 loading="lazy":
<template>
<img
v-for="img in images"
:key="img"
:src="img"
loading="lazy"
alt=""
/>
</template>
<script setup lang="ts">
import image1 from '@/assets/1.png'
import image2 from '@/assets/2.png'
// ...
const images = [image1, image2]
</script>动态背景图 👻
<template>
<div
class="banner"
:style="{ backgroundImage: `url(${bannerUrl})` }"
></div>
</template>
<script setup lang="ts">
import bannerUrl from '@/assets/banner.jpg'
</script>
<style scoped lang="scss">
.banner {
width: 100%;
height: 300px;
background-size: cover;
background-position: center;
}
</style>条件加载大文件 👻
// 仅在需要时才加载大文件
const loadBigFile = async () => {
const { default: url } = await import('@/assets/big-file.pdf')
window.open(url)
}常见问题 💎
图片 404 👻
- 检查
import路径是否正确 - 检查
publicDir配置(默认是public) - 部署后检查
base配置是否匹配
SVG 无法显示 👻
- 确认 SVG 文件没有被压缩到没有 fill 属性
- 检查 fill 颜色(默认 currentColor 时要确保父元素有 color)
- 用浏览器打开 SVG 文件本身验证
资源很大影响构建 👻
对于超大资源(> 10MB),建议:
- 放在
public/目录 - 使用 CDN
- 单独上传到 OSS / S3 等对象存储
小结
本章我们学习了 Vite 中静态资源处理的完整内容:
- import 导入:URL、原始字符串、Base64、Worker
- import.meta.glob:批量导入
- SVG 组件:作为 Vue 组件使用
- public 目录:原样拷贝
- CSS 资源:自动处理
url() - 图片优化:压缩、CDN、懒加载
- 字体处理:
@font-face+ import
下一章我们将学习 CSS 预处理器与 PostCSS。
至此,本章节的学习就到此结束了,如有疑惑,可对接技术客服进行相关咨询。