前言
在 Vue3 + Vite 项目里折腾静态资源,很多人都会踩一个坑:开发环境跑得好好的,第三方 JS 库、图片、字体,一打包就给你来个 404。这篇文章就拿一个真实案例——引入 MarchingSquares.js 库——来拆解问题到底出在哪,以及怎么解决。
问题背景
项目里要用到 MarchingSquares.js 这个库,它只能通过 标签直接在 HTML 里引入。试了几种路径写法,结果都不太理想:
- 用相对路径
./MarchingSquaresJS/MarchingSquares.js—— 打包完文件直接没了 - 改成
./public/MarchingSquaresJS/MarchingSquares.js—— 文件倒是在,但访问时照样 404 - 最后换成
/MarchingSquaresJS/MarchingSquares.js—— 开发和生产环境都 ok 了
解决方案
尝试一:使用相对路径 ./MarchingSquaresJS/MarchingSquares.js
配置方式:
结果:
- ❌ 打包后
dist目录下根本没有MarchingSquaresJS文件夹 - ❌ 文件自然访问不到
原因分析:
- Vite 只处理代码中用
import导入的资源,HTML 里直接引用的静态资源它不管 - 文件如果不在
public目录下,打包时不会被复制到dist - 相对路径
./基于当前 HTML 文件的位置来解析,但打包后的文件结构变了,路径就乱了
尝试二:移动到 public 目录并使用 ./public/MarchingSquaresJS/MarchingSquares.js
配置方式:
结果:
- ✅ 打包后
dist目录下确实有了MarchingSquaresJS/MarchingSquares.js文件 - ❌ 但浏览器访问的是
./public/MarchingSquaresJS/MarchingSquares.js,路径里多了一层public,结果 404
原因分析:
- Vite 会把
public目录下的文件原样复制到dist根目录,但不会保留public这个目录名 - 也就是说
public/MarchingSquaresJS/MarchingSquares.js→dist/MarchingSquaresJS/MarchingSquares.js - 而 HTML 里写的是
./public/...,浏览器跑去访问dist/public/...,自然找不到
最终方案一:使用绝对路径 /MarchingSquaresJS/MarchingSquares.js
配置方式:
结果:
- ✅ 开发环境(
npm run dev)正常 - ✅ 生产环境(打包后)正常,前提是部署在根目录
原因分析:
- 以
/开头的路径是绝对路径,相对于网站根目录 - Vite 把
public下的文件复制到dist根目录,所以public/MarchingSquaresJS/→dist/MarchingSquaresJS/ - 用
/MarchingSquaresJS/MarchingSquares.js就能正确命中dist/MarchingSquaresJS/MarchingSquares.js - 开发环境里 Vite 的 dev server 同样把
public作为静态资源根目录,所以也能访问
局限性:
- 如果应用部署在子目录(比如
/app/),硬编码的/MarchingSquaresJS/...就失效了 - 需要手动根据实际部署路径修改 HTML 里的路径
最终方案二:使用 BASE_URL 模板变量(推荐)
配置方式:
结果:
- ✅ 开发环境正常
- ✅ 生产环境正常
- ✅ 支持部署到任意路径,根目录或子目录都行
原因分析:
BASE_URL是vite-plugin-html插件提供的模板变量- 它的值自动等于 Vite 配置中的
base选项值 base: '/'时,BASE_URL = '/';base: '/app/'时,BASE_URL = '/app/'- 这样资源路径会自动跟着部署路径变化,不用手动改 HTML
优势:
- 自动适配部署路径
- 与 Vite 的
base配置保持一致 - 省去手动维护路径的麻烦,减少出错概率
原理
Vite 的 public 目录机制
Vite 对 public 目录有特殊处理:
开发环境(dev):
public下的文件被映射到网站根路径/- 例如
public/fa vicon.ico→http://localhost:3000/fa vicon.ico public/MarchingSquaresJS/MarchingSquares.js→http://localhost:3000/MarchingSquaresJS/MarchingSquares.js
生产环境(build):
public下的所有文件被原样复制到dist根目录- 不会保留
public目录名 - 例如
public/MarchingSquaresJS/MarchingSquares.js→dist/MarchingSquaresJS/MarchingSquares.js
路径解析规则
HTML 里路径解析的方式如下:
| 路径格式 | 解析方式 | 示例 |
|---|---|---|
/path/to/file.js | 绝对路径,相对于网站根目录 | http://localhost:3000/path/to/file.js |
./path/to/file.js | 相对路径,相对于当前 HTML 文件所在目录 | 如果 HTML 在 /,则解析为 /path/to/file.js |
../path/to/file.js | 相对路径,相对于当前 HTML 文件的父目录 | 如果 HTML 在 /sub/,则解析为 /path/to/file.js |
path/to/file.js | 相对路径,等同于 ./path/to/file.js | 同上 |
为什么绝对路径 / 可以工作?
开发环境:
- Vite dev server 将
public目录映射到/ /MarchingSquaresJS/MarchingSquares.js→public/MarchingSquaresJS/MarchingSquares.js✅
生产环境:
- 打包后文件在
dist/MarchingSquaresJS/MarchingSquares.js - 如果部署在网站根目录,
/MarchingSquaresJS/MarchingSquares.js直接命中 ✅ - 如果部署在子目录(如
/app/),需要配合base配置(见下文)
Vite 配置
base 配置的作用
在 vite.config.ts 中,base 选项用于设置应用的公共基础路径:
export default defineConfig({
base: '/', // 默认值,应用部署在根目录
// 或者
base: '/app/', // 应用部署在子目录
})
作用:
影响打包后的资源路径:
- 当
base: '/'时,所有资源路径都是绝对路径(如/assets/index.js) - 当
base: '/app/'时,所有资源路径会加上前缀(如/app/assets/index.js)
影响 HTML 中的路径解析:
- 如果 HTML 中用了绝对路径
/path/to/file.js,且base: '/app/',实际访问路径会是/app/path/to/file.js,但注意这取决于你如何组织资源,通常public下的静态资源不会受base影响——不过BASE_URL模板变量能帮你自动处理这种场景。
总结
关键要点
静态资源必须放在 public 目录:
- 只有
public下的文件会被 Vite 复制到dist public目录名不会出现在打包后的路径中
使用绝对路径 / 或 BASE_URL 而不是相对路径:
- 绝对路径
/path/to/file.js相对于网站根目录,稳定可靠 - 相对路径
./path/to/file.js在不同环境下解析结果可能不一致 - 推荐使用
BASE_URL,自动适配部署路径,一劳永逸
base 配置的作用:
- 设置应用的公共基础路径,影响打包后资源的路径前缀
- 对于 HTML 中硬编码的绝对路径,
base的影响有限 - 但
BASE_URL模板变量的值会自动等于base配置,完美联动
BASE_URL 的优势:
- 自动适配部署路径(根目录或子目录)
- 与 Vite 的
base配置保持一致 - 支持通过环境变量配置,无需修改代码
开发和生产环境的一致性:
- 使用
/开头的绝对路径或BASE_URL,可以保证开发和生产环境行为一致 - Vite 的 dev server 和打包后的静态服务器都会正确处理
推荐配置
方案一:使用绝对路径(适合固定部署在根目录)
// vite.config.ts
export default defineConfig({
base: '/', // 固定部署在根目录
// ... 其他配置
})
方案二:使用 BASE_URL(推荐,支持灵活部署)
// vite.config.ts
export default defineConfig({
base: isProd ? APP_BASE_PATH : '/', // 根据实际部署路径调整
plugins: [
// ... 其他插件
createHtmlPlugin({
minify: isProd,
inject: {
data: {
title: APP_TITLE,
// BASE_URL 会自动等于 base 的值,无需手动设置
},
},
}),
],
// ... 其他配置
})
对比:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
绝对路径 / | 简单直接 | 不支持子目录部署 | 固定部署在根目录 |
BASE_URL | 自动适配部署路径 | 需要了解模板语法 | 需要支持多环境部署 |