vite 构建正确内联 svg 资源

本文最后更新于:2022年5月30日 晚上

场景

目前 vite 构建时内联媒体资源的功能有 bug,从 2020 年底就已经存在一个 github issue,感觉短期不太可能解决,于是吾辈决定自行使用插件恢复这个功能。

下面是一个使用 vite 构建的 dist,可以看到其中的 svg 并未被正确内联到代码中,而是单独分割为了一个 bundle,而 jpg 图片则被正确内联。

1653919149233

现有插件

为什么不使用插件 @rollup/plugin-image

  • 它没有对 vite 做特殊处理,vite 本身内置插件处理 svg 的时机要早于它,它永远不会有机会处理图片
  • 它没有处理 monorepo 项目中其他模块的资源

实现起来并不困难,核心是拦截 .svg 的 load,将之替换为一个 js 文件,其中默认导出 svg dataUri 字符串。

import { createFilter, FilterPattern } from "@rollup/pluginutils";
import { readFile } from "fs-extra";
import svgToMiniDataURI from "mini-svg-data-uri";
import * as path from "path";
import { optimize, OptimizedSvg } from "svgo";
import { Plugin } from "vite";

const defaults = {
  exclude: null,
  include: null,
};

export function svgPatch(opts?: {
  include?: FilterPattern;
  exclude?: FilterPattern;
}): Plugin {
  const options = Object.assign({}, defaults, opts);
  const filter = createFilter(options.include, options.exclude);

  return {
    name: "vite-plugin-svg-patch",
    enforce: "pre",
    resolveId(id: string, importer: string) {
      if (this.meta.watchMode) {
        return null;
      }
      if (id.endsWith(".svg")) {
        return path.resolve(path.dirname(importer), id);
      }
    },
    async load(id) {
      if (this.meta.watchMode) {
        return null;
      }
      if (!filter(id) || !id.endsWith(".svg")) {
        return null;
      }
      const source = optimize(
        (await readFile(id, "utf-8")).replace(/[\r\n]+/gm, "")
      );
      if (source.error || source.modernError) {
        console.error("svg optimization failed: ", id);
      }
      const dataUri = svgToMiniDataURI((source as OptimizedSvg).data);
      return `export default "${dataUri}";`;
    },
  };
}

使用

已发布为 npm 包 @liuli-util/vite-plugin-svg-patch

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { svgPatch } from "@liuli-util/vite-plugin-svg-patch";

export default defineConfig({
  plugins: [vue(), svgPatch()],
});

以下是使用插件之后,可以看到 svg 文件没有被单独分割为一个文件了。

1653919908486

局限性

目前,没有找到一种钩子去处理 vue style/css 文件中使用 background-image: url("") 的图片资源,可能是只有 vite 内部才可以访问的钩子吧

一种思路是使用 transformHook,但吾辈暂时不想处理 sourcemap 之类的事情,所以暂未尝试。


vite 构建正确内联 svg 资源
https://blog.rxliuli.com/p/9eb3b1a6e46a4777b3661ce0bb890d69/
作者
rxliuli
发布于
2022年5月30日
许可协议