「抠抠记账反思」引入svg的坑

777 阅读1分钟

一、引入svg的简单方法

在iconfont上选好图标,然后生成一个svg.js, 把这个svg.js保存到项目里,然后引用,就可以使用svg了
代码示例:

Icon.vue

<template>
	<svg class="icon" @click="$emit('click', $event)">
		<use :xlink:href="`#i-${name}`"></use>
	</svg>
</template>

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

  @Component
  export default class Icon2 extends Vue {
    @Prop(String) name: string | undefined;
  }
</script>

上面这种方法虽然简单,但是又有点麻烦,因为当我们需要更新svg图标,就需要重新登录iconfont网站,获取新的svg.js, 然后下载下来,再引入项目,能不能让更新svg简单一些?
答案是可以的。所以我们的目标是:通过webpack来引入svg, 如果有新的svg, 直接把下载好的svg复制到对应的icon文件夹, 然后在任何一个组件里写就可以用这个svg.
接下来将一步步实现这个目标

二、如何用Vue@cli引入svg?

2.1 安装svg loader

我们可以很快地搜索到需要安装svg-sprite-loader, 才能在webpack里使用
但问题是,安装过后,需要配置,但官方文档里的配置是写在webpack.config.js, 而我们的项目是用Vue@cli搭建的,并没有webpack.config.js这个文件,只有vue.config.js

2.2 配置vue.config.js

所以我们需要做的就是把官方文档里 webpack.config.js 关于svg-sprite-loader 的配置 翻译成 vue.config.js 里的配置。于是我就去读了vue的官方文档,发现可以在vue.config.js里用一个chainWebpack来修改webpack的配置
Vue官方给的例子是:

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
        .loader('vue-loader')
        .tap(options => {
          // 修改它的选项...
          return options
        })
  }
}

于是照猫画虎,配置写出来我们需要的vue.config.js

const path = require('path')

module.exports = {
  lintOnSave: false,
  chainWebpack: config => {
    const dir = path.resolve(__dirname, 'src/assets/icons') // 当前目录

    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排除icons目录


  }

}

这样配置完成之后,我们就可以在项目里引用svg了

<svg>
  <use x:link:href="对应的svg图标的name"></use>  
</svg>

2.3 给svg去色

写芒果记账时,有一个需求是高亮svg图标,即本身灰色的svg图标高亮成黄色或者红色,但在写CSS时,我发现有些svg自己带了颜色,无法修改,怎么办呢?
我去打开svg文件,读了读里面的内容,发现一个属性叫fill, 表示填色。于是进一步在控制台里做实验,发现如果去掉svg里的fill属性,那么这个svg就没有自带颜色了。
但问题是,这么多svg图标,难道我们要一个一个地去删除fill属性?
当然不可能。通过搜索,我发现了一个Loader叫 'svgo-loader', 它可以去除fill标签, 配置如下

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

于是现在我们的vue.config.js是:

const path = require('path')

module.exports = {
  lintOnSave: false,
  chainWebpack: config => {
    const dir = path.resolve(__dirname, 'src/assets/icons') // 当前目录

    config.module
        .rule('svg-sprite')
        .test(/\.svg$/)
        .include.add(dir).end() // 只包含icons目录
        .use('svg-sprite-loader').loader('svg-sprite-loader')
        .options({extract: false}).end() //不需要解析成文件
        .use('svgo-loader').loader('svgo-loader')
        .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end()
    config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}])
    config.module.rule('svg').exclude.add(dir) // 其他svg loader排除icons目录
  }
}

这样就可以顺利引入svg且给svg去色

2.4 抽取Icon组件

如果每次引用svg都要写一次:

<svg>
  <use x:link:href="对应的svg图标的name"></use>  
</svg>

实在是有些麻烦,于是我们可以抽取出一个Icon组件,每次这样就好。 Icon组件的实现很简单,就是把本身的代码包裹一层,然后接收一个Name作为参数

Icon组件:

<template>
	<svg class="icon" @click="$emit('click', $event)">
		<use :xlink:href="`#i-${name}`"></use>
	</svg>
</template>

2.5 一次性引入所有svg文件

目前还存在的问题是我们是一个一个地引入svg图标,能不能一次性引入一整个svg文件夹?
可以!用一个ImportAll 方法即可

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


现在我们解决了全部的问题,如果有新的svg, 就把它复制到assets/icons目录下,然后在项目里直接就可以愉快地用它了!