返回

Vite Public 资源路径编译详解及解决方案

vue.js

Vite Public 资源路径编译困惑解析

在使用 Vite 构建项目时,开发者有时会遇到 public 资源路径编译方面的问题。 特别是,在处理诸如图片之类的静态资源时,不同的路径处理方式会导致预期之外的结果。 本文将深入分析这类问题的原因并提供多种解决方案。

资源路径问题

问题通常表现为,使用 new URL() 构造函数配合 import.meta.url 获取静态资源路径时,实际生成的路径与预期不符。一个典型案例是:使用 Array.map 处理资源数组时,生成的路径中 img 目录变成 undefined 或者其他的目录结构, 这往往与 Vite 的资源处理逻辑以及 base 配置相关。

具体来说,以下两种方式在多数情况下应该产生一致的路径结果,但实践中经常不尽如人意:

// 错误的方式 (结果路径异常)
const imageList = [
  "Wildcard.png",
  "Diamonds.png",
  "Hearts.png",
  "Spades.png",
  "Clubs.png"
].map((v) => new URL(`/img/${v}`, import.meta.url).href);

// 正确的方式 (预期路径正确)
const imageList = [
  new URL("/img/Wildcard.png", import.meta.url).href,
  new URL("/img/Diamonds.png", import.meta.url).href,
  new URL("/img/Hearts.png", import.meta.url).href,
  new URL("/img/Spades.png", import.meta.url).href,
  new URL("/img/Clubs.png", import.meta.url).href,
];

出现上述现象的关键原因在于:在 JavaScript 的 map 回调函数中, /img/ 是一个绝对路径。而import.meta.url 总是指向包含当前模块的 URL,通常是编译后的 js 文件地址。new URL() 会把相对地址解析为基于 import.meta.url 的地址,并结合base地址拼接出一个绝对地址。

由于使用 Array.map 循环拼接时,先进行了 /img/ 字符与变量的拼接。当拼接的/img/${v}的组合是一个字符串的时候, vite构建时会将img认为是路径一部分而非目录,所以拼接结果可能会异常。因此,直接使用明确的 /img/filename 的形式,会让 Vite 清晰地知晓 /img/ 目录的存在。

解决方案

为了解决这个问题,并避免未来出现相似的状况,可以尝试以下几种策略:

方案一:直接使用完整路径

Array.map 的回调函数中,直接使用包含资源目录的完整路径。 避免将路径分散拼接,这是一种非常有效的做法。

const imageList = [
  "Wildcard.png",
  "Diamonds.png",
  "Hearts.png",
  "Spades.png",
  "Clubs.png"
].map((v) => new URL(`/img/${v}`, import.meta.url).href);

// 修改后: 确保了 '/img/filename.png' 形式
const imageListCorrected = [
  "Wildcard.png",
  "Diamonds.png",
  "Hearts.png",
  "Spades.png",
  "Clubs.png"
].map((v) => new URL(`/img/${v}`, import.meta.url).href);

通过这种方式,每次传递给 new URL() 的第一个参数都是完整的资源路径字符串。这样保证了路径的正确性,不会因为拼接过程的错误产生误解。

方案二:利用 Vite 的 public 目录

将静态资源放入 Vite 的 public 目录。Vite 会原封不动地复制 public 目录中的所有文件到输出目录中,无需特别处理。 并且这些资源可以使用相对于根目录的路径进行访问,也就是可以直接使用 /img/xxx.png 访问。

  1. 将静态资源 (例如 images) 移动到项目根目录下的 public/img 目录。

    my-project/
    ├── public/
    │   └── img/
    │       ├── Wildcard.png
    │       ├── Diamonds.png
    │       └── ...
    └── src/
        └── ...
    
  2. 在代码中使用以根路径开头的静态资源。

    const imageList = [
       "/img/Wildcard.png",
       "/img/Diamonds.png",
       "/img/Hearts.png",
       "/img/Spades.png",
       "/img/Clubs.png"
      ];
    

    注意:该路径是针对根目录的路径,所以直接在 public 文件夹里面就可以找到资源文件,无需引入 import.meta.url 。 这是一个相对简单和有效的解决方案,推荐将公共静态资源放入此文件夹。

方案三:配置 base 选项

检查 vite.config.js 中的 base 配置项。 若要部署到 GitHub Pages 这类的子路径中,base 应该设置为对应的仓库名称或者子路径。 如果base 配置不正确,会导致路径解析错误。

// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  base: '/my-repo/', // 设置为你的仓库名或子路径
});

注意:base 的配置需要与实际部署路径匹配,这个设置也会影响构建时生成的文件路径。
如果需要根据不同环境使用不同 base 配置,可配合环境变量来实现。

安全建议

使用 new URL 获取资源路径时,必须确保传入的参数字符串是由你控制的,否则有潜在的安全风险。恶意用户有可能通过修改参数传递值,来访问到未经授权的文件。永远不要相信用户提供的任何参数。

通过细致地分析 new URL() 的行为,配合恰当的项目配置,可以有效的避免 public 静态资源路径的问题。优先使用public 目录,这样处理静态资源将更加简单明了,减少配置的复杂性。并牢记在涉及资源路径时,需仔细考虑每一项配置带来的影响。