vue3项目引入svg-基于vuecli-webpack

需求描述

vue项目中要想使用svg图片,变色会比较方便。

svg图片在vue项目中变色的逻辑

svg图片的fill属性可以设置图片的颜色。但是大部分svg图片内层的标签都会设置fill属性。在vue中一般是在外层svg设置className,然后把内层标签的fill属性都删除,这样内层标签就可以继承fill属性,实现变色。

实现方案

方案一:手动复制svg代码,做成vue组件

一种引入svg图片的方法是直接把svg代码复制到文件中,做成vue组件,使用的时候按照组件的方式引入就可以。–这种方法太过繁琐。需要一个个图片引入、修改

<template>
  <svg
    width="14px"
    height="14px"
    viewBox="0 0 14 14"
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    class="svg-icon"
  >
    <title>工作站默认</title>
    <defs>
      <rect id="path-1" x="0" y="0" width="14" height="14"></rect>
    </defs>
    <g
      id="页面-1"
      stroke="none"
      stroke-width="1"
    >
      <g id="导航栏图标展示" transform="translate(-26, -121)">
        <g id="工作站默认" transform="translate(26, 121)">
          <mask id="mask-2" >
            <use xlink:href="#path-1"></use>
          </mask>
          <g id="蒙版"></g>
          <g id="编组" mask="url(#mask-2)">
            <g transform="translate(1, 1)">
              <path
                d="M1.086,1.715 C1.086,1.459 1.127,1.284 1.204,1.212 C1.254,1.165 1.396,1.087 1.788,1.087 L4.043,1.087 C4.434,1.087 4.575,1.165 4.626,1.212 C4.703,1.284 4.744,1.458 4.744,1.715 L4.744,10.534 C4.744,10.79 4.703,10.965 4.624,11.039 C4.576,11.086 4.435,11.163 4.043,11.163 L1.788,11.163 C1.395,11.163 1.254,11.085 1.205,11.039 C1.127,10.965 1.086,10.79 1.086,10.534 L1.086,1.715 Z M4.043,0 L1.788,0 C1.186,0 0.763,0.133 0.46,0.419 C0.15,0.711 0,1.134 0,1.715 L0,10.534 C0,11.115 0.15,11.539 0.46,11.83 C0.742,12.096 1.127,12.23 1.663,12.248 L4.043,12.25 C4.644,12.25 5.066,12.116 5.37,11.83 C5.68,11.539 5.831,11.114 5.831,10.534 L5.831,1.715 C5.831,1.135 5.68,0.711 5.371,0.42 C5.067,0.133 4.645,0 4.043,0 L4.043,0 Z M7.918,1.6481 C7.918,1.4901 7.937,1.2861 8.025,1.2071 C8.076,1.1621 8.22,1.0861 8.619,1.0861 L10.875,1.0861 C11.274,1.0861 11.418,1.1621 11.469,1.2071 C11.557,1.2861 11.576,1.4901 11.576,1.6481 L11.576,4.1831 C11.576,4.4091 11.539,4.5611 11.469,4.6221 C11.421,4.6651 11.286,4.7361 10.916,4.7381 L10.879,4.7381 L8.619,4.7381 C8.219,4.7381 8.075,4.6641 8.025,4.6201 C7.937,4.5421 7.918,4.3361 7.918,4.1771 L7.918,1.6481 Z M12.191,0.3941 C11.888,0.1251 11.47,9.99999993e-05 10.875,9.99999993e-05 L8.619,9.99999993e-05 C8.024,9.99999993e-05 7.606,0.1251 7.304,0.3941 C6.986,0.6771 6.831,1.0871 6.831,1.6481 L6.831,4.1771 C6.831,4.7411 6.986,5.1521 7.304,5.4351 C7.585,5.6821 7.965,5.8071 8.494,5.8241 L10.871,5.8261 L10.897,5.8261 C11.478,5.8261 11.889,5.7021 12.19,5.4371 C12.508,5.1551 12.663,4.7451 12.663,4.1831 L12.663,1.6481 C12.663,1.0871 12.508,0.6771 12.191,0.3941 L12.191,0.3941 Z M7.918,8.2067 C7.918,7.8437 7.989,7.6957 8.049,7.6357 C8.108,7.5767 8.255,7.5057 8.619,7.5057 L10.875,7.5057 C11.238,7.5057 11.386,7.5767 11.445,7.6357 C11.505,7.6957 11.576,7.8427 11.576,8.2067 L11.576,10.4617 C11.576,10.7497 11.531,10.9477 11.446,11.0327 C11.385,11.0917 11.237,11.1637 10.875,11.1637 L8.619,11.1637 C8.256,11.1637 8.109,11.0927 8.049,11.0327 C7.963,10.9467 7.918,10.7497 7.918,10.4617 L7.918,8.2067 Z M10.875,6.4187 L8.619,6.4187 C8.02,6.4187 7.582,6.5657 7.279,6.8677 C6.978,7.1687 6.831,7.6067 6.831,8.2067 L6.831,10.4617 C6.831,11.0607 6.978,11.4987 7.279,11.8007 C7.56,12.0817 7.958,12.2277 8.494,12.2477 L10.875,12.2497 C11.475,12.2497 11.913,12.1027 12.214,11.8007 C12.516,11.4997 12.663,11.0617 12.663,10.4617 L12.663,8.2067 C12.663,7.6057 12.516,7.1677 12.214,6.8677 C11.913,6.5657 11.474,6.4187 10.875,6.4187 L10.875,6.4187 Z"
                id="形状结合"
              ></path>
            </g>
          </g>
        </g>
      </g>
    </g>
  </svg>
