svg引入及问题解决

2,173 阅读2分钟

项目使用 vue + webpack + typescript。

所使用的svg图标为从iconFont上下载得到,需要引入至项目中。

1. 直接引入

首先解决如何引入的问题。

直接使用 import 引入并不可行,需要在shims-vue.d.ts文件中加入以下代码:

declare module '*.svg' {
  const content: string;
  export default content;
}

此时通过import得到的为对应的svg的路径,但这并不是我们想最终得到的。

2. 使用svg-sprite-loader引入

在js中导入svg图标

import x from '@/assets/icons/label.svg';

在vue.config.js中加入以下代码:

const path = require('path')

module.exports = {
  lintOnSave: false,
  chainWebpack: config => {
    const dir = path.resolve(__dirname, 'src/assets/icons') //此处为存放svg的文件目录
    config.module
      .rule('svg-sprite')
      .test(/\.svg$/)
      .include.add(dir).end() //只包含icons目录
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({ extract: false })
      .end()
    config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{ plainSprite: true }])
    config.module.rule('svg').exclude.add(dir) // 其他svg loader 排除 dir 目录
  }
}

svg-sprite-loader会把svg变成symbol,symbol对应的id为label,并在symbol外套一个svg,在body里生成。

在template中使用

<template>
    <svg>
         <use xlink:href="#xxx"/>
    </svg>
</template>

3. import目录

再来解决一个问题:一个一个引入文件并不方便。

在使用svg-sprite-loader后,我们仍然需要一个一个import,所以来一次性将文件夹下所有文件引入

const importAll = (requireContext: __WebpackModuleApi.RequireContext) =>
  requireContext.keys().forEach(requireContext);
try {
  importAll(require.context("../assets/icons", true, /.svg$/));
} catch (error) {
  console.log(error);
}

对应的文件夹下以svg结尾的文件都会被引入到当前文件中。

以上代码为ts,需要安装一下类型支持

yarn add --dev @types/webpack-env

4. 使用svgo-loader

在实际使用中遇到的新的问题: 在试图使用css的color属性来指定svg图标的颜色时,不起作用。

经过查资料和看源文件,发现svg文件中的fill属性指定了颜色,导致修改无效,只要将该属性删除即可。

但当svg文件较多时,手动删除十分低效,所以此处使用svgo-loader来解决这一问题。

在vue.config.js中补充这一段代码

  .use('svgo-loader')
  .loader('svgo-loader')
  .tap(options => ({ ...options, plugins: [{ removeAttrs: { attrs: 'fill' } }] })) //删除fill属性
  .end()

5. 封装为Icon组件

至此,我们已解决了svg的引入及使用问题,但每次使用的需要重复use。

通过封装Icon组件,来减少重复书写代码。

template部分

<template>
  <svg class="icon">
    <use :xlink:href="'#' + name" />
  </svg>
</template>

script部分

<script lang='ts'>
const importAll = (requireContext: __WebpackModuleApi.RequireContext) =>
  requireContext.keys().forEach(requireContext);
try {
  importAll(require.context("../assets/icons", true, /.svg$/));
} catch (error) {
  console.log(error);
}
export default {
  name: "Icon",
  props: ["name"],
};
</script>

该Icon组件接收properyname,通过此参数可指定使用的svg图标。

<Icon name="money" />

浏览发现一篇其他人写的对代码原理更详细解释的博客, 使用 svg-sprite-loader、svgo-loader 优化项目中的 Icon