webpack(包含vue-cli)项目统一处理svg——封装svg组件

245 阅读2分钟

PS: 文章是循序渐进的,想直接看如何使用直接跳到结尾即可

为什么使用svg?

  • svg是矢量级的,无损放大缩小;基于文本格式,通常比图片文件更小

项目中如何使用svg?

在思考这个问题之前,我们需要思考在项目中如何使用png类型的图片?

原生html直接导入

无论是以png结尾还是以svg结尾的文件都是可以直接引入到html中使用的

image.png

image.png

在webpack构建的项目中直接使用

  • png类型的图片

image.png

  • svg类型的图片

image.png 两种类型的文件是相同的错误,需要一个合适的加载器(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,封装公用入口组件)

  1. 借助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了 image.png
  1. 要想实现上述效果,还要将svg图片全部引入到依赖图
function importAll(r) {
	r.keys().forEach(r);
	console.log(r)
}
importAll(require.context('../svg', true, /\.svg$/));

上述代码来自webpack中文文档 批量引入指定目录下某一类型的文件

  1. 封装好通用组件即统一使用入口,这里用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-loaderurl-loader ...),也就是说内部已经完成了上述webpack处理png、svg图片的步骤,图片是可以直接被引入使用的,所以我们需要对准备封装成统一入口组件的一类的svg进行单独打包

  1. 配置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()
}
  1. 批量引入svg到依赖图中(同上)

  2. 封装统一入口组件(同上)