</template>

<script setup lang="ts">
import { ref } from "vue";
const color = ref("yellow");
</script>
<style lang="scss" scoped>
//这里可以自定义一些样式,例如:
.svg-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  overflow: hidden;
  vertical-align: -0.15em;
  fill: red;
}
</style>

方案二 使用插件完成svg图片引入和处理

此方案分两步,
第一步,把svg图片能自动化引入。不用一个个的做成vue组件。
第二步,自动化处理svg图片,把svg标签内的fill属性自动化删除。

第一步:自动化引入svg图片

使用 svg-sprite-loader 可以把svg图片项雪碧图一样插入到页面中。首先安装依赖。

yarn add svg-sprite-loader --save -D
# 或
npm install svg-sprite-loader --save -D

然后配置文件写入这个loader的加载规则,以下是vue.congfit.ts的文件,我的项目是用的vuecli搭的是基于webpack的。如果是vite项目需要自行去寻找配置方案。

const { defineConfig } = require("@vue/cli-service"); // 通过这个引入defineConfig方法,可以配置vue.config.js
const path = require("path"); // 通过这个引入path模块,有一些跟项目路径有关的方法
// 拼接项目绝对路径的方法
function resolve(dir) {
  return path.join(__dirname, dir);
}
module.exports = defineConfig({
  chainWebpack: (config) => {
    // 内置的svg处理排除指定目录下的文件,注意src/svg/icons改成你自己项目中存svg图片的相对路径-这行可以不加
    config.module.rule("svg").exclude.add(resolve("src/svg/icons")).end();
    // 自定义svg处理,使用
    config.module
      .rule("svg-sprite-loader") // 添加svg-sprite-loader
      .test(/\.svg$/) // 处理以.svg结尾的文件
      .include.add(resolve("src/svg/icons")) // 指定处理svg文件所在的目录,注意这里换成自己的
      .end() // 结束
      .use("svg-sprite-loader") // 添加svg-sprite-loader
      .loader("svg-sprite-loader") // 指定svg-sprite-loader
      .options({ // 指定svg-sprite-loader的配置
        symbolId: "icon-[name]", // 指定symbol的id格式,也就是处理成雪碧图后的给的id值,是”icon-文件名“
      })
      .end()
  },
});

对loader处理的规则规定好了之后就需要一个ts/js文件动态的import进来存放svg目录下所有的svg图片。我这里直接与svg组件写在了同个目录下,但是其实他们没什么关联。
在components下创建了SvgIcon目录,并创建index.ts文件
路径:src/components/SvgIcon/index.ts
文件:注释内是下面方法用到方法的解释。网址是webpack中require.context这个方法的说明。

// https://webpack.docschina.org/guides/dependency-management/#requirecontext
// require.context(
//   directory, //这里指存放svg文件目录的路径
//   (useSubdirectories = true), //是否还搜索其子目录,默认为true
//   (regExp = /^\.\/.*$/), //正则匹配文件类型, 这里我们匹配 .svg 结尾的文件 /\.svg$/
//   (mode = 'sync') //模式,默认同步
// );
// // request返回结果为一个函数,并且此函数身上存在3个属性resolve, keys, id
// const request = require.context('../../svg/icons', false, /\.svg$/)
// // 动态引入svgicons文件夹下的所有文件
// function importAll(req: __WebpackModuleApi.RequireContext) {
//   req.keys().forEach(req)
// }
// importAll(request)

export default function importAllSvgIcons() {
  try {
    const request: __WebpackModuleApi.RequireContext = 
       require.context('../../svg/icons', false, /\.svg$/)
    request.keys().forEach(request)
  } catch(err) {
    console.log(err)
  }
}

然后创建一个组件,作为引入svg图片的组件。可以根据自己的需求自行修改目录
目录:src/components/SvgIcon/index.vue
内容:

<template>
  <svg class="svg-icon">
    <!-- 通过use标签,id选择器找对应的svg图片 -->
    <use :xlink:href="`#icon-${props.name}`" />
  </svg>
</template>
 
