[Vue] tsx 动态渲染 element icons

前言

假设,页面 A 下有一个图标,根据路由组件传递的参数来决定图标具体渲染的是 icons 图标集中的一个,可以:

  1. 将用到的图标写在 Vue 模板中;
  2. 获取路由组件传递过来的参数;
  3. 通过 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");
posted @ 2023-02-02 17:01  Himmelbleu  阅读(299)  评论(0编辑  收藏  举报