svg和png转base64插件

# 图例

An image

# 插件功能

  • 生产环境下,将小于 8KB 的 svg 和 png 图标转成 base64 编码,减少 http 请求,加快图标的展示速度

# 实现思路

  • 判断 process.env.NODE_ENV !== 'production',非生产环境不执行这个插件的功能,本地开发的时候继续使用源文件引用的方式展示图标

    • 避免不必要的文件处理,不需要频繁修改和保存 scc 和 scss 文件,从而提示开发效率

    • 便于调试,base64 无法找到文件对于的位置,没法调试

  • 打包过程中,process.env.NODE_ENV === 'production',这个时候执行插件的代码逻辑

    • 写一个正则 urlRegex ,匹配 scss 和 css 文件里面使用 url() 引入的 png 和 svg 文件

    • 然后将匹配到的这些图标的完整路径获取到,读取图标文件的大小和内容

    • 超过 8 KB 的图标不进行处理,仍然使用源文件的 url 模式导入

    • 不超过 8 KB 的图标将其内容转成 base64 格式编码,然后替换 scss 或 css 文件内的 url(xxx.svg|png) 部分

    • 需要注意的是这个步骤需要在 load 钩子函数中完成,因为 transform 钩子函数里的 css 和 scss 文件已经被预处理过了,资源路径已经被预处理并替换为 __VITE_PUBLIC_ASSET__ 这样的占位符,导致无法在 transform 钩子中匹配到原始的 svg 或 png 路径

# 实现代码

点击 展开/收起 jsx 代码
import fs from 'fs';
import path from 'path';

export default function svgToBase64Plugin(maxSize = 8 * 1024) {
  return {
    name: 'vite-plugin-svg-base64',

    load(id) {
      if (process.env.NODE_ENV !== 'production') {
        return null;
      }

      // 处理 CSS 和 SCSS 文件
      if (id.endsWith('.css') || id.endsWith('.scss')) {
        const src = fs.readFileSync(id, 'utf-8');
        const urlRegex = /url\(['"]?([^'")]+\.(svg|png))['"]?\)/g;
        let match;
        let newSrc = src;

        while ((match = urlRegex.exec(src)) !== null) {
          const filePath = match[1];
          const fileExt = match[2];

          if (filePath.startsWith('/svgs/') || filePath.startsWith('/')) {
            const fullPath = path.resolve('public', filePath.slice(1));

            if (fs.existsSync(fullPath)) {
              const stats = fs.statSync(fullPath);
              const fileSizeInBytes = stats.size;

              if (fileSizeInBytes <= maxSize) {
                const fileContent = fs.readFileSync(fullPath);
                const base64 = Buffer.from(fileContent).toString('base64');
                const mimeType =
                  fileExt === 'svg' ? 'image/svg+xml' : 'image/png';
                const dataUrl = `data:${mimeType};base64,${base64}`;
                newSrc = newSrc.replace(filePath, dataUrl);
              }
            }
          }
        }
        return newSrc;
      }

      return null;
    },
  };
}