<script setup lang="ts">
import { computed } from "vue";
type Props = {
  name: string
}
const props = withDefaults(defineProps<Props>(), {
  name: ''
})
</script>
<style lang="scss" scoped>
//这里可以自定义一些样式,例如:
.svg-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  overflow: hidden;
  vertical-align: -0.15em;
  fill: red;
}
// 如果上面的样式可以生效,这里就不需要设置全局的。
// 如果上面的没生效,可能是因为svg图片层级太深所以fill属性没继承下去,这里才给全局的svg标签设置填充色。
// :global(svg) {
//   fill: red;
// }
</style>

然后首先把引入svg图片的方法在main.js调用一下。
在main.js里添加代码

import importAllSvgIcons from './components/SvgIcon'
......
importAllSvgIcons()

如果不需要全局引入svg组件,在对应的组件引入上面的svg组件就可以了。如果想要全局,用的时候懒得import,则在main里添加引入的代码,main文件全部代码如下:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

import SvgIcon from '@/components/SvgIcon/index.vue'
import importAllSvgIcons from './components/SvgIcon'

const app = createApp(App)
app.component('svg-icon', SvgIcon) // 全局注册svg-icon组件。
importAllSvgIcons()
app.use(router)
app.mount('#app')
// createApp(App).component().use(router).mount('#app')

这样svg代码就引入了,可以正常使用,在需要显示svg的组件直接写icon-svg标签引入

<template>
  <div class="about">
    <!-- name传入图片名就行,不需要svg后缀。 -->
    <svg-icon :name="iconName"/>
    <!-- 如这里引入的就是设置好的目录下,po.svg图片 -->
    <svg-icon name="po"/>
  </div>
</template>
<script setup lang="ts">
import { ref } from "vue";
const iconName = ref("workstation");
</script>

但是svg中的fill标记还未去掉,设置类的fill属性还无法生效。
接下来引入能自动化去掉svg图片标签内fill属性的插件

第二步:引入自动化处理svg的loader -svgo-loader

安装依赖

yarn add svgo-loader --save -D
# 或
npm install svgo-loader --save -D

配置vue.config.ts

const { defineConfig } = require("@vue/cli-service"); // 通过这个引入defineConfig方法,可以配置vue.config.js
const path = require("path"); // 通过这个引入path模块,有一些跟项目路径有关的方法
// 拼接项目绝对路径的方法
function resolve(dir) {
  console.log("dir: ", dir);
  console.log("path: ", __dirname);
  console.log("path.join: ", path.join(__dirname, dir));
  return path.join(__dirname, dir);
}
module.exports = defineConfig({
  chainWebpack: (config) => {
    // 内置的svg处理排除指定目录下的文件,注意src/svg/icons改成你自己项目中存svg图片的相对路径-这行可以不加
    config.module.rule("svg").exclude.add(resolve("src/svg/icons")).end();
    // 自定义svg处理,使用
    config.module
      .rule("svg-sprite-loader") // 添加svg-sprite-loader
      .test(/\.svg$/) // 处理以.svg结尾的文件
      .include.add(resolve("src/svg/icons")) // 指定处理svg文件所在的目录,注意这里换成自己的
      .end() // 结束
      .use("svg-sprite-loader") // 添加svg-sprite-loader
      .loader("svg-sprite-loader") // 指定svg-sprite-loader
      .options({ // 指定svg-sprite-loader的配置
        symbolId: "icon-[name]", // 指定symbol的id格式,也就是处理成雪碧图后的给的id值,是”icon-文件名“
      })
    .end()
    .before("svg-sprite-loader")
    .use("svgo-loader") 
    .loader("svgo-loader")
    .options({
      plugins: [
        // 各个可删除的属性的配置
        // https://blog.51cto.com/knifeedge/5671578
        {
          name: "removeAttrs",
          // 删除标签的规则
          params: {
            // attrs: "(fill|stroke|fill-rule|stroke-width)", // 删除标签的fill属性,stroke属性,fill-rule属性和stoke-width属性
            attrs: "fill", // 删除fill属性
          },
        },
        // {
        //   name: "removeTitle",
        // },
        // 给外层svg添加class
        // {
        //   name: "addClassesToSVGElement",
        //   params: {
        //     className: "icon-svg"
        //   }
        // }
      ],
    })
    .end();
  },
});

再看引入的svg图片svg属性就生效了,如果没生效,可以看下body标签下第一个标签中的svg图片们fill属性还有保留吗?
如果还有,则是svgo-loader这个没配值好,想办法配置。
如果没有,但是fill属性不生效,就是层级太深,继承不到,因为我们设置的vue组件是通过use标记联到这里的,有时候继承不到fill属性。这时设置全局svg标记的样式的fill颜色就行。
以上便是本次svg图片引入vue3-webpack项目的一些步骤,对小伙伴们有帮助的话请点个赞吧。

  • 21
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值