likes
comments
collection
share

Nuxtjs 项目使用 svg-sprite-loader 更改 svg 的颜色

作者站长头像
站长
· 阅读数 22

前言

由于一些原因,最新的公司项目要使用的技术切换到了 Nuxt.js,也就是 Vue 的 ssr 服务端渲染框架。

项目需求

在新项目的开发过程中,遇到了一个需求:有一栏带 icon 的 tab,要实现鼠标悬浮和 tab 在激活状态下,icon 和文字一起改变颜色。

第一次开发

解决思路

在之前使用 svg icon,都是直接把它放进 img 里来使用的,也就是这样:

<img src='assest/svg/icon_xxx.svg' />

这样的形式。这次开发我第一时间想到的还是这样的引入形式,然后在鼠标悬浮和激活时,把其替换成 active 状态的 icon,也就是这样:

<img v-if="active" src='assest/svg/icon_xxx_active.svg' />
<img class="icon-xxx" v-else src='assest/svg/icon_xxx.svg' />
//...

<style lang="scss" scoped>
.icon-xxx {
    &:hover {
        content: url(assets/svg/icon_xxx_active.svg);
    }
}
</style>

问题

由于激活和未激活状态下,使用的并不是同一个 icon,在网速有点慢的情况下,就会出现,鼠标悬浮上去,icon 消失的情况。这是因为 active 的 icon 还没加载出来。那这样的体验无疑是很差的。

第二次开发

解决思路

不能用两个 icon 互相替换这样的形式。应该使用同一个 icon,并且在激活状态下更改它的颜色。那么通过 img 标签引入 svg 这样的形式肯定是不行的了。

然后找到了解决方案:svg-sprite-loader

第二次开发

  1. 安装依赖
yarn add --dev svg-sprite-loader
  1. 创建 SvgIcon 组件

components 目录下创建 SvgIcon.vue 文件

<template>
  <svg :class="svgClass" aria-hidden="true">
    <use :href="iconName"></use>
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true,
    },
    className: {
      type: String,
      default: '',
    },
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    },
  },
}
</script>

<style lang="scss" scoped>
.svg-icon {
  fill: currentColor !important;
  overflow: hidden;
}
</style>
  1. 一次性引入全部 icon

plugins 目录下,创建文件 svg-icon.js

import Vue from 'vue'
import SvgIcon from '~/components/SvgIcon'

// 注册组件
Vue.component('SvgIcon', SvgIcon)
const req = require.context('~/assets/svg', true, /.svg$/)
const requireAll = (requireContext) => requireContext.keys().map(requireContext)
requireAll(req)
  1. 修改 build 配置 修改 nuxt.config.js 文件
const { resolve } = require('path')
const nuxtConfig = {
    //...
    plugins: [
        //...,
        '~/plugins/svg-icon',
    ],
    build: {
        //...
        extend(config, ctx) {
          const svgRule = config.module.rules.find((rule) =>
            rule.test.test('.svg')
          )
          svgRule.exclude = [resolve(__dirname, 'assets/svg')]
          config.module.rules.push({
            test: /.svg$/,
            include: [resolve(__dirname, 'assets/svg')], 
            use: [
              {
                loader: 'svg-sprite-loader',
                options: { symbolId: 'icon-[name]' },
              },
            ],
          })
        },
    }
}
  1. 使用

这时,我们可以在代码里使用 svg-icon 组件,但是还有一个要注意的问题:在 svg 文件中,要将其颜色的 fill 属性的值修改为 currentColor

<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>ThumbsUp</title>
    <g id="xxx" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="xxx" transform="translate(-80.000000, -202.000000)">
            <g id="xxx" transform="translate(66.000000, 168.000000)">
                <g id="ThumbsUp" transform="translate(14.000000, 34.000000)">
                    <rect id="矩形" x="0" y="0" width="20" height="20"></rect>
                    <path d="M18.1141664,6.25907891 C17.7581668,5.8560092 17.2463006,5.62510827 16.7085266,5.625 L12.4995422,5.625 L12.4995422,4.375 C12.4975824,2.64992258 11.0996196,1.25195979 9.37454219,1.25 C9.1378058,1.25 8.92138498,1.38374168 8.81553672,1.59549688 L5.86326563,7.5 L2.49954219,7.5 C1.80951339,7.50078916 1.25033135,8.05997121 1.24954219,8.75 L1.24954219,15.625 C1.25033135,16.3150288 1.80951339,16.8742108 2.49954219,16.875 L15.7710266,16.875 C16.7161444,16.8736603 17.5130326,16.1701949 17.631607,15.2325437 L18.569107,7.73254375 C18.6356533,7.19888546 18.4699883,6.66232865 18.1141664,6.25907891 Z M2.49954219,8.75 L5.62454219,8.75 L5.62454219,15.625 L2.49954219,15.625 L2.49954219,8.75 Z" id="形状" fill="currentColor" fill-rule="nonzero"></path>
                </g>
            </g>
        </g>
    </g>
</svg>

然后,在我们的代码里使用 svg-icon 组件啦!

<template>
    <svg-icon icon-class="icon_xxx" />  // icon-class 对应的是 icon 的名字
</template>
<style lang="scss" scoped>
    svg {
        color: red;
        &:hover {
            color: black;
        }
    }
</style>

此时,svg 修改颜色就完成了

总结

使用 svg-sprite-loader 会让你的 svg 引入写起来非常的舒服,可以随意修改其颜色,就是配置起来稍稍麻烦一点。但对比开发体验来说,这点时间花费,是完全值得的!