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

本文讲述了在Vue开发中,如何在*.vue文件中使用JSX/TSX渲染函数处理复杂业务场景,如根据数据动态渲染不同组件,同时保持模版语法的便利性。通过导入@vitejs/plugin-vue-jsx并设置lang属性,开发者可以在*.vue文件中无缝结合两者的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在日常开发中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也会给出智能提示。

cbdd0fecfa7ad78d52c07b310623bd34.png

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模版语法中内置的指令等功能。

我的源代码如下:ERROR in ./src/App.vue?vue&type=template&id=7ba5bd90&ts=true (./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[3]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/App.vue?vue&type=template&id=7ba5bd90&ts=true) 5:27 Module parse failed: Unexpected token (5:27) File was processed with these loaders: * ./node_modules/vue-loader/dist/templateLoader.js * ./node_modules/vue-loader/dist/index.js You may need an additional loader to handle the result of these loaders. | const _hoisted_1 = { class: "demo-collapse" } | > export function render(_ctx: any,_cache: any,$props: any,$setup: any,$data: any,$options: any) { | const _component_el_collapse_item = _resolveComponent("el-collapse-item")! | const _component_el_collapse = _resolveComponent("el-collapse")! @ ./src/App.vue?vue&type=template&id=7ba5bd90&ts=true 1:0-196 1:0-196 @ ./src/App.vue 1:0-72 6:68-74 16:71-18:3 17:29-35 16:2-18:4 @ ./src/main.js 4:0-28 5:22-25 ERROR in [eslint] C:\Users\tkx\Desktop\123\234\src\App.vue 54:12 error Parsing error: Unexpected token, expected "from" (3:12) ✖ 1 problem (1 error, 0 warnings) webpack compiled with 3 errors 出现错误: error in ./src/App.vue?vue&type=template&id=7ba5bd90&ts=true Module parse failed: Unexpected token (5:27) File was processed with these loaders: * ./node_modules/vue-loader/dist/templateLoader.js * ./node_modules/vue-loader/dist/index.js You may need an additional loader to handle the result of these loaders. | const _hoisted_1 = { class: "demo-collapse" } | > export function render(_ctx: any,_cache: any,$props: any,$setup: any,$data: any,$options: any) { | const _component_el_collapse_item = _resolveComponent("el-collapse-item")! | const _component_el_collapse = _resolveComponent("el-collapse")! ERROR in ./src/App.vue?vue&type=script&lang=ts&setup=true (./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/App.vue?vue&type=script&lang=ts&setup=true) 3:12 Module parse failed: Unexpected token (3:12) File was processed with these loaders: * ./node_modules/vue-loader/dist/index.js You may need an additional loader to handle the result of these loaders. | import { defineComponent as _defineComponent } from 'vue' | import { ref } from 'vue' > import type { CollapseModelValue } from 'element-plus' | | @ ./src/App.vue?vue&type=script&lang=ts&setup=true 1:0-133 1:0-133 1:134-256 1:134-256 @ ./src/App.vue 2:0-65 3:0-60 3:0-60 6:49-55 @ ./src/main.js 4:0-28 5:22-25 ERROR in ./src/App.vue?vue&type=template&id=7ba5bd90&ts=true (./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[3]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/App.vue?vue&type=template&id=7ba5bd90&ts=true) 5:27 Module parse failed: Unexpected token (5:27) File was processed with these loaders: * ./node_modules/vue-loader/dist/templateLoader.js * ./node_modules/vue-loader/dist/index.js You may need an additional loader to handle the result of these loaders. | const _hoisted_1 = { class: "demo-collapse" } | > export function render(_ctx: any,_cache: any,$props: any,$setup: any,$data: any,$options: any) { | const _component_el_collapse_item = _resolveComponent("el-collapse-item")! | const _component_el_collapse = _resolveComponent("el-collapse")! @ ./src/App.vue?vue&type=template&id=7ba5bd90&ts=true 1:0-196 1:0-196 @ ./src/App.vue 1:0-72 6:68-74 16:71-18:3 17:29-35 16:2-18:4 @ ./src/main.js 4:0-28 5:22-25 ERROR in [eslint] C:\Users\tkx\Desktop\123\234\src\App.vue 54:12 error Parsing error: Unexpected token, expected "from" (3:12)
03-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值