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 官方文档

相关推荐
萌萌哒草头将军17 小时前
🚀🚀🚀 Oxc 恶意扩展警告;Rolldown 放弃 CJS 支持;Vite 发布两个漏洞补丁版本;Rslib v0.13 支持 ts-go
前端·javascript·vue.js
接着奏乐接着舞。17 小时前
3D地球可视化教程 - 第1篇:基础地球渲染系统
前端·javascript·vue.js·3d·three.js
龙傲天66617 小时前
Scala的面向对象和函数式编程特性 Idea环境搭建和输入输出
前端
蓝色海岛17 小时前
element-ui表格嵌套表格,鼠标移入时样式错乱-问题调研及处理办法
前端
薄雾晚晴17 小时前
Rspack 实战:用 SWC Loader 搞定 JS 兼容(支持 IE 11 + 现代浏览器,兼顾构建速度)
前端·vue.js
恋猫de小郭17 小时前
Flutter 官方 LLM 动态 UI 库 flutter_genui 发布,让 App UI 自己生成 UI
android·前端·flutter
薄雾晚晴17 小时前
Rspack 实战:用 image-minimizer-webpack-plugin 做图片压缩,优化打包体积
javascript·vue.js
niuhuahua17 小时前
大模型流式聊天,实时对话,快捷问题选项
vue.js
JIngJaneIL17 小时前
汽车租赁|基于Java+vue的汽车租赁系统(源码+数据库+文档)
java·vue.js·spring boot·汽车·论文·毕设·汽车租赁系统
kymjs张涛18 小时前
零一开源|前沿技术周刊 #15
前端·javascript·面试