PS: 文章是循序渐进的,想直接看如何使用直接跳到结尾即可
为什么使用svg?
- svg是矢量级的,无损放大缩小;基于文本格式,通常比图片文件更小
项目中如何使用svg?
在思考这个问题之前,我们需要思考在项目中如何使用png类型的图片?
原生html直接导入
无论是以png结尾还是以svg结尾的文件都是可以直接引入到html中使用的
在webpack构建的项目中直接使用
- png类型的图片
- svg类型的图片
两种类型的文件是相同的错误,需要一个合适的
加载器(loader)来处理该类型的文件。
显然,webpack是不识别这种文件类型的,那么就需要相应的加载器来处理。
在webpack构建的项目中正确使用
{
test: /\.(svg)(\?.*)?$/,
type: 'asset/resource',
generator: {
filename: 'img/[name].[hash:8][ext]'
}
},
{
test: /\.(png|jpe?g|gif|webp|avif)(\?.*)?$/,
type: 'asset',
generator: {
filename: 'img/[name].[hash:8][ext]'
}
},
使用webpack5的内置模块配置打包即可,这样就可以在项目中正常引入图片了。
优雅的使用svg(统一处理svg,封装公用入口组件)
- 借助
svg-sprite-loader
{
test: /\.svg$/,
include: [
'reslove('src/xxx')' // 需要处理成雪碧图统一处理的svg目录
],
use: [
{
loader: 'svg-sprite-loader',
options: {
symbolId: 'icon-[name]'
}
}
]
}
- 这样被loader处理过的svg会被集成一个大的svg
(雪碧图)并挂载到body上 - 每个svg会被处理成
symbol标签包裹的节点,上面会有一个id属性就是我们配置的symbolId
接下来就可以封装一个统一的使用入口来使用指定的svg了
- 要想实现上述效果,还要将svg图片
全部引入到依赖图中
function importAll(r) {
r.keys().forEach(r);
console.log(r)
}
importAll(require.context('../svg', true, /\.svg$/));
上述代码来自webpack中文文档 批量引入指定目录下某一类型的文件
封装好通用组件即统一使用入口,这里用vue举例
svg中use标签的href属性可以指定symbolId(对应symbol标签的id属性),从而加载对应的svg
<script setup lang="ts">
import { defineProps, withDefaults } from 'vue';
interface SvgProps {
width?: number,
height?: number,
name: string,
class?: string
}
withDefaults(defineProps<SvgProps>(), {
width: 25,
height: 25
})
</script>
<template>
<svg :width="width" :height="height" :class="class">
<use :href='`#icon-${name}`'></use>
</svg>
</template>
<style scoped>
.edit:hover {
fill: skyblue;
cursor: pointer;
}
</style>
注册成全局组件
import svgIcon from '@/components/svg-icon/index.vue'
const app = createApp(App)
app.component('svg-icon', svgIcon)
vue-cli项目中优雅封装svg标签
由于vue-cli项目中脚手架内部已经处理好以.svg或.png结尾的图片了(type: 'asset/resource'、 file-loader、 url-loader ...),也就是说内部已经完成了上述webpack处理png、svg图片的步骤,图片是可以直接被引入使用的,所以我们需要对准备封装成统一入口组件的一类的svg进行单独打包
- 配置vue.config.js
module.exports = {
// code...
chainWebpack () {
const svgRule = config.module.rule('svg')
// 缩小脚手架内部处理svg的loader范围,避免和svg-sprite-loader冲突,如果不需要可以直接clear
svgRule.include.add(resolve('src/assets/img')).end()
// svgRule.clear()
// const imgRule = config.module.rule('images')
// imgRule.clear()
// svgRule.exclude.add(resolve('src/icons')).end()
config.module
.rule('icons-svg')
.test(/\.svg$/)
.include.add(resolve('src/icons')) // 明确loader范围
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
}
-
批量引入svg到依赖图中(同上)
-
封装统一入口组件(同上)