一、icon组件

在项目中,会用到两种图标:element-plus自带图标和自定义图标

编写自定义组件展示自定义svg图标,这是一种通用方案

二、创建svgIcon组件

1.组件接受两个prop:icon和className

按需引入defineProps来定义接受的prop

  1. import { defineProps, computed } from 'vue'
  2. // vue3 中defineProps用来接收props
  3. const props = defineProps({
  4. // icon路径
  5. icon: {
  6. type: String,
  7. required: true
  8. },
  9. // 图标类名
  10. className: {
  11. type: String,
  12. default: ''
  13. }
  14. })

2.需要三个计算属性:

(1)是否是外部图标

根据icon是否为http(s),tel,mailto开头来判断是否为外部图标,可以封装一个工具函数

  1. export function isExternal(path) {
  2. /**
  3. * param path 资源路径
  4. */
  5. return /^(https?:|mailto:|tel:)/.test(path)
  6. }

(2)外部图标样式
  1. <template>
  2. <div>
  3. <!-- 展示外部图标 -->
  4. <div
  5. v-if="isExternal"
  6. :class="className"
  7. :style="externalIconStyles"
  8. class="svg-external-icon svg-icon"
  9. ></div>
  10. <svg v-else :class="className" class="svg-icon" aria-hidden="true">
  11. <use :xlink:href="iconName"></use>
  12. </svg>
  13. <!-- 展示内部图标 -->
  14. </div>
  15. </template>

(3)内部图标名
  1. import { defineProps, computed } from 'vue'
  2. import { isExternal as isExternalValidation } from '@/utils/validate'
  3. // vue3 中defineProps用来接收props
  4. const props = defineProps({
  5. // icon路径
  6. icon: {
  7. type: String,
  8. required: true
  9. },
  10. // 图标类名
  11. className: {
  12. type: String,
  13. default: ''
  14. }
  15. })
  16. // 需要一些计算属性
  17. /**
  18. * 内部图标
  19. */
  20. /**
  21. * 外部图标样式
  22. */
  23. /**
  24. * 判断是否为外部图标
  25. */
  26. // 判断一个资源是否为内部资源是一个常用方法,可以封装为一个工具
  27. const isExternal = computed(() => isExternalValidation(props.icon))
  28. // 外部图标样式
  29. const externalIconStyles = computed(() => ({
  30. mask: `url(${props.icon}) no-repeat 50% 50%`,
  31. 'webkit-mask': `url(${props.icon}) no-repeat 50% 50%`
  32. }))
  33. // 内部图标添加一个统一前缀
  34. const iconName = computed(() => `#icon-${props.icon}`)

三、图标显示

1.外部图标显示

外部图标用div标签,给div绑定mask属性便可显示svg

  1. {
  2. mask: `url(${props.icon}) no-repeat 50% 50%`,
  3. 'webkit-mask': `url(${props.icon}) no-repeat 50% 50%`
  4. }

2.内部图标

require.context

多个内部图标需要先注册,使用webpackrequire.context方法可以创建一个上下文(返回一个require函数),可以自动导入某个文件下多个子文件不用重复调用import

  1. require.context(
  2. directory,
  3. (useSubdirectories = true),
  4. (regExp = /^\.\/.*$/),
  5. (mode = 'sync')
  6. );

require函数

返回的require函数有三个属性,还可以接收一个request参数这里主要用到了函数的keys方法(返回相对文件名路径)

(1)将路径作为request参数传入require函数进行svg导入

(2)引入svg-icon进行全局注册
  1. // 导入所有svg图标
  2. // 将SvgIcon组件进行全局注册
  3. import SvgIcon from '@/components/svgIcon/index.vue'
  4. const svgRequire = require.context('./svg', false, /\.svg$/)
  5. // svgRequire 是一个函数,接受一个request参数,该函数有三个属性
  6. /**
  7. * resolve 函数,返回request被解析后得到的模块id
  8. * keys 函数,返回文件数组,这里返回svg图标
  9. * id 返回context module的id
  10. */
  11. // 将svgRequire.keys返回的的文件名作为request参数传入svgRequire,进行图标注册
  12. svgRequire.keys().forEach((svgIcon) => svgRequire(svgIcon))
  13. export default (app) => {
  14. app.component('svg-icon', SvgIcon)

3.svg-sprite-loader

使用内部图标后,无法显示,是因为项目中不能处理svg的loader
(1)安装loader

  1. cnpm i svg-sprite-loader@6.0.9 --save--dev

(2)添加配置

  1. const path = require('path')
  2. function resolve(dir) {
  3. return path.join(__dirname, dir)
  4. }
  5. module.exports = {
  6. chainWebpack(config) {
  7. // 设置 svg-sprite-loader
  8. config.module.rule('svg').exclude.add(resolve('src/icon')).end()
  9. config.module
  10. .rule('icons')
  11. .test(/\.svg$/)
  12. .include.add(resolve('src/icon'))
  13. .end()
  14. .use('svg-sprite-loader')
  15. .loader('svg-sprite-loader')
  16. .options({
  17. symbolId: 'icon-[name]'
  18. })
  19. .end()
  20. }
  21. }