Vite 引用本地静态资源的正确姿势🤺

🍇 前言

在前端开发中,除了通过 API 动态请求的数据外,还有一些诸如 HTML 文件、图片、字体等文件需要在项目中被用到,通常这些被视为静态资源。对于静态资源的管理和引用方式有时也会产生让人困惑的问题,就比如在 Vite 构建的项目中引用图片资源时,如果引用方式不正确就可能会出现意外的 "图裂" 情况。

本文将基于 Vite + React 来讲述并解锁 Vite 引用本地静态资源的正确姿势,主要包括 Vite 对本地资源的静态引用和动态引用两种方式,并结合一个案例来演示动态引用资源的实现过程。

🍈 静态资源目录结构

静态资源的存放位置基本上是约定俗成的,public目录和src/assets

  • public:目录中一般会放着index.htmlfavicon.ico
  • src/assets:而其他业务编码中会使用的静态资源,如图片、字体等会统一放在srcassets目录下。

比如这是我所开发的一个项目中所用到的部分图片资源:

🍉 引用图片资源的常规方式

在基础前端项目开发中,通常会用到三种常规的加载图片的方式,<img>元素、background样式、JS 脚本动态指定src属性。

  • <img>:在 HTML中通过<img>元素指定src引用图片资源。
html 复制代码
<img src="./assets/logo.png" alt="" />
  • background:通过background: url()background-image: url()引用本地资源。
css 复制代码
#logo {
  background: url('./assets/logo.png');
}
css 复制代码
#logo {
  background-image: url('./assets/logo.png');
}
  • JS 脚本动态指定图片src属性。
javascript 复制代码
document.getElementById('logo').src = './assets/logo.png'

🍊 配置路径别名 @assets

为了更灵活地引用项目中的静态资源,往往会给特定的路径配置别名以方便引用,比如常见的src对应@src/assets对应@assets

如果项目中使用 Vite 构建,可以在vite.config.ts中作如下配置:

typescript 复制代码
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  base: './',
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.join(__dirname, './src'),
      '@assets': path.join(__dirname, './src/assets'),
    },
  },
}); 

🍋 Vite 项目中引用图片资源

在以 Vite 为底层构建的 React 项目中引用图片资源就不能再同于以往常规的方式了(这也包括以 Webpack 为底层构建的 CRA 项目)。

由于 Vite 或者 Webpack 这类打包工具对静态资源有特定的处理方式,在这里我们需要给引用资源的方式分为两类:静态引入和动态引入。

静态引用图片资源

静态引入图片资源就是使用像 Tailwind CSS、CSS 样式或者指定<img>元素的src属性这样的。

需要注意的是在<img>元素中,src属性使用@路径别名是无效的,这是因为对于 Vite 来说src中的内容就是单纯的字符串,即使是配置了路径别名也并不会当做资源文件去处理;当然如果想使用@路径别名指定src也是有方法的,这时则需要通过动态引入的方式。

  • Tailwind CSS:
jsx 复制代码
<div className='
  w-full h-full
  bg-[url("@assets/images/boat.png")]
  bg-contain bg-no-repeat bg-center'>
</div>
  • CSS:
css 复制代码
div {
  background-image: url("@assets/images/boat.png");
}
  • <img>元素指定src属性,使用常规的绝对路径:
jsx 复制代码
<img
  src="/src/assets/images/boat.png"
  alt="Preview"
  className='max-w-full max-h-full object-contain'
  />
  • <img>元素指定src属性,使用@路径别名是无效的:
jsx 复制代码
<img
  src="@assets/images/boat.png"
  alt="Preview"
  className='max-w-full max-h-full object-contain'
  />

动态引用图片资源

动态引用图片资源就是用importnew URL(url, import.meta.url)这样的方式。

import.meta.url 是一个 ESM 的原生功能,会暴露当前模块的 URL。将它与原生的 URL 构造器组合使用,在一个 JavaScript 模块中,通过相对路径我们就能得到一个被完整解析的静态资源 URL。

  • import:Vite 中引入一个静态资源会返回解析后的公共路径,生产构建后生成散列文件名:
jsx 复制代码
import boatImage from '@assets/images/boat.png';

const Main = () => {
  return (
    <img
      src={boatImage}
      alt="Preview"
      className='max-w-full max-h-full object-contain'
      />
  );
};

export default Main;
  • new URL(url, import.meta.url)
jsx 复制代码
const boatImage = new URL('@assets/images/boat.png', import.meta.url).href;

const Main = () => {
  return (
    <img
      src={boatImage}
      alt="Preview"
      className='max-w-full max-h-full object-contain'
      />
  );
};

export default Main;

🍌 Vite 动态引用本地图片案例

我们以一个菜单列表为案例演示 Vite 项目中动态引用图片资源。在业务需求中,菜单列表中会展示固定的一些图标,这些就是本地图片资源。

菜单数据列表

在菜单数据列表中,icon属性就是本地图片路径,这里用了一个自定义函数获取,后面会给到。

jsx 复制代码
const menuItems = [
  {
    id: 'profile',
    name: 'Profile',
    icon: getAssetsFile('menu/Profile.png')
  },
  {
    id: 'favorites',
    name: 'Favorites',
    icon: getAssetsFile('menu/Favorites.png')
  }
]

页面结构

页面结构中,主要分为菜单图片和菜单的描述文本。

在这里菜单图标是通过行内样式指定图片路径的,如果使用静态引用的方式那么icon就是绝对路径,如果是动态引用,那么icon的路径就是需要通过new URL()获取。

jsx 复制代码
<div className='flex w-[72px] h-full pt-[24px] pr-0 pb-[24px] pl-0 flex-col gap-[24px] items-center flex-nowrap bg-[#fff] border-solid border-r border-r-[rgba(0,0,0,0.1)] absolute top-[64px] left-0 overflow-hidden z-[36]'>
  {menuItems.map((item) => (
  <div
    key={item.id}
    className={`flex h-[49px] flex-col justify-center items-center self-stretch shrink-0 flex-nowrap relative cursor-pointer border-l-4 ${activeMenu === item.id ? 'border-l-[#ffd05a]' : 'border-l-transparent'}`}
    onClick={() => handleMenuClick(item.id)}
  >
    {/* 菜单图标 */}
    <div
      className="w-[32px] h-[32px] shrink-0 bg-cover bg-no-repeat relative overflow-hidden"
      style={{ backgroundImage: `url(${item.icon})` }}
    />
    {/* 菜单描述 */}
    <span className="h-[20px] shrink-0 basis-auto font-['Inter'] text-[14px] font-normal leading-[20px] text-[#323232] relative text-left whitespace-nowrap">
      {item.name}
    </span>
  </div>
  ))}
</div>

动态获取图片路径

前面我们提到icon的路径是由一个自定义函数getAssetsFile获取的。实际上就是通过new URL()的方式动态获取图片路径。

typescript 复制代码
// 获取assets静态资源
export const getAssetsFile = (url: string) => {
  return new URL(`../assets/images/${url}`, import.meta.url).href;
};

🍍 回顾总结

最后回顾总结一下:

  • 静态资源常见存放目录
    • public
    • src/assets
  • 引用图片资源的常规方式
    • <img>元素指定src
    • background: url()background-image: url()样式
    • JS 脚本动态指定src属性
  • Vite 静态引用图片资源
    • background: url()background-image: url()样式
    • <img>元素指定src属性,使用绝对路径或相对路径
  • Vite 动态引用图片资源
    • import导入
    • new URL(url, import.meta.url)
相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax