大家好,我是 前端架构师 - 大卫。
初心为助前端人🚀,进阶路上共星辰✨,
您的点赞👍与关注❤️,是我笔耕不辍的灯💡。
更多优质内容,欢迎关注我的公众号 @前端大卫,一起探索前端技术的无限可能!
背景
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
。
特点:
- 需要引入
h
和defineComponent
函数,没有模板语法。 - 适合动态组件或 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>
);
};
优点:
- 代码简单,适用于简单的展示组件 (如按钮、图标等)。
- 没有响应式数据追踪开销,性能更高。
缺点:
- 不能在组件内部使用
ref
或reactive
,状态必须是全局变量或props
传入。 - 全局
ref
可能导致多个组件实例共享状态,引发意外的状态同步问题。
总结
方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
SFC (单文件组件) | 适用于大多数 Vue 项目 | 结构清晰、官方推荐、支持 script setup |
需要构建工具 |
渲染函数 (Render Functions) | 适用于动态组件/UI 库 | 代码更灵活,适用于动态 VNode 结构 | 可读性较低,维护成本高 |
JSX / TSX | 适用于复杂逻辑组件 | 代码灵活,可与 TypeScript 结合 | 需要额外配置,不符合 Vue 传统语法 |
函数式组件 | 适用于无状态小组件 | 代码简单、性能较高 | 不能使用 ref ,全局状态共享有风险 |
在实际开发中,SFC 是最推荐的方式 ,大多数 Vue 组件都可以用 SFC 实现。对于动态 VNode 结构,可以考虑 渲染函数 或 JSX/TSX 。函数式组件 在 Vue 3 中的使用场景很少,通常不推荐使用。
如果本文对你有帮助,欢迎点赞❤️收藏⭐,也欢迎在评论区交流!
更多详细内容可参考: Vue 官方文档