我制作了一款svg精灵图的vite插件,它通过SVGO库对其代码进行优化,将多个svg合并在一个单独的svg精灵中
预下载npm i svgo
//用来优化svgnpm i svgstore
//用来制作svg sprites
1、导入path、fs、store和optimize模块。
import path from 'path'
import fs from 'fs'
import store from 'svgstore'
import { optimize } from 'svgo'
2、定义一个名为svgstore的函数,它接受一个选项对象作为参数。
export const svgstore = (options = {}) => {
}
3、在选项对象中设置输入文件夹的路径,如果没有提供,则默认为src/assets/icons。
const inputFolder = options.inputFolder || 'src/assets/icons'; //存放svg的路径
4、返回一个Rollup插件对象,该插件对象具有name、resolveId和load属性。
return {
name: 'svgstore',
resolveId(id) {
},
load(id) {
}
5、在resolveId方法中,如果请求的ID为**@svgstore**,则返回svg_bundle.js的ID。
if (id === '@svgstore') {
return 'svg_bundle.js'
}
//这也就是为了适配编译器,在main.ts中引入(import "@svgstore")
6、在load方法中,如果请求的ID为svg_bundle.js,则执行以下步骤:
- 创建一个空的store对象,用于保存SVG图标代码。
if (id === 'svg_bundle.js') {
const sprites = store(options);
}
- 获取输入文件夹中的所有文件,并为每个文件创建一个SVG ID,然后将SVG代码添加到store对象中。
const iconsDir = path.resolve(inputFolder);
for (const file of fs.readdirSync(iconsDir)) {
const filepath = path.join(iconsDir, file);
//单独的svgid
const svgid = path.parse(file).name
//code 就是svg内的代码
let code = fs.readFileSync(filepath, { encoding: 'utf-8' });
sprites.add(svgid, code)
}
- 使用optimize函数对store对象中的所有SVG代码进行优化,并生成优化后的SVG精灵代码。
const { data: code } = optimize(sprites.toString({ inline: options.inline }), {
plugins: [
'cleanupAttrs', 'removeDoctype', 'removeComments', 'removeTitle', 'removeDesc',
'removeEmptyAttrs',
{ name: "removeAttrs", params: { attrs: "(data-name|data-xxx)" } }//svg代码里有许多无用的属性
]
})
- 将生成的JavaScript代码作为字符串返回,该字符串将SVG精灵代码注入到文档中,并添加一些样式,使SVG元素不可见。
return `const div = document.createElement('div')
div.innerHTML = \`${code}\`
const svg = div.getElementsByTagName('svg')[0]
if (svg) {
svg.style.position = 'absolute'
svg.style.width = 0
svg.style.height = 0
svg.style.overflow = 'hidden'
svg.setAttribute("aria-hidden", "true")
}
// listen dom ready event
document.addEventListener('DOMContentLoaded', () => {
if (document.body.firstChild) {
document.body.insertBefore(div, document.body.firstChild)
} else {
document.body.appendChild(div)
}
})`
//将加载svg通过设置width、height为0、overflow:hidden、绝对定位来实现隐藏
全部代码
import path from 'path'
import fs from 'fs'
import store from 'svgstore'
import { optimize } from 'svgo'
export const svgstore = (options = {}) => {
const inputFolder = options.inputFolder || 'src/assets/icons'; //存放svg的路径
return {
name: 'svgstore',
resolveId(id) {
if (id === '@svgstore') {
return 'svg_bundle.js'
}
},
load(id) {
if (id === 'svg_bundle.js') {
const sprites = store(options);
const iconsDir = path.resolve(inputFolder);
for (const file of fs.readdirSync(iconsDir)) {
const filepath = path.join(iconsDir, file);
const svgid = path.parse(file).name
let code = fs.readFileSync(filepath, { encoding: 'utf-8' });
sprites.add(svgid, code)
}
const { data: code } = optimize(sprites.toString({ inline: options.inline }), {
plugins: [
'cleanupAttrs', 'removeDoctype', 'removeComments', 'removeTitle', 'removeDesc',
'removeEmptyAttrs',
{ name: "removeAttrs", params: { attrs: "(data-name|data-xxx)" } }
]
})
return `const div = document.createElement('div')
div.innerHTML = \`${code}\`
const svg = div.getElementsByTagName('svg')[0]
if (svg) {
svg.style.position = 'absolute'
svg.style.width = 0
svg.style.height = 0
svg.style.overflow = 'hidden'
svg.setAttribute("aria-hidden", "true")
}
// listen dom ready event
document.addEventListener('DOMContentLoaded', () => {
if (document.body.firstChild) {
document.body.insertBefore(div, document.body.firstChild)
} else {
document.body.appendChild(div)
}
})`
}
}
}
}
还需要的配置
1、main.ts文件中import "@svgstore"
2、vite.config.ts文件中import { svgstore } from './src/vite_plugins/svgstore'
plugins: [
vue(), svgstore()
]
3、组件中使用
<svg>
<use xlinhref="#文件名"></use>
</svg>