[Vue] tsx 动态渲染 element icons
前言
假设,页面 A 下有一个图标,根据路由组件传递的参数来决定图标具体渲染的是 icons 图标集中的一个,可以:
- 将用到的图标写在 Vue 模板中;
- 获取路由组件传递过来的参数;
- 通过 v-if 判断路由组件传递的参数来决定渲染哪一个图标;
问题:随着业务增加,模板的 v-if 也增加,写起来很麻烦,代码也不简洁。
思考:Vue 模板不能满足本业务和使用场景,需要写 tsx/jsx 组件。
理想的组件:给组件传递一个 prop,接收一个字符串,通过字符串即可决定渲染什么图标。
file:[DemoIndex.vue]
<div class="header f-c-s">
<TextIcon icon="menu" :icon-size="2" :text-size="1" />
</div>
配置 tsx/jsx
下载插件:
file:[cmd]
cnpm i @vitejs/plugin-vue-jsx -D
配置 vite.config.ts:
file:[vite.config.ts]
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({
plugins: [
vue(),
vueJsx()
]
});
tsx/jsx 组件
这与 React 中的 tsx/jsx 差不多,Vue 的 tsx/jsx 比 Vue 模板更灵活,适合很多灵活的场景,但尽可能使用 Vue 模板来写组件。除非像本文章遇到的问题抛弃 Vue 模板而改用 Vue tsx/jsx 写组件。
file:[Icon.tsx]
import { defineComponent, resolveComponent, h } from "vue";
function isText(props: any) {
if (props.text) {
return (
<div class="text mt-1" style={{ fontSize: props.textSize + "rem" }}>
{props.text}
</div>
);
}
}
export default defineComponent({
props: {
icon: {
type: String,
required: true
},
text: {
type: String,
default: ""
},
iconSize: {
type: Number,
default: 1
},
textSize: {
type: Number,
default: 1
}
},
setup(props, ctx) {
// resolveComponent 接收字符串,解析对应的 element-plus 图标组件
const elIcon = resolveComponent(props.icon);
// h 函数渲染 elIcon 组件
return () => (
<div class="i-icon text-center">
<div class="f-c-c">
<el-icon style={{ fontSize: props.iconSize + "rem" }}>{h(elIcon)}</el-icon>
</div>
{isText(props)}
</div>
);
}
});
通过 resolveComponent
解析 element-plus 的图标组件,该函数可以通过组件的名称来解析组件。前提是该组件已经存在于整个 Vue 全局中,能够被识别到的组件。
根据上面的函数解析出来的组件,传递给 h
函数,它将该组件渲染到模板中。
注意事项
要把所有的 element-plus 图标集组件全部导入到全局组件中,不能按需自动导入,否则 resolveComponent
不能解析组件:
file:[main.ts]
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
// all element icons
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.mount("#app");