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 字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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

1
2
3
4
5
6
7
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日
许可协议