51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Vue Icon 图标处理方案

Preface {#preface}

在 Vue 前端项目中不可避免的要使用 icon svg 图标组件,对于这个组件它需要两种能力:

  1. 显示外部 svg 图标。
  2. 显示项目内的 svg 图标。

基于以上概念,我们先来实现显示外部 svg 图标。

显示外部 svg 图标 {#显示外部-svg-图标}

  1. 首先在项目中创建 src/components-global/svg-icon/index.vue,编写如下代码:
<template>
  <div
    v-if="isExternal"
    :style="styleExternalIcon"
    class="svg-external-icon svg-icon"
    :class="className"
  />
</template>

<script setup>
import { defineProps, defineOptions, computed } from 'vue'

defineOptions({
  name: 'SvgIcon'
})

const props = defineProps({
  // icon 图标
  icon: {
    type: String,
    required: true
  },
  // 图标类名
  className: {
    type: String,
    default: ''
  },
  // 同时设置宽度和高度
  size: {
    type: String
  },
  // 宽度
  width: {
    type: String,
    default: '1em'
  },
  // 高度
  height: {
    type: String,
    default: '1em'
  }
})

/**
 * 判断是否为外部图标
 */
const isExternal = computed(() => {
  return /^(https?:|http?|mailto:|tel:)/.test(props.icon)
})
/**
 * 外部图标样式
 */
const styleExternalIcon = computed(() => ({
  /*
    在 CSS 中,mask 属性用于定义图像蒙版或剪切蒙版。
    它允许您通过指定一个图像或 SVG 元素作为另一个元素的蒙版来创建复杂的视觉效果。
    mask 属性通过根据蒙版的透明度值隐藏元素的部分来实现。
   */
  mask: `url(${props.icon}) no-repeat 50% 50%`,
  '-webkit-mask': `url(${props.icon}) no-repeat 50% 50%`,
  // 设置宽高
  width: props.size ?? props.width,
  height: props.size ?? props.height
}))
</script>

<style scoped>
.svg-icon {
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>
  1. 编写完成之后,我们在 App.vue 使用:http://res.lgdsunday.club/user.svg这个链接来测试外部 svg 图标是否引用成功。
<template>
  外部图标测试:<svg-icon icon="http://res.lgdsunday.club/user.svg" />
</template>

<script setup>
import SvgIcon from '@/components-global/svg-icon'

</script>

<style lang="scss"></style>

显示效果如下:

image-20231115120229537

显示内部 svg 图标 {#显示内部-svg-图标}

  1. 首先需要准备一些 svg 图标文件,可以从iconfont-阿里巴巴矢量图标库中下载,下载完成之后放到项目中的src/assets/icons/svg目录下。

image-20231115144325267

  1. 创建 src/assets/icons/index.js
import SvgIcon from '@/components-global/svg-icon'

/*
  https://webpack.docschina.org/guides/dependency-management/#requirecontext
  require.context 是一个在 Webpack 中用于创建模块上下文的函数。
  它的主要作用是实现动态地导入模块,特别是用于导入一组符合特定规则的模块。
  通常,它用于在前端项目中处理导入大量模块或文件的情况,例如导入所有的图片、样式文件或特定目录下的组件。

  require.context 接受三个参数:
    1. 要搜索的目录:一个字符串,表示要搜索的目录路径。
    2. 是否搜索其子目录:一个布尔值,表示是否要搜索指定目录的子目录。
    3. 匹配文件的正则表达式:一个正则表达式,用于匹配文件名。

  使用 svgContext.keys() 获取所有匹配的文件路径,然后通过 svgContext(svgIcon) 来动态导入每个 SVG 文件
 */
const svgContext = require.context('./svg', false, /\.svg$/)
svgContext.keys().forEach(svgIcon => svgContext(svgIcon))

export default Vue => {
  // 这里为什么将变量名设置为 Vue 呢?请参考:https://juejin.cn/post/7114133835339530276
  Vue.component('svg-icon', SvgIcon)
}
  1. main.js 中引入该文件。
...
// 导入 SvgIcon
import installIcons from '@/assets/icons'

...
// 安装 svg 图标和注册 SvgIcon 组件
installIcons(app)
...
  1. 使用 svg-sprite-loader 处理 svg 图标

svg-sprite-loaderwebpack 中专门用来处理 svg 图标的一个 loader

下载该 loader,执行:npm i --save-dev svg-sprite-loader

创建 vue.config.js 文件,新增如下配置:

const { defineConfig } = require('@vue/cli-service')

const path = require('path')

/**
 * __dirname 是当前执行脚本文件的目录,
 * 因此 resolve 函数会将相对路径与该目录拼接,返回一个绝对路径。
 */
const resolve = (dir) => {
  return path.join(__dirname, dir)
}

// https://cli.vuejs.org/zh/guide/webpack.html
module.exports = defineConfig({
  chainWebpack (config) {
    /*
    配置 svg-sprite-loader 来处理 SVG 图标,将多个独立的SVG图标文件打包成一个单独的SVG精灵图(SVG sprite)
      1. 设置排除规则(exclude)和包含规则(include):
        1.1 config.module.rule('svg') 定义了一个规则,用于处理所有以 .svg 结尾的文件,
            但它通过 .exclude.add(resolve('src/assets/icons')) 来排除了 src/assets/icons 目录下的 SVG 文件。
            这意味着 src/icons 目录下的 SVG 文件不会被该规则处理,而是由下面的规则单独处理。
        1.2 config.module.rule('icons') 定义了一个规则,用于处理 src/icons 目录下的 SVG 文件,通过 .include.add(resolve('src/assets/icons')) 来指定只处理该目录下的文件。
      2. 使用 svg-sprite-loader:
        2.1 通过 .use('svg-sprite-loader') 来配置使用 svg-sprite-loader 作为处理 SVG 文件的 loader。
        2.2 通过 .loader('svg-sprite-loader') 指定加载器的名称为 svg-sprite-loader。
        2.3 使用 .options({ symbolId: 'icon-[name]' }) 来配置 svg-sprite-loader 的选项,其中 symbolId 用于定义每个 SVG 符号的标识,通常在 SVG 精灵图中使用。
     */
    config.module
      .rule('svg')
      .exclude.add(resolve('src/assets/icons'))
      .end()
    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/assets/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()
  }
})
  1. 编写完成之后,我们在 App.vue 使用第一步导入的 头像 男孩.svg 文件进行测试。
<template>
  内部图标测试:<svg-icon icon="头像 男孩" size="28px" />
</template>

<style lang="scss"></style>

显示效果如下:

image-20231115144558886

至此一个可以引用外部和内部 svg 图标的 SvgIcon 组件封装完毕。

参考资料 {#参考资料}

赞(5)
未经允许不得转载:工具盒子 » Vue Icon 图标处理方案