如何在Vite中内敛SVG

366 阅读2分钟

1、如何将图片资源变成base64?

vite中要将图片资源转成base64而不是提取成网络资源,需要配置build.assetsInlineLimit参数,表示小于该值的图片资源都会被内敛,减少网络请求数。

2、问题:vite4之前的版本包括vite4,该配置对svg无效,导致有很多细小的svg会发起网络请求,得不尝失。

3、如何解决?

  1. 对这个问题的讨论,可以追溯到2020年,有兴趣的朋友可以看看以下issues.

  2. 直到最近,终于给出了解决方案。大家可以看看以下PR

  3. 采用何种方式来编码svg?

    • mini-svg-data-uri的作者提到了为什么不使用base64编码svg,并给出了自己的方案codepen.io/tigt/post/o…

    • 这篇文章也提到了不使用base64编码svg的原因。css-tricks.com/probably-do…

    • 而vite中不采用base64编码也不采用mini-svg-data-uri。为什么?PR中给出了以下两个理由

      1. colors minification should be done ahead of time or with a plugin with other optimizations (using svgo for example)
      2. using multiple replaceAll (node 15) which is faster than encodeURI or regexes or one regex with match function

      意思是:

      1. 颜色压缩应该提前完成,或者使用其他优化的插件来完成(例如使用 svgo)。
      2. 使用多个 replaceAll 方法(Node 15 版本)比使用 encodeURI 或正则表达式,或者使用带有匹配函数的一个正则表达式更快。
  4. Vite5解决了这个问题,那么对于vite4及以前版本该如何解决?

    我编写了一个插件来转换svg,转换代码取至上述PR

    以下是转换svg的代码。

    // Inspired by https://github.com/iconify/iconify/blob/main/packages/utils/src/svg/url.ts
    function svgToDataURL(content: Buffer): string {
      const stringContent = content.toString()
      // If the SVG contains some text, any transformation is unsafe, and given that double quotes would then
      // need to be escaped, the gain to use a data URI would be ridiculous if not negative
      if (stringContent.includes('<text')) {
        return `data:image/svg+xml;base64,${content.toString('base64')}`
      } else {
        return (
          'data:image/svg+xml,' +
          stringContent
            .trim()
            .replaceAll('"', "'")
            .replaceAll('%', '%25')
            .replaceAll('#', '%23')
            .replaceAll('<', '%3c')
            .replaceAll('>', '%3e')
            // Spaces are not valid in srcset it has some use cases
            // it can make the uncompressed URI slightly higher than base64, but will compress way better
            // https://github.com/vitejs/vite/pull/14643#issuecomment-1766288673
            .replaceAll(/\s+/g, '%20')
        )
      }
    }
    

    如何使用?

    import { resolve } from "path";
    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue";
    import { inlineSvgPlugin } from "./plugins/inlineSvg";
    export default defineConfig(({ command, mode }) => {
      const isProduction = mode == "production";
      return {
        plugins: [
          vue({
            isProduction,
          }),
          inlineSvgPlugin({
            assetsInlineLimit: 10 * 1024,
          }),
        ],
        resolve: {
          alias: {
            "@/": `${resolve(__dirname, "src")}/`,
          },
        },
        
        build: {
          chunkSizeWarningLimit: 500,
          cssCodeSplit: true, //css 拆分
          sourcemap: false,
          assetsInlineLimit: 5 * 1024, //小于该值 图片将打包成Base64
          minify: "terser", //是否压缩
        },
      };
    });
    ​
    

    导入inlineSvgPlugin并传入assetsInlineLimit即可,含义与build.assetsInlineLimit一致。若没有传,则取build.assetsInlineLimit的值。

    优化结果:svg资源文件个数60个->4个

  5. 插件的代码放在了github上,有兴趣的朋友自行领取。

    github地址:github.com/fishermanxz…

    插件目前只支持import 导入的图片和img标签src引用的图片。即以下两种情况。

    <template>
        <img src="@/assets/1.svg"/>
    </template>
    

    或者

    import svg from "@/assets/1.svg"
    

    暂不支持css导入的svg。

以上就是vite中关于svg内敛的探讨,如果对您有帮助,请您高抬贵手帮忙点个赞~