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

相关推荐
vvilkim1 小时前
全面解析React内存泄漏:原因、解决方案与最佳实践
前端·javascript·react.js
vvilkim1 小时前
React批处理(Batching)更新机制深度解析
前端·javascript·react.js
Bayi·1 小时前
前端面试场景题
开发语言·前端·javascript
程序猿熊跃晖2 小时前
Vue中如何优雅地处理 `<el-dialog>` 的关闭事件
前端·javascript·vue.js
进取星辰2 小时前
12、高阶组件:魔法增幅器——React 19 HOC模式
前端·javascript·react.js
拉不动的猪2 小时前
前端低代码开发
前端·javascript·面试
程序员张32 小时前
Vue3集成sass
前端·css·sass
夜跑者2 小时前
axios 在请求拦截器中设置Content-Type无效问题
前端
知识分享小能手2 小时前
JavaScript学习教程,从入门到精通,Ajax与Node.js Web服务器开发全面指南(24)
开发语言·前端·javascript·学习·ajax·node.js·html5
烛阴2 小时前
Swizzling--OpenGL的向量的灵活组合
前端·webgl