引言
这是element组件库系列文章,主要是平时使用element组件库的一些过程和心得,文章篇幅不定,有长有短,但主打一个实用(可能实用不一定是对你实用,不实用请轻喷)。在element-plus中,引入官方自带的图标从原有的class引入方式变成了导入组件的方式。
<script>
import {Document} from "@element-plus/icons-vue"
</script>
<template>
<el-icon :size="20" color="#000000">
<Document/>
</el-icon>
</template>
通过按需导入的方式,需要时导出即可。
然而,这会造成一些问题:在一些高度封装的场景,典型的就是中后台的路由菜单中,我们希望通过配置自动生成菜单,并在每一项菜单中显示图标。
就像这样:
于是,我们需要在路由配置中的meta属性加上一个icon字段,用来告诉渲染菜单时要一起渲染icon,但这时,icon的值明显是一个字符串,怎么办呢?别着急,笔者就向大家介绍一下如何实现。
版本说明和开发环境
前置知识
阅读本文你需要一些有关渲染函数的基础,如不是很了解,请先移步这里
各依赖版本
| packages | version |
|---|---|
| element-plus | 2.2.15 |
| vue3 | 3.4.3 |
开发环境
| 环境 | 说明(版本) |
|---|---|
| 操作系统 | macOS |
| Node | 16.14.2 |
正文开始
分析过程
首先明确我们的需求,给定一个icon,这其实就是告诉要渲染的icon名字;但我们的icon又需要导入,导入的是一个组件。在这,突破口就是我们需要一个name和组件的映射关系,如果知道js中的Map对象,就可以有:
const iconsMap = new Map<string,Component>()
iconsMap为存放这个映射关系的映射集合,我们传入icon的name,就可以取出想要渲染的组件。而把这个组件渲染出来,由于其是我们所导入的一个组件值,我们可以利用h函数将其渲染为VNode。
于是可以进一步得到:
<el-icon>{h(iconsMap.get(iconName))}</el-icon>
到这里,这个组件最终的模样已经非常清晰了,el-icon有两个参数 size和color,我直接将其作为组件的props,给这个组件起名ZIcon。那么代码为:
<script lang="tsx">
import * as icons from "@element-plus/icons-vue"
import {Component,h,defineComponent,toRefs} from "vue";
const iconsMap =new Map<string,Component>()
for (let icon in icons){
//驼峰变量变小写并加上el-, ArrowLeft ===> el-arrow-left
const iconName ='el-'+icon.split(/(?=[A-Z])/)
.map(str=>str.toLowerCase()).join('-');
//@ts-ignore
iconsMap.set(iconName,icons[icon])
}
export default defineComponent({
props:{
iconName:{
type:String,
required:true
},
iconSize:{
type:Number,
default:20
},
iconColor:{
type:String,
default:'#000000'
}
},
setup(props){
//只是为了加入 @ts-ignore,不然ts在报错
function getIcon(){
//@ts-ignore
return h(iconsMap.get(props.iconName))
}
return {
getIcon,
...toRefs(props)
}
},
render(){
return <el-icon size={this.iconSize} color={this.iconColor}>{this.getIcon()}</el-icon>
}
})
</script>
可以看到,ZIcon有三个参数,icon的名字、大小和颜色。icon的名字通过形如el-arrow-left传入,这样项目中以后引入其他图标库时也方便开发中一眼看懂这是element-plus自带的图标,更为语义化。
以上就是如何优雅使用element-plus自带图标库的方式了,希望能帮到你,让我们下篇再见👋!
2023-11-21更新
为了方便还在使用js的小伙伴,增加js版本的写法,需要的小伙伴粘贴这个就行了。
<script setup>
import * as icons from "@element-plus/icons-vue";
import { h } from "vue";
/**
* @type {Map<string,import('vue').Component>}
*/
const iconsMap = new Map();
for (let icon in icons) {
//驼峰变量变小写并加上el-, ArrowLeft ===> el-arrow-left
const iconName =
"el-" +
icon
.split(/(?=[A-Z])/)
.map((str) => str.toLowerCase())
.join("-");
//@ts-ignore
iconsMap.set(iconName, icons[icon]);
}
const props = defineProps({
iconName: {
type: String,
required: true
},
iconSize: {
type: Number,
default: 20
},
iconColor: {
type: String,
default: "#000000"
}
});
//具体渲染的组件
function EIcon() {
return h(iconsMap.get(props.iconName));
}
</script>
<template>
<el-icon :size="props.iconSize" :color="props.iconColor">
<EIcon />
</el-icon>
</template>