封装SvgIcon组件

1、认识SvgIcon组件作用

首先我们认识一下svg并且知道Svg格式图片的好处,这里可以去看一下之前我写的关于svg部分的文章。

为什么要将svg其封装成一个单独的组件

SvgIcon组件在Vue中用于专门展示SVG图标,可以充分利用SVG的优点,使得图标的显示和交互都能得到更好的支持。

  1. 可缩放性:SvgIcon组件可以利用SVG的特性,使得图标可以在不失真的情况下进行缩放。这对于需要根据用户需求动态调整大小或者需要在不同分辨率下都能清晰显示的图标来说非常有用。
  2. 可编辑性:由于SVG图标是由XML代码定义的,因此可以方便地进行修改和编辑。这在需要定制图标或者图标需要经常变动的情况下非常方便。
  3. 支持复杂交互:SVG图标可以支持鼠标事件(如点击、悬停等),这使得可以在图标上实现一些复杂的交互功能。

2、(项目采用)借vite-plugin-svg-icons -D 封装

这里我的项目采用的是这个方式

我们借助插件实现SvgIcon组件的封装,具体步骤如下:

把我们的svg组件都放到这个下面

src => assets => svg
  • 1.
(1)安装插件:yarn add -D vite-plugin-svg-icons
yarn add-D vite-plugin-svg-icons
  • 1.
  • 2.

安装成功以后可以看到我们的依赖项

"vite-plugin-svg-icons": "^2.0.1",
  • 1.
(2)配置vite.config.js(vite.config.ts)配置文件,导入,并将其插件配置到plugins数组当中

这里代表我们将svg的所有组件全部封装进入src/assets/svg 这个下面

**vite.config.ts**中配置插件

注意这里的iconDirs 背后的地址就是你svg文件夹的那个地址:

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

export default defineConfig({
    base: '/',
    plugins: [
        createSvgIconsPlugin({
            iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
            symbolId: 'icon-[dir]-[name]',
        }),
        vue(),
        // svgBuilder('./src/assets/svg/'),// 这里已经将src/assets/svg/下的svg全部导入,无需再单独导入
        //视图分析工具
        visualizer({
            open: true, //在默认用户代理中打开生成的文件
            gzipSize: true, // 收集 gzip 大小并将其显示
            brotliSize: true, // 收集 brotli 大小并将其显示
            filename: "repotrs.html", // 分析图生成的文件名
        }),
    ],
    resolve: {
        // 解决公共路径问题
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    },
    
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
(3)main.ts入口文件导入 (大坑)

配置main.js(main.ts),在其中注册svg图标

import 'virtual:svg-icons-register'
  • 1.

报错:封装SvgIcon组件_封装

安装fast-glob依赖

yarn add fast-glob -D
  • 1.

☞ src下面新建立一个components文件夹,下面建立一个 SvgIcon

<template>
  <!--svg外层容器,需要配置子元素use使用-->
  <svg :style="{width, height}">
    <!--xlink:href引用的svg图标,#icon-图标名-->
    <use :xlink:href="symbolId" :fill="fill" />
  </svg>
</template>

<script setup>
import {computed,defineProps} from "vue"
const props = defineProps({
  prefix: {
    type: String,
    default: 'icon'
  },
  name: {
    type: String,
    required: true
  },
  width: {
    type: String,
    default: "16px"
  },
  height: {
    type: String,
    default: "16px"
  },
  fill: {
    type: String,
    default: '#333'
  }
})
// svg icon引入的格式
const symbolId = computed(() => {
  return `#${props.prefix}-${props.name}`
})
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
(4)页面使用

这里我们直接就已经可以使用了

<SvgIcon name="collapsetrue"/>
<SvgIcon name="collapsefalse"/>
  • 1.
  • 2.

Icon就可以正常显示了

(5)全局使用
// main.ts之中引入
import SvgIcon from '@/components/SvgIcon/index.vue'
app.component('SvgIcon', SvgIcon) //icon组件


<SvgIcon name="collapsetrue"/>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

完成!

3、 借助 svg-sprite-loader 封装

(1)安装
yarn add svg-sprite-loader -D
  • 1.
(2)SvgIcon.vue内容如下
<script setup lang="ts">
import { computed } from 'vue-demi'
const props = defineProps(['name'])
const iconName = computed(() => `#icon-${props.name}`)
</script>

<template>
  <svg class="svg-icon" aria-hidden="true">
    <use :xlink:href='iconName'></use>
  </svg>
</template>

<style lang="scss" scoped>
.svg-icon {
  vertical-align: middle;
  fill: currentColor;
  overflow: hidden;
}
</style>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
(3)src下面新建文件夹plugins

在plugins文件夹创建svgBuilder.js插件

import { readFileSync, readdirSync } from 'fs'

let idPerfix = ''
const svgTitle = /<svg([^>+].*?)>/
const clearHeightWidth = /(width|height)="([^>+].*?)"/g

const hasViewBox = /(viewBox="[^>+].*?")/g

const clearReturn = /(\r)|(\n)/g

function findSvgFile(dir) {
  const svgRes = []
  const dirents = readdirSync(dir, {
    withFileTypes: true,
  })
  for (const dirent of dirents) {
    if (dirent.isDirectory()) {
      svgRes.push(...findSvgFile(dir + dirent.name + '/'))
    } else {
      const svg = readFileSync(dir + dirent.name)
        .toString()
        .replace(clearReturn, '')
        .replace(svgTitle, ($1, $2) => {
          // console.log(++i)
          // console.log(dirent.name)
          let width = 0
          let height = 0
          let content = $2.replace(clearHeightWidth, (s1, s2, s3) => {
            if (s2 === 'width') {
              width = s3
            } else if (s2 === 'height') {
              height = s3
            }
            return ''
          })
          if (!hasViewBox.test($2)) {
            content += `viewBox="0 0 ${width} ${height}"`
          }
          return `<symbol id="${idPerfix}-${dirent.name.replace(
            '.svg',
            ''
          )}" ${content}>`
        })
        .replace('</svg>', '</symbol>')
      svgRes.push(svg)
    }
  }
  return svgRes
}

export const svgBuilder = (path, perfix = 'icon') => {
  if (path === '') return
  idPerfix = perfix
  const res = findSvgFile(path)
  // console.log(res.length)
  // const res = []
  return {
    name: 'svg-transform',
    transformIndexHtml(html) {
      return html.replace(
        '<body>',
        `
          <body>
            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
              ${res.join('')}
            </svg>
        `
      )
    },
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
(4)更改vite.config.js配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import { visualizer } from 'rollup-plugin-visualizer'; //视图分析工具
import { svgBuilder } from '@/src/plugins/svgBuilder'; 
export default defineConfig({
    base: '/',
    plugins: [
        vue(),
        svgBuilder('./src/assets/svg/'),// 这里已经将src/assets/svg/下的svg全部导入,无需再单独导入
        //视图分析工具
        visualizer({
            open: true, //在默认用户代理中打开生成的文件
            gzipSize: true, // 收集 gzip 大小并将其显示
            brotliSize: true, // 收集 brotli 大小并将其显示
            filename: "repotrs.html", // 分析图生成的文件名
        }),
    ],
    resolve: {
        // 解决公共路径问题
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    },
    
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
(5)使用
yarn add @element-plus/icons-vue

### 注册所有图标
从 `@element-plus/icons-vue` 中导入所有图标并进行全局注册

// main.ts
// 如果您正在使用CDN引入,请删除下面一行。
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.