Vite 如何用 ?url 与 ?inline 显式控制资源的导入或内联?
在 Vite 项目里导入静态资源时,默认行为是按文件大小自动决定“内联成 base64 data URL”还是“输出为带 hash 的独立文件”。当你导入一个不在内置资源类型列表里的文件(比如 Houdini Paint Worklet 脚本、自定义扩展名的文件),或者你希望某个资源无视 build.assetsInlineLimit 强制内联/强制不内联时,就需要用 ?url、?inline、?no-inline 这类后缀查询参数显式控制导入行为。本文基于 docs/guide/assets.md 及其引用的配置文档,说明这三种参数的用法和如何验证生效。
默认行为:先理解“自动内联”的边界
导入静态资源会返回服务时的 resolved public URL(引自 docs/guide/assets.md 的文档示例):
import 'vite/client'
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
imgUrl 在开发时是 /src/img.png,在生产构建中会变成类似 /assets/img.2d8efhg.png 的带 hash 文件名。是否内联由 build.assetsInlineLimit 决定:
- 类型:
number|((filePath: string, content: Buffer) => boolean | undefined),默认4096(4 KiB); - 小于该阈值的资源被内联为 base64 URL,设为
0则完全禁用内联; - 传入回调函数时,返回布尔值可针对单个文件 opt-in 或 opt-out,不返回值则走默认逻辑;
- 指定了
build.lib时build.assetsInlineLimit会被忽略,资源总是被内联,与文件大小和 Git LFS 占位符状态无关。
两个直接影响“是否被当作资源处理”的条件:
- 常见的图片、媒体、字体类型会被自动识别为资源,内置列表可用 assetsInclude 选项扩展,例如:
export default defineConfig({
assetsInclude: ['**/*.gltf'],
})
命中 assetsInclude 的文件从 JS 中导入时会返回 resolved URL 字符串。
- 被引用的资源会进入构建资源图,获得 hash 文件名,并可以被插件处理。例外是 Git LFS 占位符:因为不包含真实文件内容,会自动被排除在内联之外;想让它们被内联,需要在构建前通过 Git LFS 下载真实内容。
用 ?url 显式导入为 URL
不在内置列表、也没配 assetsInclude 的文件,可以用 ?url 后缀显式作为 URL 导入。文档给出的典型场景是导入 Houdini Paint Worklet(引自 docs/guide/assets.md):
import 'vite/client'
import workletURL from 'extra-scalloped-border/worklet.js?url'
CSS.paintWorklet.addModule(workletURL)
docs/guide/features.md 中也展示了同样的用法,并标注 ?url 导入的资源“automatically inlined depending on the file size”(仍按文件大小自动内联),即 ?url 本身不改变内联策略:
import 'vite/client'
// Explicitly load assets as URL (automatically inlined depending on the file size)
import assetAsURL from './asset.js?url'
另一个文档给出的组合场景是 WebAssembly:用 ?url 拿到 wasm 文件的 URL 后自行 fetch 并 instantiateStreaming:
import 'vite/client'
import wasmUrl from 'foo.wasm?url'
const main = async () => {
const responsePromise = fetch(wasmUrl)
const { module, instance } =
await WebAssembly.instantiateStreaming(responsePromise)
/* ... */
}
用 ?inline 与 ?no-inline 显式控制是否内联
?inline 和 ?no-inline 分别强制内联和强制不内联,这是文档为“绕过 4 KiB 阈值”提供的显式开关(引自 docs/guide/assets.md):
import 'vite/client'
import imgUrl1 from './img.svg?no-inline' // 即使小于 assetsInlineLimit 也输出为独立文件
import imgUrl2 from './img.png?inline' // 即使大于 assetsInlineLimit 也内联为 data URL
packages/vite/client.d.ts 中的类型声明还定义了组合形式 *?url&inline 与 *?url&no-inline,默认导出类型同样是 string,即“URL 导入 + 显式内联策略”可以叠加:
declare module '*?url&inline' {
const src: string
export default src
}
declare module '*?url&no-inline' {
const src: string
export default src
}
注意 TypeScript 默认不识别静态资源导入,以上写法都需要把 vite/client 纳入类型。docs/guide/features.md 给出两种方式:在 tsconfig.json 的 compilerOptions.types 中加入 "vite/client",或者在声明文件中写 /// <reference types="vite/client" />。
?inline 在 CSS 导入上是另一个含义
同一个 ?inline 后缀作用于 CSS 文件时,含义变为“关闭自动注入”:处理后的 CSS 字符串照常作为模块默认导出返回,但不会注入到页面(引自 docs/guide/features.md):
import 'vite/client'
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 查询替代。也就是说,静态资源上的 ?inline 与 CSS 上的 ?inline 是两套语义,按导入对象区分即可。
如何验证参数生效
文档本身给出的可核对判据(均来自文档正文与仓库内测试 playground/assets/tests/assets.spec.ts):
?url导入:开发时得到基于项目根的路径(如/foo/bar/foo.js);生产构建时,若文件小于内联阈值,会得到data:text/javascript;base64,...形式的 data URL,否则得到带 hash 的assets/路径。?inline导入:返回值以data:前缀开头,例如 PNG 会得到data:image/png;base64,...开头、未知扩展名会得到data:application/octet-stream;开头的字符串——这是文档测试断言的匹配模式,可直接作为自己页面的验证依据。?no-inline导入:生产构建时返回值是带 hash 的独立资源路径(形如/assets/fragment-xxxxxxxx.svg),不会变成 data URL。
实操上最短的验证路径是:在项目里加一行 console.log(yourImport),分别跑 vite(开发,见 docs/guide/cli.md)与 vite build,对照上面三组形态判断参数是否按预期生效。若返回值既不是 data: 前缀也不是可访问路径,先确认文件是否落在 root 内、扩展名是否命中内置列表或 assetsInclude。
限制与注意事项
?url不控制内联:它只解决“非资源类型无法被识别”的问题,内联与否仍由文件大小阈值(或显式的?inline/?no-inline)决定。- 库模式下内联策略失效:指定
build.lib后build.assetsInlineLimit被忽略,资源总是内联(见 docs/config/build-options.md 的 Note)。 - Git LFS 占位符永远不被内联,除非先通过 Git LFS 拉取真实内容。
build.assetsInlineLimit回调可精控:如果你需要按文件路径而非逐个导入语句写?inline,在配置里传回调函数返回布尔值即可,两种方式可混用。
参考文档
- docs/guide/assets.md:Explicit URL Imports、Explicit Inline Handling 小节
- docs/config/build-options.md:
build.assetsInlineLimit - docs/config/shared-options.md:
assetsInclude - docs/guide/features.md:Static Assets、Client Types、CSS 注入关闭
- packages/vite/client.d.ts:
*?url、*?inline、*?no-inline等类型声明
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00