Vue3 定义组件的 4 种方式,你真的选对了吗?

大家好,我是 前端架构师 - 大卫

初心为助前端人🚀,进阶路上共星辰✨,

您的点赞👍与关注❤️,是我笔耕不辍的灯💡。

更多优质内容,欢迎关注我的公众号 @前端大卫,一起探索前端技术的无限可能!

背景

Vue 作为一款流行的前端框架,提供了多种方式来定义组件,包括单文件组件 (SFC)、渲染函数 (Render Functions)、JSX/TSX 以及函数式组件 (Functional Components)。不同的方式适用于不同的场景,开发者在选择时需要考虑可读性、性能和灵活性等因素。本文将对这四种方式进行详细对比,帮助你找到最适合自己项目的方案。

1. SFC (Single-File Component)

单文件组件,以 *.vue 作为文件扩展名,是 Vue 官方推荐的方式。

特点:

  • 模板和逻辑分离,结构清晰,官方推荐。
  • 支持 Vue 内置功能,如 script setup、CSS 作用域、单文件组件热更新等。
  • 适合大多数 Vue 项目,代码组织更直观。

Test.vue 代码如下:

vue 复制代码
<script setup lang="ts">
import { ref } from "vue";
defineProps<{
  text: string;
}>();

const num = ref(0);
</script>
<template>
  <div class="aaa">
    {{ text }}
    <div @click="num++">{{ num }}</div>
  </div>
</template>

优点:

  • 代码结构清晰,符合 MVVM 模式,模板部分易读。
  • script setup 提供更简洁的语法,减少模板和逻辑之间的代码切换。
  • 具有良好的工具链支持 (Vue 官方生态、Vite、Vue Loader 等)。

缺点:

  • 需要额外的构建工具 (如 Vite 或 Webpack) 进行编译,不能直接在浏览器运行。
  • 在某些场景下 (如动态创建组件) 可能不如渲染函数灵活。

2. 渲染函数 (Render Functions)

Vue 提供了一个 h() 函数用于创建虚拟节点 vnodes

特点:

  • 需要引入 hdefineComponent 函数,没有模板语法。
  • 适合动态组件或 UI 库开发。

h 是一个 helper 函数,用于创建虚拟 DOM(VNode)。它是 createElement 的别名,类似于 React 里的 React.createElement

Test.ts 代码如下:

ts 复制代码
import { defineComponent, h, ref } from "vue";

export default defineComponent({
  props: {
    text: {
      type: String,
      required: true
    }
  },
  setup(props) {
    const num = ref(0);
    return () =>
      h("div", { class: "aaa" }, [props.text, h("div", { onClick: () => num.value++ }, num.value)]);
  }
});

优点:

  • 代码更灵活,适用于需要动态控制 VNode 结构的场景,如表单渲染器、可拖拽组件等。
  • 体积更小,不需要 SFC 解析器。

缺点:

  • 代码可读性较低,没有模板语法,编写复杂组件时维护成本较高。
  • 开发体验不如 SFC 友好,特别是对于不熟悉 JSX/TSX 的开发者。

3. JSX / TSX

JSX 和 TSX 是 React 的语法扩展,Vue 也支持这种语法。

特点:

  • 语法类似 React,允许在 Vue 组件中使用 JSX/TSX 语法。
  • 适用于更灵活的逻辑处理,且无需引入 h() 函数。

tsconfig.json 需要配置:

json 复制代码
{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "vue"
    // ...
  }
}

vite.config.ts 需要配置 vueJsx 插件:

ts 复制代码
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";

export default defineConfig({
  plugins: [vue(), vueJsx()]
  // ...
});

Test.tsx 代码如下:

ts 复制代码
import { defineComponent, ref } from "vue";

export default defineComponent({
  props: {
    text: {
      type: String,
      required: true
    }
  },
  setup(props) {
    const num = ref(0);
    return () => (
      <div class="aaa">
        {props.text}
        <div onClick={() => num.value++}>{num.value}</div>
      </div>
    );
  }
});

优点:

  • 代码灵活,适用于复杂 UI 组件开发。
  • 在 TypeScript 项目中拥有更好的类型推导支持。

缺点:

  • 需要额外的 @vitejs/plugin-vue-jsx 插件支持,并在 tsconfig.json 配置 JSX 选项。
  • 代码风格不符合 Vue 传统的模板语法,可能不适合所有团队。

4. 函数式组件 (Functional Components) --- 不推荐

特点:

  • 组件本质上是一个纯函数,ref 只能定义在组件外部,属于全局共享状态。
  • 适用于只依赖 props 进行渲染,且无状态 (stateless) 的组件。

Test.tsx 代码如下:

ts 复制代码
import { ref, type FunctionalComponent } from "vue";

interface Props {
  text: string;
}

const num = ref(0);
export const TestFunctionalCom: FunctionalComponent<Props> = (props) => {
  return (
    <div class="aaa">
      {props.text}
      <div onClick={() => num.value++}>{num.value}</div>
    </div>
  );
};

优点:

  • 代码简单,适用于简单的展示组件 (如按钮、图标等)。
  • 没有响应式数据追踪开销,性能更高。

缺点:

  • 不能在组件内部使用 refreactive,状态必须是全局变量或 props 传入。
  • 全局 ref 可能导致多个组件实例共享状态,引发意外的状态同步问题。

总结

方式 适用场景 优点 缺点
SFC (单文件组件) 适用于大多数 Vue 项目 结构清晰、官方推荐、支持 script setup 需要构建工具
渲染函数 (Render Functions) 适用于动态组件/UI 库 代码更灵活,适用于动态 VNode 结构 可读性较低,维护成本高
JSX / TSX 适用于复杂逻辑组件 代码灵活,可与 TypeScript 结合 需要额外配置,不符合 Vue 传统语法
函数式组件 适用于无状态小组件 代码简单、性能较高 不能使用 ref,全局状态共享有风险

在实际开发中,SFC 是最推荐的方式 ,大多数 Vue 组件都可以用 SFC 实现。对于动态 VNode 结构,可以考虑 渲染函数JSX/TSX函数式组件 在 Vue 3 中的使用场景很少,通常不推荐使用。

如果本文对你有帮助,欢迎点赞❤️收藏⭐,也欢迎在评论区交流!

更多详细内容可参考: Vue 官方文档

相关推荐
码客前端2 分钟前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛2 分钟前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
工藤学编程15 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保15 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫16 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
内存不泄露22 分钟前
基于Spring Boot和Vue 3的智能心理健康咨询平台设计与实现
vue.js·spring boot·后端
欧阳天风24 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder27 分钟前
箭头函数和 this 绑定
linux·前端·javascript·typescript
郑州光合科技余经理28 分钟前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活
沐墨染30 分钟前
大型数据分析组件前端实践:多维度检索与实时交互设计
前端·elementui·数据挖掘·数据分析·vue·交互