前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何在Vue项目中更优雅地使用svg

如何在Vue项目中更优雅地使用svg

作者头像
Chor
发布2020-05-06 20:27:51
12.1K1
发布2020-05-06 20:27:51
举报
文章被收录于专栏:前端之旅前端之旅

最近看项目视频的时候对里面使用 svg 的方式感到很好奇,于是去网上查了一下,发现 svg 竟然也有类似于 css 雪碧图一样的用法,也就是 svg-sprite(孤陋寡闻了),而且配合插件后能够以组件化的方式使用 svg,非常方便。这里记录下一些相关用法。

视频里的做法是注册这样的一个全局组件:

代码语言:javascript
复制
// icon.vue
<svg>
    <defs>
        <symbol id="icon1">
            <path></path>
        </symbol>
        <symbol id="icon2">
            <path></path>
        </symbol>
        ......
    </defs>
</svg>

css 雪碧图中是把多个背景图片放在一张大的图片中,而 svg 雪碧图则是把多个 symbol 放在一个大的 svg 中,每个 symbol 代表了一个图标,以后每次想要使用图标,只需要写这么一段代码即可:

代码语言:javascript
复制
<svg>
    <use :xlink:href="#icon1"></use>
</svg>

但是这里有两个问题:

  1. 从图标库(比如阿里的 iconfont)下载下来的通常是 .svg 文件,如何根据多个单独的 .svg 文件生成 svg 雪碧图?
  2. 每次要使用图标都得写这么一段代码,并不是很方便,是否可以像使用组件那样使用图标?

这里的关键是使用 svg-sprite-loader 这个插件。

安装插件

首先 npm 安装:

代码语言:javascript
复制
npm install svg-sprite-loader --save

接着我们用一个文件夹专门放各种需要用到的 .svg 文件,这里以 src/assets/img/icons 为例,从 iconfont 下载 .svg 文件后放到这个文件夹即可。

修改配置

vue-cli3 默认会通过 file-loader.svg 文件进行处理,这里我们并不想让它处理我们的 .svg 图标文件,但是有的 .svg 文件又确确实实需要用它处理(总不可能所有的 svg 文件都用来做图标吧),所以我们要排除掉 file-loadersrc/assets/img/icons 这个文件夹的处理。在 vue.config,jsmodule.export 中新增:

代码语言:javascript
复制
module.expors = {
  chainWebpack(config){
   //排除icons目录中svg文件处理
    config.module.rule('svg')
   .exclude.add(resolve('src/assets/img/icons')
   .end()
  }
}

接着,指定 svg-sprite-loader 对图标文件夹里面的 .svg 文件进行处理:

代码语言:javascript
复制
const path = require('path')
function resolve(dir){
    return path.join(__dirname,dir)
}

module.expors = {
  chainWebpack(config){
   //排除icons目录中svg文件处理
    config.module.rule('svg')
   .exclude.add(resolve('src/assets/img/icons')
   .end()
   //设置svg-sprite-loader处理icons目录中的svg
    config.module
    .rule('icons')
    .test(/\.svg$/)
    .include.add(resolve('src/assets/img/icons'))
    .end()
    .use('svg-sprite-loader')
    .loader("svg-sprite-loader")
    .options({symbolId:'icon-[name]'})
    .end()                
  }
}

这样其实已经可以生成 svg 雪碧图了,之后这个雪碧图会作为 svg 元素注入到 html 中:

接下来封装图标组件。

封装图标组件

components/common/icon 下新建一个 SvgIcon.vue 文件:

代码语言:javascript
复制
<template>
  <svg :fill="iconColor" class="svg-icon">
    <use :xlink:href="iconNameCp" />
  </svg>
</template>
<script>
export default {
  name: 'svgIcon',
  props: {
    // 图标类型
    iconName: {
      type: String,
      required: true
    },
    // 图标颜色
    iconColor:{
      type:String,
      default:'#666'
    }
  },
  computed: {
    iconNameCp() {
      return `#icon-${this.iconName}`
    }
  } 
}
</script>

使用组件的时候可以通过传值 iconNameiconColor 确定图标的类型和颜色。

全局注册组件

因为可能很多地方都会用到图标,这里选择全局注册 SvgIcon.vue 组件。在 src/assets/img/icons 文件夹下新建 index.js 文件:

代码语言:javascript
复制
// 全局引入 svgIcon 组件
import Vue from 'vue'
import SvgIcon from '@/components/common/icon/SvgIcon.vue'
Vue.component('svg-icon', SvgIcon)

// 让 icons/svg下面的图片自动导入,而不是每次手动导入
const req = require.context('./svg', false, /\.svg$/);
req.keys().map(req);

之后,在 main.js 中引入 index.js 文件:

代码语言:javascript
复制
import 'assets/img/icons'

使用组件

以后每次要使用图标就非常方便了,只需要一行代码即可:

代码语言:javascript
复制
<svg-icon class="icon" :icon-name="xxxxx" :icon-color="xxxxx"></svg-icon>

样式修改

从 iconfont 下载下来的图标文件默认没有内联的 fill 属性,所以可以像上面那样直接为 svg 元素指定 fill 属性,fill 会继承给子元素;如果下载的时候选择了颜色,就会多出来内联的 fill 属性,此时需要显式指定子元素的 fill 继承自父元素(否则继承的权重很低,样式无法被应用):

代码语言:javascript
复制
svg path {
    fill:inherit
}

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

代码语言:javascript
复制
.icon path {
    fill:inherit
}

这是因为 svg->use 里面会生成一个 shadow dom,这个 shadow dom 包含了 svg->path,它是无法通过 css 选择器拿到的,所以上面这个样式声明不会起效果。

当然还可以用 currentColor 修改图标颜色。因为在元素自身没有 color 属性的时候,它的 currentColor 会继承父元素的 color 属性,所以可以给 .icon 设置 color,并指定每一个 pathfill 属性都是 currentColor

代码语言:javascript
复制
.icon {
   color:#fff
}

svg path {
   fill:currentColor
}

补充

iconfont 本身可以根据添加的图标自动生成 js 代码,之后只需将 js 文件引入项目中即可,这种方式同样可以将 svg 注入到 html 中:

但是这种方式不利于代码的维护,不可能说每一次新增图标都到 iconfont 重新生成一遍代码,再重新引入到项目中,这样太麻烦了。所以才使用了 svg-sprite-loader 插件,这样每次新增图标,只需要下载图标并放到对应文件夹即可。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装插件
  • 修改配置
  • 封装图标组件
  • 全局注册组件
  • 使用组件
  • 样式修改
  • 补充
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档