vue cli3配置使用svg (记录踩过的坑)

SVG

简单理解svg

SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
SVG 用于定义用于网络的基于矢量的图形
SVG 使用 XML 格式定义图形
SVG 图像在放大或改变尺寸的情况下其图形质量不会有损失
SVG 是万维网联盟的标准

简单理解svg图标

一、Sprite技术
这里所说的Sprite技术,没错,类似于CSS中的Sprite技术。图标图形整合在一起,实际呈现的时候准确显示特定图标。
2.SVG Sprites与symbol元素
目前,SVG Sprite最佳实践是使用symbol元素。symbol元素是什么呢?单纯翻译的话,是“符号”的意思。然,这个释义并不符合这里的场景。不知大家有没有用过Flash,symbol实际上就类似于Flash中的“影片剪辑”、或者“元件”。

因此,我个人觉得,symbol应该解释为“元件”最为恰当!

那,symbol和SVG Sprite又有什么关系呢?

我们可以把SVG元素看成一个舞台,而symbol则是舞台上一个一个组装好的元件,这这些一个一个的元件就是我们即将使用的一个一个SVG图标。

于是,对于一个集合了三个SVG图标的SVG元素的代码结构会是这样:

<svg>
    <symbol>
        <!-- 第1个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第2个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第3个图标路径形状之类代码 -->
    </symbol>
</svg>

每一个symbol就是一个图标元件,但是,只有上面的代码,是无法呈现类似下面的效果的:
vue cli3配置使用svg (记录踩过的坑)
因为,舞台上只是放置了图标,如果你不使用(use),是看不见的。就好比你女朋友买了几箱的衣服放家里,如果不穿出去,谁知道她这么土豪呢?

因此,还差一个“使用”,也就是SVG中的 元素。

三、SVG中的use元素
use元素是SVG中非常强大,非常重要的一个元素,尤其在Web开发中,为何?
两点:
可重复调用;
跨SVG调用;
1. 可重复调用
你好不容易,用了几十个坐标值,好不容易绘制了一个图形,如果你想再弄一个同样造型,但位置不同的图形出来,你会怎么办?——再复制一遍代码?别说笑了,(如果真那样)SVG文件的尺寸赶得上二师兄的腰围了。
使用 元素就可以,看下面的板栗:

<svg>
  <defs>
    <g id="shape">
        <rect x="0" y="0" width="50" height="50" />
        <circle cx="0" cy="0" r="50" />
    </g>
  </defs>
  <use xlink:href="#shape" x="50" y="50" />
  <use xlink:href="#shape" x="200" y="50" />
</svg>

结果是(IE9+浏览器和现代浏览器可见):
vue cli3配置使用svg (记录踩过的坑)
首先,注意到没有,use元素是通过xlink:href属性,寻找要使用的元素的。#shape对应的就是id为shape的元素。use元素可以有自己的坐标,以及支持transform变换,甚至可以use其他use元素。

这里,两个use元素使用的是同一个g元素(组合),从而实现了图形的重复调用功能。

2. 跨SVG调用
SVG中的use元素可以调用其他SVG文件的元素,只要在一个文档中。


vue cli3配置使用svg (记录踩过的坑)
结果仍是那个图形:
而这个跨SVG调用就是“SVG Sprite技术”的核心所在。
试想下,我们只要在页面某处载入一个充满Sprite(symbol)的SVG文件(或直接include SVG代码),于是,在页面的任何角落,只要想使用这个图标,只要简单这一点代码就可以了:

<svg class="size"><use xlink:href="#target" /></svg>

图标尺寸CSS控制,里面只有一个仅有xlink:href属性的use元素,Done! 完成!
这部分内容来自:

https://www.zhangxinxu.com/wordpress/2014/07/introduce-svg-sprite-technology/?shrink=1

优缺点比较

1.为什么使用svg-sprite-loader?

通常我们项目都是使用iconfont阿里巴巴图标矢量库,缺点:
操作繁琐,每次ui需要加一些新的图标的时候,都要重新下载图标库的项目,然后把整体的文件(其中包括css,svg,ttf,woff等)替换掉。
如果更改名称的话,需要在图标库里改一次,然后进行下载复制拷贝。
iconfont网站库图标内容有限,特殊业务条件需要uimm们自己设计的图(PSD小图标变身SVG Sprites/font-face历险记)

