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函數手動解析註冊的組件。