Vue+TS的项目中引入SVG文件

561 阅读2分钟

最近在做个记账的项目,用了VUE2和TS开发的,项目中要引入一些svg图标。

引入svg图标

首先iconfont下载svg图标,放入assets文件下的icons文件夹 使用import引入,如下:

import xxx from '@/assets/icons/xxx.svg'

问题就来了,TS爆红线,表示找不到当前模块 于是百度了 typescript svg cannot find module 找到答案:

Create a file custom.d.ts with the following content:(新建一个名字为xxxx.d.ts的文件写上下面的代码,于是在 shims-vue.d.ts 文件复制上下面的代码)

declare module '*.svg' {
  const content: any;
  export default content;
}

这下引入成功了,没有下划线报错了,那么问题是如何使用在项目上呢

svg如何使用

首先先打印了下引入得到的是什么

import labels from '@/assets/icons/labels.svg'
console.log(labels)
得到下面的地址字符串
//  /img/labels.c5b46c51.svg

引入得到的是字符串

那么如何使用呢?

  1. 下载下面三个svg的loader
"svg-sprite-loader": "^4.1.6",
// 用webstorm才需要下载mod这个loader
"svg-sprite-loader-mod": "^4.1.6-mod1",
"svgo-loader": "^2.2.1",

SVG的坑

SVG 可能会自带一个 fill 属性,如果有此属性,那么引入后无法再通过 CSS 修改颜色。所以在引入前应将 fill 删掉。但是这样做太低效了!

好在有另一个 loader 可以帮助我们自动完成这一工作,那就是 svgo-loader。

另外一个坑是svg-sprite-loader会破坏webstorm的错误提示,因为它已经好几年没有维护了,所以用webstorm的人可以用svg-sprite-loader-mod,这是有人把svg-sprite-loader的github的issues合并了(然后下面vue.config.js里的svg-sprite-loader都改成svg-sprite-loader-mod)

安装好依赖后需要在vue.config.js里面配置一下webpack

const path = require('path')
module.exports = {
  lintOnSave: false,
  
  chainWebpack: config => {
    const dir = path.resolve(__dirname, 'src/assets/icons') 
    // icon存放路径
    config.module
      .rule('svg-sprite') // 添加规则
      .test(/\.svg$/) //使用条件:.svg结尾的文件
      .include.add(dir).end()   //只包含icons目录
      .use('svg-sprite-loader').loader('svg-sprite-loader').options({extract: false}).end() 
      //webstorm用户需要用svg-sprite-loader-mod这个loader,extract: false表明,不要生成其他的文件
      .use('svgo-loader').loader('svgo-loader') // 此优化器能够自动消除掉fill的内容
      .tap(options => ({...options, plugins: [{removeAttrs: {attrs: 'fill'}}]})).end()
      //移除svg的fill属性
    config.plugin('svg-sprite').use(require('svg-sprite-loader/plugin'), [{plainSprite: true}])
    config.module.rule('svg').exclude.add(dir)  //其他目录的.svg文件,不需要用到以上规则
  }
}

此时算大功告成了,重新运行项目,看看打印出来的结果

企业微信截图_16890409522750.png

看,此时打印出来的是一个symbol

那我们再看一看body文件里面

企业微信截图_1689041095260.png

它会在body下面添加一个svg标签,svg下面有你inport进去的那些svg文件的symbol, symbol里都有一个id

这样我们就可以使用了,如下:

<template>
    <layout-wrapper>
        <svg>
            <use xlink:href="#labels" />
        </svg>
        <svg>
            <use xlink:href="#money" />
        </svg>
        <svg>
            <use xlink:href="#statistics" />
        </svg>
        Labels Page
    </layout-wrapper>
</template>

使用方法: 用一个svg标签包裹,里面用一个use标签, herf里写上面那个symbol里的id

到此,我们就已经可以成功的使用svg文件了

问题:

那每次引入一个svg文件都要import一次吗?

每次都使用 <svg><use xlink:href="#labels" /></svg> 去添加svg吗

一次性import所有svg

封装个组件吧

SvgIcon.vue文件

<template>
    <svg class="icon">
        <use :xlink:href="'#'+name" />
    </svg>
</template>

<script lang="ts">
    // eslint-disable-next-line no-undef
    // 上面的注释是为了不让eslint报错
    // 下面就是一次性导入所有icons文件下的svg文件的代码
    let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext)
    try {
        importAll(require.context('../assets/icons', true, /\.svg$/));
    } catch (error) {
        console.log(error)
    }
    export default {
        props: ['name']
    }
</script>

<style lang="scss" scoped>
.icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
}
</style>

那么接下来我们就可以在任何文件里使用svg文件了

<svg-icon name="labels" />
<svg-icon name="money" />
<svg-icon name="statistics" />