svg优点:
图标易于实时修改
图标可以带动画
可以使用标砖的prop和默认值来将图标保持在一个典型的尺寸并随时按需改变他们
图标是内联的,所以不需要额外的HTTP请求
可以动态地使得图标可访问
这部分来自:

https://blog.csdn.net/maggie_live/article/details/90169182?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control

vue cli3 项目使用svg步骤(想直接配置的看这里就好)

我们可以直接在 iconfont 复制 svg 到项目用就行,但是如果要用很多,那就很不明智了,所以下面方式配置成组件使用方式,方便,有效…

1.安装依赖(现在版本 ^5.0.0)
npm install svg-sprite-loader -D

2.在vue.config.js配置文件中增加svg的配置
注意,这个文件的配置,网上有两种配置方法,我自己配置时都试过了,一种不行, 我现在也不知道为什么不行,就是图标显示不出来,没有报错,把svg-sprite-loader 版本降低到 3.8.0 也不行,嗯嗯嗯…我现在用第二种配置,具体如下:

第一种配置:
这个我配置不行,有知道原因的分享交流

const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  chainWebpack(config) {
      //排除icons目录中svg文件处理
      config.module
        .rule('svg')
        .exclude.add(resolve('src/icons')) // 这里是svg文件目录,后面会讲到
        .end();
      //设置svg-sprite-loader处理icons目录中的svg
      config.module
        .rule('icons')
        .test(/\.svg$/)
        .include.add(resolve('src/icons'))
        .end();
        .use('svg-sprite-loader')
        .loader('svg-sprite-loader')
        .options({ symbolId: 'icon-[name]' })
        .end()
    }
    const svgRule = config.module.rule('svg')
    svgRule.uses.clear()
    svgRule
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
    }

第二种:
一些说明:
vue-cli3 默认会通过 file-loader 对 .svg 文件进行处理,这里我们并不想让它处理我们的 .svg 图标文件,但是有的 .svg 文件又确确实实需要用它处理(总不可能所有的 svg 文件都用来做图标吧),所以我们要排除掉 file-loader 对 src/icons/svg 这个文件夹的处理

const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  chainWebpack(config) {
  const svgRule = config.module.rule('svg');
    // 清除已有的所有 loader。
    // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
    svgRule.uses.clear();
    svgRule
      .test(/\.svg$/)
      .include.add(path.resolve(__dirname, './src/icons/svg')) // 文件目录
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      });
      // 修改file对svg的处理,不让它处理
    const fileRule = config.module.rule('file');
    fileRule.uses.clear();
    fileRule
      .test(/\.svg$/)
      .exclude.add(path.resolve(__dirname, './src/icons/svg'))
      .end()
      .use('file-loader')
      .loader('file-loader');
      // 修改images loader 添加svg处理,这个可以不添加,我看到有的有加
    // const imagesRule = config.module.rule('images')
    // imagesRule.exclude.add(resolve('src/icons'))
    // config.module
    //   .rule('images')
    //   .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
  }

}

3. 在 components 中创建 SvgIcon 组件
vue cli3配置使用svg (记录踩过的坑)
组件内容:

<template
<!-- class绑定类名 -->
  <svg :class="svgClass" aria-hidden="true">
 <!-- use绑定id -->
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
export default {
  name: 'svg-icon',
  props: {
    // 使用时 图标对应的文件名
    iconClass: {
      type: String,
      required: true
    },
    //图标类名,通过这个属性自定义类名,方便修改图标大小,颜色
    className: {
      type: String
    }
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    }
  }
}
</script>
<style  scoped>
.svg-icon {
  /* 默认样式设置 默认宽高1em*/
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  /* 当前颜色 */
  fill: currentColor;
  overflow: hidden;
}
</style>

4. 在 src 目录下创建 icons文件夹, icons/svg 用来放 svg文件, icons/index.js 写相关js
vue cli3配置使用svg (记录踩过的坑)
(1) svg文件放我们从阿里 iconfont 下载下来的svg文件
vue cli3配置使用svg (记录踩过的坑)
(2) index.js 文件如下
这里是全局注册组件
因为可能很多地方都会用到图标,这里选择全局注册 SvgIcon.vue 组件

