前言
在 Vue3 + Vite 项目开发过程中,经常会遇到静态资源文件(如第三方 JS 库、图片、字体等)在开发环境正常,但打包后出现 404 错误的问题。本文通过一个实际案例------引入 MarchingSquares.js 库的过程,详细分析问题的原因和解决方案。
问题背景
项目中需要使用 MarchingSquares.js 这个第三方库,该库需要在 HTML 中通过 <script> 标签直接引入。在开发过程中,我们尝试了多种路径配置方式,但都遇到了不同的问题:
- 使用相对路径
./MarchingSquaresJS/MarchingSquares.js:打包后文件不存在 - 使用
./public/MarchingSquaresJS/MarchingSquares.js:打包后文件存在,但访问时 404 - 最终使用
/MarchingSquaresJS/MarchingSquares.js:开发和生产环境都正常
解决方案
尝试一:使用相对路径 ./MarchingSquaresJS/MarchingSquares.js
配置方式:
html
<script type="text/javascript" src="./MarchingSquaresJS/MarchingSquares.js"></script>
结果:
- ❌ 打包后
dist目录下不存在MarchingSquaresJS文件夹 - ❌ 文件无法被访问
原因分析:
- Vite 只会处理在代码中通过
import导入的资源,对于 HTML 中直接引用的静态资源,Vite 不会自动处理 - 如果文件不在
public目录下,Vite 在打包时不会将其复制到dist目录 - 相对路径
./在 HTML 中解析时,会基于当前 HTML 文件的位置,但打包后的文件结构可能不同
尝试二:移动到 public 目录并使用 ./public/MarchingSquaresJS/MarchingSquares.js
配置方式:
html
<script type="text/javascript" src="./public/MarchingSquaresJS/MarchingSquares.js"></script>
结果:
- ✅ 打包后
dist目录下出现了MarchingSquaresJS/MarchingSquares.js文件 - ❌ 访问打包后的页面时,浏览器尝试访问
./public/MarchingSquaresJS/MarchingSquares.js,出现 404 错误
原因分析:
- Vite 会将
public目录下的文件原样复制到dist目录的根目录,但不会保留public这个目录名 - 例如:
public/MarchingSquaresJS/MarchingSquares.js→dist/MarchingSquaresJS/MarchingSquares.js - 但 HTML 中写的是
./public/MarchingSquaresJS/MarchingSquares.js,浏览器会尝试访问dist/public/MarchingSquaresJS/MarchingSquares.js,这个路径不存在
最终方案一:使用绝对路径 /MarchingSquaresJS/MarchingSquares.js
配置方式:
html
<script type="text/javascript" src="/MarchingSquaresJS/MarchingSquares.js"></script>
结果:
- ✅ 开发环境(
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 模板变量(推荐)
配置方式:
html
<script type="text/javascript" src="<%- BASE_URL %>MarchingSquaresJS/MarchingSquares.js"></script>
结果:
- ✅ 开发环境(
npm run dev)正常访问 - ✅ 生产环境(打包后)正常访问
- ✅ 支持部署到任意路径(根目录或子目录)
原因分析:
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/favicon.ico→http://localhost:3000/favicon.ico - 例如:
public/MarchingSquaresJS/MarchingSquares.js→http://localhost:3000/MarchingSquaresJS/MarchingSquares.js
-
生产环境(build):
public目录下的所有文件会被原样复制 到dist目录的根目录- 不会保留
public目录名 - 例如:
public/MarchingSquaresJS/MarchingSquares.js→dist/MarchingSquaresJS/MarchingSquares.js - 例如:
public/favicon.ico→dist/favicon.ico
路径解析规则
在 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✅
- Vite dev server 将
-
生产环境:
- 打包后文件在
dist/MarchingSquaresJS/MarchingSquares.js - 如果部署在网站根目录,
/MarchingSquaresJS/MarchingSquares.js直接对应dist/MarchingSquaresJS/MarchingSquares.js✅ - 如果部署在子目录(如
/app/),需要配置base选项(见下文)
- 打包后文件在
Vite 配置
base 配置的作用
在 vite.config.ts 中,base 选项用于设置应用的公共基础路径:
typescript
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
- 如果 HTML 中使用绝对路径
总结
关键要点
-
静态资源必须放在
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 和打包后的静态服务器都会正确处理
- 使用
推荐配置
方案一:使用绝对路径(适合固定部署在根目录)
html
<!-- index.html -->
<script type="text/javascript" src="/MarchingSquaresJS/MarchingSquares.js"></script>
typescript
// vite.config.ts
export default defineConfig({
base: '/', // 固定部署在根目录
// ... 其他配置
})
方案二:使用 BASE_URL(推荐,支持灵活部署)
html
<!-- index.html -->
<script type="text/javascript" src="<%- BASE_URL %>MarchingSquaresJS/MarchingSquares.js"></script>
typescript
// 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 |
自动适配部署路径 | 需要了解模板语法 | 需要支持多环境部署 |
参考文档: