Vue使用svg-sprite-loader自动引入svg图标 [自己遇到的问题解决]


我是在网上看到了在vue 中优雅的使用 svg 的文章, 就想学习使用一下, 但是自己在使用的时候遇到了问题, 问题已经解决, 分享一下使用方法, 我遇到的问题, 解决办法.

写完之后发现写的可能有些啰嗦 😂
我的项目目录

在这里插入图片描述

现在未配置之前使用 svg

在这里插入图片描述

1. 安装 svg-sprite-loader

npm i svg-sprite-loader -S

2. 在 vue.config.js 中配置 svg-sprite-loader

可以使用 vue inspect --rule svg 查看当前 svg 的处理规则
vue inspect 的简单使用可以戳这里
在这里插入图片描述

vue.config.js 配置

这是我的 vue.coonfig.js 文件的配置 (只配置了svg相关的

// vue.config.js
const path = require('path')
const resolve = dir => path.join(__dirname, dir)

module.exports = {
  chainWebpack: config => {
    // 1. 取消默认 file-loader 对 svg 文件的处理
    config.module
      .rule('svg') // 对 svg 规则的配置
      .exclude // 添加忽略文件夹, 忽略file-loader对这个文件夹下的svg文件处理
      // 建议使用绝对路径
      // .add('./src/assets/icons/svg') // 相对路径
      .add(resolve('./src/assets/icons/svg')) // 绝对路径
      .end()
    config.module
      .rule('icons') // 配置 icons 的规则
      .test(/\.svg$/) // 匹配以 .svg 结尾的文件
      .include // 添加要处理的文件夹
      .add(resolve('./src/assets/icons/svg'))
      .end()
      .use('svg-sprite-loader') // 使用 svg-sprite-loader 处理
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]' // 配置 symbol id, 需要使用这个 id 来使用 svg 图标
      })
      .end()
  }
}

为什么我要使用绝对路径
我看着网上教程配置的时候都是使用的相对路径, 但是我使用的时候却出现了错误, 报错exclude配置的路径不是一个绝对路径
在这里插入图片描述

配置之后再可以查看是否配置成功

在这里插入图片描述在这里插入图片描述
配置完成之后记得重新启动项目.
配置成功就可以在页面中 引入svg并使用 并且 原来使用 img 标签显示不出来
在这里插入图片描述
当然在每个页面都要引入svg会很麻烦. 下面配置自动引入和 svg 组件

3. 配置自动引入svg, 配置完成之后只需要下载svg -> 把 svg 文件放到指定文件夹中就可以直接使用.

根据自己的项目目录在新建 一个js 文件. 我是在 icons 下面新建了 indes.ts (我使用的ts 文件
文件内容:

// index.ts
// require.context 可以获取到项目目录中的文件.
// require.context(路径, 是否递归子文件夹, 要处理什么文件)
const req: any = require.context('./svg', false, /\.svg$/)
req.keys().map(req) // 得到的是文件路径<处理后路径 /img开头>的数组

main.js 中引入此文件就会自动处理指定文件夹下的所有 .svg 文件
在这里插入图片描述

4. 封装一个 svg-icon 组件

src/components 路径下新建 svg-icon 组件

.ts 版本
// ts 版本
<template>
  <svg
    :class="svgClass"
    aria-hidden="true"
  >
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'

@Component
export default class IconSvg extends Vue {
  @Prop({ required: true }) private iconClass!: string
  private name: string = 'icon-svg'
  private data(): object {
    return {
      isCommon: true,
    }
  }
  public get iconName() {
    return `#icon-${this.iconClass}`
  }
  public get svgClass() {
    return this.iconClass ? 'svg-icon ' + this.iconClass : 'svg-icon'
  }
}
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>
.js 版本
<template>
  <svg
    :class="svgClass"
    aria-hidden="true"
  >
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    }
  },
  computed: {
    iconName () {
      return `#icon-${this.iconClass}`
    },
    svgClass () {
      return this.iconClass ? 'svg-icon ' + this.iconClass : 'svg-icon'
    }
  }
}
</script>

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

全局注册此组件即可使用.
在这里插入图片描述

5. 自动注册组件

我配置了自动注册组件, 分享一下.
我在components下新建了 component.ts

.ts版本
import { Vue } from 'vue-property-decorator'
const files: any = require.context('./', true, /\.vue$/)
const components: any = {}

files.keys().forEach((key: string) => {
  try {
    const str = key.match(/[^/]+$/)![0].replace(/\.vue$/, '')
    // 如果组件的data中有 isCommon 就代表是全局注册的组件.
    if (files(key).default.extendOptions.data().isCommon) {
      components[str] = files(key).default
    }
  } catch { }
})

// 全局注册组件, 我组件名称使用的是大驼峰写法, SvgIcon 注册后使用 <svg-icon />
Object.keys(components).forEach((key: string) => {
  Vue.component(key, components[key])
})
.js 版
import Vue from 'vue'
const files = require.context('./', true, /\.vue/)

files.keys().forEach(filePath => {
  const component = files(filePath).default
  // 组件注册的时候可以使用 name. 我的ts中使用的是文件名.
  Vue.component(component.name, component)
})