1. 关于Element-plus的菜单渲染问题
跨过了Element-UI,终于来到了Element-plus。又回到了一个老问题,menu的渲染。
创建一个menu数组,利用v-for
来渲染数组,生成menu,非常常规的操作。但是操作的过程中,出现了一个小问题,就是关于icon
的渲染。
我们知道,在Element-plus中,渲染一个带图标的菜单项,是这么搞的:
<el-menu-item index="/mypath">
<template #title>
<el-icon><Odometer /></el-icon>
<span>title</span>
</template>
</el-menu-item>
icon
图标是直接以一个组件的形式进行渲染的。
那么,当我们企图利用v-for
进行列表渲染的时候,这个图标的组件怎么渲染出来,成了个难题。
直接用双花括号{{}}肯定是不行的,直接会把标签搞成文本。
用v-html
也不行,它只能渲染原生的HTML标签。
WTF?
2. 如何才能动态的把自定义组件渲染出来?
在<template></template>
里面搞模版语法是行不通了。
那就只能尝试走其他的路线了。在搜索引擎愉快的与海量信息搏斗之后,找到了切入点:render
函数。
老实说,其实早就该想到这个了,毕竟组件渲染就这么两条路嘛。奈何对render
的使用频率太低了,选择性的搞忘记了。
那么来尝试吧。
写一个组件,通过props
接收到图标的标签写法,然后渲染出来。
//注意在vue3中,render函数中不再有参数了,h函数需要按需加载。
import { h } from 'vue';
export default{
props: {
//Odometer
html: String
},
render(){
return h('el-icon', null, h(this.html));
}
}
果不其然没有达到效果。常用vue做开发的小伙伴肯定一眼就发现了一个问题:
用h
函数生成虚拟DOM节点时,如果要生成的是组件,则第一个参数直接使用导入的组件即可。如果使用字符串,会原封不动的把字符串当做HTML标签渲染,而不是当作组件渲染。(参考链接)
修改一下:
import { h } from 'vue';
import { ElIcon } from 'element-plus';
export default{
props: {
//Odometer
html: String
},
components: {
ElIcon
},
render(){
return h(ElIcon, null, h(this.html));
}
}
还是不对呀,图标名称是传过来的字符串,没法直接获取到导入的组件呀。
吓得我赶紧又翻了一下文档,在最后一行找到了这么一句话:
如果一个组件是用名字注册的,不能直接导入 (例如,由一个库全局注册),可以使用 resolveComponent() 来解决这个问题。
原来如此。。。
好了,给出最终答案:
<el-menu-item :index="item.path">
<template #title>
<DynamicIcon :html="item.icon"></DynamicIcon>
<span>{{item.title}}</span>
</template>
</el-menu-item>
//DynamicIcon
import { h, resolveComponent } from 'vue';
import { Odometer, ChatDotRound } from '@element-plus/icons-vue';
export default{
props: {
//Odometer
html: String
},
components: {
Odometer,
ChatDotRound
},
render(){
//ElIcon直接全局全局导入了
const IconBox = resolveComponent('ElIcon');
const Icon = resolveComponent(this.html);
return h(IconBox, null, h(Icon));
}
}
3. 总结
最后总结一下子吧。
想要动态渲染组件,就需要利用props
与render
函数。
在使用h
函数的时候,生成组件的虚拟vnode,要直接使用导入的组件。
如果只能获取一个组件名称,那么就用resolveComponent
函数手动解析注册的组件。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。