vite 构建正确内联 svg 资源
本文最后更新于:2022年5月30日 晚上
场景
目前 vite 构建时内联媒体资源的功能有 bug,从 2020 年底就已经存在一个 github issue,感觉短期不太可能解决,于是吾辈决定自行使用插件恢复这个功能。
下面是一个使用 vite 构建的 dist,可以看到其中的 svg 并未被正确内联到代码中,而是单独分割为了一个 bundle,而 jpg 图片则被正确内联。
现有插件
为什么不使用插件 @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 文件没有被单独分割为一个文件了。
局限性
目前,没有找到一种钩子去处理 vue style/css
文件中使用 background-image: url("")
的图片资源,可能是只有 vite 内部才可以访问的钩子吧
一种思路是使用 transformHook,但吾辈暂时不想处理 sourcemap 之类的事情,所以暂未尝试。
vite 构建正确内联 svg 资源
https://blog.rxliuli.com/p/9eb3b1a6e46a4777b3661ce0bb890d69/