最近在做个记账的项目,用了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
引入得到的是字符串
那么如何使用呢?
- 下载下面三个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文件,不需要用到以上规则
}
}
此时算大功告成了,重新运行项目,看看打印出来的结果
看,此时打印出来的是一个symbol
那我们再看一看body文件里面
它会在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" />