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)
相关推荐
工藤学编程11 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保12 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫13 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
欧阳天风20 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder24 分钟前
箭头函数和 this 绑定
linux·前端·javascript·typescript
郑州光合科技余经理24 分钟前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活
沐墨染26 分钟前
大型数据分析组件前端实践:多维度检索与实时交互设计
前端·elementui·数据挖掘·数据分析·vue·交互
xkxnq30 分钟前
第一阶段:Vue 基础入门(第 11 天)
前端·javascript·vue.js
lifejump30 分钟前
Pikachu | Unsafe Filedownload
前端·web安全·网络安全·安全性测试
Irene199135 分钟前
CSS新属性分类总结(2020年后引入)
前端·css