svg做图标自动引入和生成
说明
传统方案中,是使用图标生成字体,然后当图标发生变化的时候需要手动的每次去替换,其实可以通过svg-sprite-loader,可以实现增加svg的时候自动加载图标。npm i svg-sprite-loader
探索
整体思路:原来的svg排除一个目录,并增加一个配置
小知识点:
- resolve的作用是传入一个相对路径,返回一个绝对路径
- end方法是结束当前目录回到上一个目录
- 链式操作的文档指南:https://cli.vuejs.org/zh/guide/webpack.html#链式操作-高级
module.exports = {
chainWebpack(config){
// 在已有的svg loader配置中 排除掉对src/icons里svg进行转换
config.module
.rule('svg')
.exclude.add(resolve('src/icons')) // 排除掉src/icons目录
.end()
// svg icon工作原理 https://segmentfault.com/a/1190000015367490
// 配置svg-sprite-loader
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons')) // 指定src/icons要处理svg的文件目录
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader') // 用svg-sprite-loader解析
.options({
symbolId: 'icon-[name]' // symbol id命名格式 icon-图标名称
})
.end()
}
}
//使用
<svg>
<use xlink:href="#icon-qq">
</svg>
import 'icons/svg/qq.svg'
优化
自动引入:用req指定上下文,然后入口文件进行全量的自动引入该目录下所有图片。
疑问点:全量引入会不会很大?
- svg本身都是文本信息,体积比图片小
- 如果实际业务中,对对应图片的使用是部分的,你可以去单独定义按照划分的svg目录,然后按需引入
- 维护的方式是通过按需使用的,如果不需要使用,直接删除;需要新的,进行新增。
//只匹配到当前上下文,不包括子目录,定义匹配的正则文件
const req = require.contex('./svg',false,/\.svg$/);
req.keys().map(req);
//main.js中加载 从而实现全量自动加载所有图标
import 'icons/index.js';
定义svg的使用组件,让使用更加方便,你可以通过传入属性控制其加载的图片文件夹,具体样式等等
//定义一个svgIcon 的组件,然后全局使用
import 'components/svgIcon'
Vue.component('svg-icon',svgIcon)
Nuxt框架配置Svg自动导入
1. 引入解析器插件
https://www.npmjs.com/package/nuxt-svg-sprite-loader
nuxt.config.js
export default {
...
modules: [
['nuxt-svg-sprite-loader', {
symbolId: 'foo-[name]'
}]
]
}
2. 编写插件plugins,全局引入组件
编写组建components/SvgIcon/icon.vue
<template>
<!-- 如果iconClass是带协议的图标链接 则通过style属性方式渲染-->
<div
v-if="isExt"
class="svg-icon svg-external-icon"
:style="styleExternalIcon"
v-bind="$attrs"
/>
<!-- SVG icon 通过名称使用 -->
<svg v-else :class="svgClass" aria-hidden="true" v-bind="$attrs">
<!--
SVG中的use元素可以调用其他SVG文件的元素,<use xlink:href="#symbolId"></use>
-->
<use :xlink:href="iconName" />
</svg>
</template>
<script>
import { isExternal } from '@/common/utils'
/**
* v-bind="$attrs" 组件$attrs属性透传绑定到元素上
* vue3.0中$lietens已被移除 现在事件监听器是 $attrs 的一部分
* 文档说明:
* https://v3.cn.vuejs.org/guide/migration/listeners-removed.html#_3-x-%E8%AF%AD%E6%B3%95
*/
export default {
name: 'SvgIcon',
inheritAttrs: false, // 组件上的$attrs不自动添加到组件根元素上 默认添加到组件根元素上
props: {
iconClass: {
type: String,
default: ''
},
className: { // 我们也可以自定义类名
type: String,
default: ''
}
},
data () {
return {
isExt: '',
iconName: '',
svgClass: '',
styleExternalIcon: ''
}
},
created () {
const that = this
// 是否是带协议的图片链接
this.isExt = isExternal(this.iconClass || '')
// 拼接成symbolId 在loader配置中指定了symbolId格式 icon-图标名称
this.iconName = `#icon-${that.iconClass}`
// 添加类名 props.className外部传入自定义类名
this.svgClass = that.className ? `svg-icon ${that.className}` : 'svg-icon'
// 如果iconClass是带协议的图标链接 则通过style css属性方式渲染
this.styleExternalIcon = {
mask: `url(${that.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${that.iconClass}) no-repeat 50% 50%`
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover !important;
display: inline-block;
}
</style>
plugins/svgIcon
// plugins目录
import Vue from 'vue'
import SvgIcon from '@/components/svgIcon/icon.vue'
const req = require.context('../icons/svg', false, /\.svg$/)
req.keys().map(req)
Vue.component('svg-icon', SvgIcon)
在nuxt.config.js的配置项plugins中引入
plugins: [
'@/plugins/svgIcon'
],
引入阿里图标
在nuxt中,需要在plugins引入kyyIcon
import Icon from 'ant-design-vue/lib/icon'
import Vue from 'vue'
const IconFont = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1400982_c4hpndz7xd.js'
})
Vue.component('KyyIcon', IconFont)
const CgsIconFont = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_2233276_hvbjqskdw5i.js'
})
Vue.component('CgsIcon', CgsIconFont)
去iconfont图库复制路径
存在问题
图标组件无法自定义长宽高及颜色