就是要用这么多图标!!!
你问我用这么多图标项目打包后体积不会很大吗?我的回答是0!
直接进入主题!(注意本文章只围绕iconfont进行icon使用)
封装组件按需加载方案地址:iconfont在vue3中按需使用和编译打包示例
资源分离加载方案地址:资源分离加载iconfont图标
icon需求
可配置、可复用、按需加载、平台兼容(pc、小程序、安卓、ios通用一套icon)
历史方案
class名称
导出一堆文件,引入后直接使用class名进行使用,刚开始接触使用
svg的id
结合插件生成svg文件导入,使用use到id的方式使用
封装组件按需加载
使用innerHtml对svg组装后生成效果,见iconfont一键下载所有图标、在vue3中的按需使用和多色配置
资源分离加载
将svg图标转json文件后远程加载使用,达到项目体积0入侵,高度可配置:单色、多色、大小,远程动态更新新图标,达到2500万图标随时替换!!!
如何选择适合的方案?
项目固定图标、无需配置
这种情况会使用使用按需加载,在开发的时候就确定了所有内容,直接将icon打包编译进项目中,加载就进行使用
需要后台配置,动态更换
使用资源分离加载,这样出现新图标只需要将静态资源更新,后台配置完后前端就能直接显示,前端0改动,直接读取配置即可,不需要关心图标的变动信息
实现效果
封装组件按需加载
Vue3版本:iconfont在vue3中按需使用和编译打包示例
动态更换
Vue3版本:资源分离加载iconfont图标
资源分离加载实现流程
先将iconfont图标转换为本地资源文件,配置加载代码的远程地址
后台配置效果:
加载icon代码 :远程加载对应图标进行显示,使用innerHTML显示内容
<template>
<div ref="iconRef" style="display: inline-flex" :style="{ fontSize: `${size}px`, color: color as any }"></div>
</template>
<script lang="ts" setup>
import { FileUtil } from 'tools-vue3'
import { onMounted, ref, watch } from 'vue'
import SText from '~/store/SText'
const props = defineProps({
name: { default: 'Ant' },
type: {
default: 'up'
},
size: { default: '20' },
color: { default: undefined as any }
})
watch(
() => [props.type, props.color],
() => {
init()
}
)
onMounted(() => {
init()
})
const iconRef = ref<HTMLDivElement>({} as any)
const viewBoxStr = '#{viewBox}'
const viewBoxDefault = '0 0 1024 1024'
const str = {
svg: [
`<svg style="width: 1em;height: 1em;vertical-align: middle;overflow: hidden;" viewBox="${viewBoxStr}" version="1.1" xmlns="http://www.w3.org/2000/svg">`,
'</svg>'
],
get0: (replacestr: string) => {
return str.svg[0].replace(viewBoxStr, replacestr)
},
getPath: (param: { d: string; fill?: string }) => {
return `<path d="${param.d}" fill="${param.fill ?? 'currentColor'}" />`
}
}
if (!window.sicon) {
window.sicon = {
resource: {},
load: {},
getResorce: async (_url: string) => {
let filestr: any
const req = window.sicon.load[_url]
if (req) {
filestr = await req
} else {
window.sicon.load[_url] = new Promise(async (res) => {
const _json = await FileUtil.getFile(_url)
res(_json)
})
filestr = await window.sicon.load[_url]
}
return filestr
}
}
}
const init = async () => {
const color = ['color:black;', 'color:red;']
if (StrUtil.isNull(props.type)) return
const _url = `${SText.icon.url}/${props.name}/${props.type}.json`
let filestr: any
const _icon_resource = window.sicon.resource[_url]
if (_icon_resource) {
filestr = _icon_resource
} else {
filestr = await window.sicon.getResorce(_url)
if (typeof filestr === 'object') {
filestr = JSON.stringify(filestr)
}
window.sicon.resource[_url] = filestr
}
if (!filestr) {
console.info(
`%c icon <%c${props.name}-${props.type}%c> 不存在`,
`${color[0]} font-size:12px`,
color[1],
color[0]
)
return
}
let res = ''
if (!filestr.startsWith('{')) {
res = `${str.get0(viewBoxDefault)}<path d="${filestr}" fill="currentColor" />${str.svg[1]}`
} else {
let objt: { d: string[]; fill: { [key: string]: number[] }; viewBox?: string } = JSON.parse(filestr)
res = str.get0(objt.viewBox ?? viewBoxDefault)
const getFill = (fill: { [key: string]: number[] }, index: number) => {
const fillkeys = Object.keys(fill)
const colors = props.color
for (let i = 0; i < fillkeys.length; i++) {
const key = fillkeys[i]
const nums = fill[key]
if (nums.includes(index)) {
if (colors) {
if (Array.isArray(colors)) return colors[i] ?? colors[colors.length - 1]
return colors
} else {
return key
}
}
}
return 'currentColor'
}
objt.d.forEach((item, index) => {
res += str.getPath({
d: item,
fill: getFill(objt.fill, index)
})
})
res += str.svg[1]
}
iconRef.value.innerHTML = res
}
</script>
使用代码
<sicon name="Ant" type="up" color="#000000" />
<sicon name="Ant" type="up" :color="['#000000','#000000']" />
结语
因为iconfont的图标很多,也在不停更新好的图标,所以一直在使用iconfont做项目,不断的解决自己的“懒”的需求,用最快的时间做更多的需求~ 有问题欢迎提出讨论~ 总会解决的~