import Vue from 'vue'
import svgIcon from '../components/svgIcon/svgIcon.vue' // 组件引入

Vue.component('svg-icon', svgIcon) //挂载全局组件

//下面这个是导入svgIcon/svg下的所有svg文件
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
/*
   第一个参数是:'./svg' => 需要检索的目录,
   第二个参数是:false => 是否检索子目录,
   第三个参数是: /\.svg$/ => 匹配文件的正则
  */
requireAll(req)

5. 在 main.js 中引入
vue cli3配置使用svg (记录踩过的坑)
6.使用
直接在想要用的地方用就行, 其中icon-class 后面是文件名(icons/svg/), className 后面是自己定义的类名,可以通过类名修改图标大小 和 颜色

 <template>
  <div>
    <h3>SVG图标配置使用</h3>
    <-- 驼峰或者 - 写法都可 -->
    <svg-icon icon-class="pay" className="icon1"></svg-icon>
    <svg-icon iconClass="TXT" class-name="icon2"></svg-icon>
  </div>
</template>
<script>
export default {}
</script>

<style scoped>
/* 单色图标直接通过类名就可以修改颜色和大小 */
.icon1 {
    font-size: 60px;
    color: red;
}

.icon2 {
    font-size: 50px;
    color: blue;
    }

 svg path {
    fill: red;
    /* fill: currentColor */
}
</style>

vue cli3配置使用svg (记录踩过的坑)

需要注意:

  1. 修改时通过 font-size 修改大小,不是width, height,
  2. 单色图标: 直接通过自定义类名修改大小和颜色就行
  3. 多色图标: 直接自定义类名可以修改大小,但是颜色不会变
    因为从 iconfont 下载下来的图标文件默认没有内联的 fill 属性,所以可以像上面那样直接为 svg 元素指定 fill 属性,fill 会继承给子元素;如果下载的时候选择了颜色,就会多出来内联的 fill 属性,此时需要显式指定子元素的 fill 继承自父元素(否则继承的权重很低,样式无法被应用):

修改方法(推荐方式2):

方式1:

svg path {
    fill:inherit
    /* 
    fill: red; 直接写颜色也行
    */
}

为什么这里不能写成下面这样呢?

.icon path {
fill:inherit
}
这是因为 svg->use 里面会生成一个 shadow dom,这个 shadow dom 包含了 svg->path,它是无法通过 css 选择器拿到的,所以上面这个样式声明不会起效果。
vue cli3配置使用svg (记录踩过的坑)
当然还可以用 currentColor 修改图标颜色。因为在元素自身没有 color 属性的时候,它的 currentColor 会继承父元素的 color 属性,所以可以给 .icon 设置 color,并指定每一个 path 的 fill 属性都是 currentColor :


.icon {
   color:#fff
}

svg path {
   fill:currentColor
}

这部分来自于:

https://cloud.tencent.com/developer/article/1624103

要注意的是,在vue单文件使用scoped时不生效,我使用样式穿透也没行,后面就没深专了,,多色图标这样改也会把全部颜色改成一样,还不如用单色,修改方便,所以可以用第二种方式,直接改svg文件,把对应的改成想要的颜色就行,一般ui设计师会设计好给前端用吧…

方式2:
通过直接改svg文件里面的 fill 对应的颜色修改,拉到后面就可以看到,前面都是path, 或者搜索一下,修改成想要的颜色就可,或者删除掉要修改颜色的 fill ,然后自定义类名设置的颜色就可以发挥效果了。
vue cli3配置使用svg (记录踩过的坑)

总结

我们可以直接下载字体图标下来引入到项目, 也可以用上面配置成组件的方式使用, 使用组件更加方便等。
其实iconfont也提供了symbol使用方式,也是推荐使用的方式,只不过现在有兼容性问题,以后这种方式会用的越来越多吧
vue cli3配置使用svg (记录踩过的坑)
写的不好地方多多交流…哈…呼…

上一篇:vue_cli3配置跨域


下一篇:Vue-cli3开发单文件