直接在*.vue文件(SFC)中使用JSX/TSX渲染函數,真香!

前言

在日常開發中vue的模版語法在大多數情況都能夠滿足我們的需求,但是在一些複雜的業務場景中使用模版語法就有些麻煩了。這個時候靈活的JSX/TSX渲染函數就能派上用場了,大多數同學的做法都是將*.vue文件改爲*.tsx或者*.jsx文件。其實我們可以直接在*.vue文件中直接使用JSX/TSX渲染函數。

什麼場景需要使用JSX/TSX渲染函數

假設我們現在有這樣的業務場景,在我們的頁面中有個list數組。我們需要去遍歷這個數組,根據每一項的item去渲染不同的組件。如果tem的數據滿足條件A,那麼就渲染組件A。如果item的數據滿足條件B,那麼就渲染組件B。如果item的數據滿足條件C,那麼就渲染組件C。

如果我們使用vue模版語法去實現這個需求,我們的Page.vue文件的代碼就需要是這樣的:

<template>
  <template v-for="item in list">
    <ComponentA v-if="isComponentA(item)" />
    <ComponentB v-else-if="isComponentB(item)" />
    <ComponentC v-else-if="isComponentC(item)" />
  </template>
</template>

<script setup lang="ts">
import ComponentA from "./component-a.vue";
import ComponentB from "./component-b.vue";
import ComponentC from "./component-c.vue";

const list: Array<number> = [1, 5, 3, 2, 1];

const isComponentA = (item): boolean => {
  return item % 3 === 0;
};

const isComponentB = (item): boolean => {
  return item % 3 === 1;
};

const isComponentC = (item): boolean => {
  return item % 3 === 2;
};
</script>

這樣雖然可以實現功能,但是明顯不夠優雅,領導code review時看了直呼搖頭。

在*.jsx/tsx文件中使用JSX/TSX渲染函數

此時機智的小夥伴會說,我們可以使用vuesetup方法使用JSX/TSX渲染函數實現。確實可以,我們來看看具體實現的代碼:

import { defineComponent } from "vue";
import ComponentA from "./component-a.vue";
import ComponentB from "./component-b.vue";
import ComponentC from "./component-c.vue";

export default defineComponent({
  setup() {
    const list = [1, 5, 3, 2, 1, 0];

    function renderDataList(data: Array<number>) {
      return data?.map((val) => {
        if (val % 3 === 0) {
          return <ComponentA />;
        } else if (val % 3 === 1) {
          return <ComponentB />;
        } else {
          return <ComponentC />;
        }
      });
    }

    return () => {
      return <div>{renderDataList(list)}</div>;
    };
  },
});

首先我們需要將原來的Page.vue文件改爲Page.tsx文件,然後我們需要將原來寫在template中的代碼摞到setup中。這種寫法有如下幾個痛點:
由於沒有使用vue的模版語法,所以vue內置的v-model等指令和項目中自己封裝的指令等都不能使用了,只能使用js去自己實現。

按照常規的思維,setup直接返回一個值就行了,但是如果你這樣寫就會收到這樣的報錯:

[Vue warn]: setup() should not return VNodes directly - return a render function instead.

原因是setup() 函數在每個組件中只會被調用一次,而返回的渲染函數將會被調用多次。這樣就導致我們的代碼只能在外面包裹一層匿名函數:

return () => {
  return <div>{renderDataList(list)}</div>;
};

在*.vue文件中使用JSX/TSX渲染函數

那麼有沒有方法可以讓我們在使用JSX/TSX渲染函數的同時,也可以在vue文件中使用模版語法呢?答案是:當然可以!

首先我們需要導入@vitejs/plugin-vue-jsx

// vite.config.js
import vue from '@vitejs/plugin-vue'

export default {
  plugins: [vue()],
}

然後我們需要將vue文件的script標籤的lang設置爲tsx或者jsx。具體的Page.vue代碼如下:

<template>
  <RenderDataList :data="list" />
</template>

<script setup lang="tsx">
import ComponentA from "./component-a.vue";
import ComponentB from "./component-b.vue";
import ComponentC from "./component-c.vue";

const list = [1, 5, 3, 2, 1];

const RenderDataList = (props: { data: Array<number> }) => {
  return props.data?.map((val) => {
    if (val % 3 === 0) {
      return <ComponentA />;
    } else if (val % 3 === 1) {
      return <ComponentB />;
    } else {
      return <ComponentC />;
    }
  });
};
</script>

在上面這個例子中我們定義了一個RenderDataList,然後在template中可以直接將RenderDataList當作一個組件使用。vscode也會給出智能提示。

react中,這種場景我們可以將RenderDataList當作一個函數去使用,然後在模版中直接調用這個函數就行了。但是在vue中,RenderDataList只能當做一個組件使用,不能當做函數調用。

還有一點需要避坑的是,假如我們的props中定義了一個駝峯命名法的變量,例如:pageNum。在template中傳入pageNum的時候必須寫成:pageNum="xxx",不能寫成:page-num="xxx"

總結

這篇文件介紹瞭如何在*.vue文件中直接使用JSX/TSX渲染函數,只需要導入@vitejs/plugin-vue-jsx,然後將script標籤的lang設置爲tsx或者jsx。就可以在script中直接定義組件,然後在template中直接使用組件就可以了。這樣我們既可以使用JSX/TSX渲染函數的靈活性,也可以使用vue模版語法中內置的指令等功能。

如果我的文章對你有點幫助,歡迎關注公衆號:【歐陽碼農】,文章在公衆號首發。你的支持就是我創作的最大動力,感謝感謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章