配置 TSX

cnpm i @vitejs/plugin-vue-jsx -D
// 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 组件

element-plus 图标集有很多,但有时候需要动态地加载某个图标,如果把所有可能用到的图标都列举出来,通过 v-if 在组件中决定到底渲染哪一个,这样不够灵活。

只要 .vue 单文件组件中做不到的,可以尝试用 TSX 和渲染函数来达到目的。比如,我想做一个底部有文字的图标,其实可以通过插槽把图标插入到组件中。

<div class="header f-c-s">
  <TextIcon icon="menu" :icon-size="2" :text-size="1" />
</div>

也可以通过 TSX 来做,如上 TextIcon 组件,我只需要传递 props icon 是 menu 的字符串,就可以渲染出一个图标来:

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>
    );
  }
});

把图标的组件名称,通过 props 传递给 TextIcon 组件,通过 resolveComponent 解析组件,h 函数渲染该组件。

注意事项

在 TSX 中使用 Element-Plus 组件要全部导入,不能按需自动导入,否则 resolveComponent 不能解析组件:

// main.ts

// import vue
import { createApp } from "vue";
import App from "./App.vue";

// import element-plus
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";

const app = createApp(App);

// all element icons
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component);
}

app.use(ElementPlus);
app.mount("#app");

总结

.vue 组件中,模板不能通过一个字符串解析组件,我们可以通过 TSX 来做,利用 resolveComponent 函数接收一个组件的名称(字符串)进行渲染,且该组件是已经全局注册过的。总而言之,TSX 非常灵活,在万不得已的情况下才用,一般 .vue 模板组件已经够用了。