极简记账之 SVG 引入

451 阅读2分钟

先简单介绍下SVG

什么是SVG?

SVG全称是Scalable Vector Graphics,是指伸缩矢量图片。我们常用的png,jpg图片属于位图,位图是由一个个像素组成的,放大后会变模糊。而SVG是通过软件生成的矢量图片,其元素对象可编辑,图像放大缩小都不影响图像的分辨率。

SVG的优点

  • SVG 是一种基于 XML 语法的图像格式,所以在前端项目中我们可以进行二次编辑,可以修改颜色,大小….
  • SVG 于JPEG 和 GIF 图像比起来,尺寸更小
  • SVG 图像的文本是可选的,同时也是可搜索的

所以我认为SVG很适合在前端项目中应用

怎么引入SVG

最近开发的一个个人理财项目,底部导航栏需要用到SVG图标,设计稿是这样的:

  • 第一步,通过iconfont搜索相应的图标,iconfont是一个阿里巴巴提供的矢量图库,可以免费下载SVG图标,下载后我把SVG图标存在项目的assets/icons文件夹中

  • 第二步,参考iconfont提供的引入方法,我是用的是symbol引入,这是官方文档提供的方法

由于我引入的下载后的SVG文件,js通过import引入

import '@/assets/icons/label.svg'
import '@/assets/icons/money.svg'
import '@/assets/icons/statistics.svg'

HTML 和 CSS 照文档敲,要注意的是 xlink:href=”#icon-xxx” ,icon-xxx是SVG文件名,敲错了就无法显示了。按照官方文档做完了,按理来说SVG就应该显示了,然而导航栏还是一片空白

(⊙o⊙)…

  • 第三步,安装svg-sprite-loader

于是开始上网查,发现是缺少了 svg-sprite-loader

安装命令:

npm i svg-sprite-loader --save
或者
yarn add svg-sprite-loader --dev
  • 在vue.config.js 中添加
 config.module
      .rule('svg-sprite')
      .test(/\.svg$/)
      .include.add(dir).end() // 找到svg-loader
      .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) 

按照上面的代码在VScode运行,有的小伙伴会遇到这个报错 (在require语句的地方)

  • 第四步, 配置vue.config.js
ESLint: Require statement not part of import statement.(@typescript-eslint/no-var-requires)

有两种解决方式

  1. 按照误提示改为 import 引入
import svgLoader from 'svg-sprite-loader/plugin';
// 然后在使用require的地方替换为 svgLoader
config.plugin('svg-sprite').use(svgLoader, [{plainSprite: true}])
  1. 告诉eslint 不检查这行代码(在报错的代码行上一行插入)
 eslint-disable-next-line @typescript-eslint/no-var-requires

现在再次运行代码,SVG图片可以显示了

  • 第五步,优化代码之引入文件

上文中js引入SVG文件是通过import,一个一个文件引入的,以后如果svg越来越多就要多次引入。

于是我在网上找了一个方法可以直接引入icons文件,这样就不用每个icon多次引入了,代码如下

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

  • 第六步, 优化代码之封装成Icon组件

按照文档所说,HTML使用的标签是

<svg class="icon" aria-hidden="true">
    <use xlink:href="'#' + name"></use>
</svg>

我用到了三个SVG图标,不太想重复,我们可以封装成Icon组件

<template>
  <svg class="icon" aria-hidden="true">
    <use :xlink:href= "'#' + name" />
  </svg>
</template>
<script lang='ts'>
  export default {
    props: ['name']
  };
</script>
<style lang='scss' scoped>
  .icon {
    width: 1em; height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
  }
</style>

并在main.ts 中全局引入

import Vue from 'vue';
import Icon from '@/components/Icon.vue';
Vue.component('Icon', Icon);

这样每个组件使用SVG图标时,只需要即可

<Icon name="SVG文件名"/>