Jym好😘,我是珑墨,今天给大家分享 不同姿势写vue3组件 ,嘎嘎的😍,看下面。
vue3组件虽然多种骚写法。可以都尝尝鲜,但是咱不能在项目中各种滥用,还是得保持项目同类型写法,除非特殊操作

📌 1. <script setup>
+ TSX(推荐)
这是目前最主流的写法,适合现代 Vue 3 + Vite + TypeScript 项目,简洁高效,一顿cv操作。
xml
vue
<script setup lang="tsx">
import { defineProps } from 'vue'
const props = defineProps({
message: String,
imgSrc: String
})
</script>
<template>
<div class="empty-box">
<img :src="imgSrc" />
<p>{{ message }}</p>
<slot />
</div>
</template>
🔍 特点
- 使用
<script setup>
简化逻辑; - 支持 JSX 模板;
- 类型推导友好;
- 开发体验更佳。
📌 2. defineComponent
+ setup() 函数(组合式 API)
适用于需要类型支持或使用 TSX 的项目,现在基本上都用ts了吧。
javascript
ts
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EmptyBox',
props: {
message: String,
imgSrc: String
},
setup(props) {
return () => (
<div class="empty-box">
<img src={props.imgSrc} />
<p>{props.message}</p>
</div>
)
}
})
🔍 特点
- 显式调用 setup();
- 可返回 JSX;
- 支持 TypeScript 类型;
- 更灵活地控制渲染逻辑。
📌 3. 选项式 API(Options API)
适合从 Vue 2 迁移的项目,结构清晰,但不如组合式 API 灵活。
php
ts
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EmptyBox',
props: ['message', 'imgSrc'],
data() {
return {
defaultImg: 'default.png'
}
},
template: `
<div class="empty-box">
<img :src="imgSrc || defaultImg" />
<p>{{ message }}</p>
</div>
`
})
🔍 特点
- 使用
data()
、methods
、computed
等选项; - 模板使用字符串或外部模板文件;
- 不适合复杂逻辑封装。
📌 4. 函数组件(Functional Component)
适用于无状态组件,性能更好,但不能使用生命周期钩子。
javascript
ts
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EmptyBox',
props: ['message', 'imgSrc'],
setup(props) {
return () => (
<div class="empty-box">
<img src={props.imgSrc} />
<p>{props.message}</p>
</div>
)
}
})
🔍 特点
- 没有
this
上下文; - 没有响应式数据;
- 更轻量,适合纯展示组件。
📌 5. 类组件(Class Component)
适合熟悉面向对象编程风格的开发者,有点像react操作,但官方已不推荐。 原因是Vue 的设计哲学是越来越倾向于 组合式 API(Composition API) 和 函数式编程风格 ,而类组件属于面向对象的写法,与 Vue 3 的发展方向不一致。再说,太依赖额外插件和装饰器语法((如 @Component
、@Prop()
)),不便于和其他类型组件融合。
scala
ts
import { defineComponent, Component as VueComponent } from 'vue-class-component'
@VueComponent
export default class EmptyBox extends VueComponent<{
message?: string
imgSrc?: string
}> {
render() {
return (
<div class="empty-box">
<img src={this.imgSrc} />
<p>{this.message}</p>
</div>
)
}
}
🔍 特点
- 使用装饰器语法;
- 类似 React 类组件;
- 不推荐用于新项目。
📌 6. 使用 .vue
单文件组件(SFC)+ JSX
在单文件组件中使用 JSX,可以结合 <template>
和 <script setup>
,这是目前最常见的。
xml
vue
<script setup lang="tsx">
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div>
<p>当前计数:{count}</p>
<button onClick={() => count++}>增加</button>
</div>
</template>
🔍 特点
- 在
<template>
中使用 JSX; - 结合响应式变量;
- 适合动态内容较多的场景。
📌 7. 异步组件(Async Component)
适用于懒加载组件,提升首屏性能。
javascript
ts
import { defineAsyncComponent } from 'vue'
export default defineComponent({
components: {
AsyncEmptyBox: defineAsyncComponent(() => import('./EmptyBox.vue'))
},
template: `
<div>
<AsyncEmptyBox v-if="show" />
</div>
`
})
🔍 特点
- 动态导入组件;
- 首屏不加载;
- 支持 loading/suspense 组件。
📌 8. 插件式组件(通过 app.component()
注册全局组件)
适用于全局通用组件,如按钮、弹窗、空状态等。
javascript
ts
// plugins/emptyBox.ts
import EmptyBox from '@/components/EmptyBox.vue'
export default {
install: (app) => {
app.component('EmptyBox', EmptyBox)
}
}
// main.ts
import emptyBoxPlugin from './plugins/emptyBox'
const app = createApp(App)
app.use(emptyBoxPlugin)
app.mount('#app')
🔍 特点
- 全局注册组件;
- 方便复用;
- 适合 UI 库开发。
📌 9. 使用 h()
渲染函数创建组件
适用于手动控制虚拟 DOM,常用于底层封装,不太建议复杂组件和业务操作,因为确实难维护,还难看的要死😁。。
javascript
ts
import { defineComponent, h } from 'vue'
export default defineComponent({
name: 'EmptyBox',
props: ['message', 'imgSrc'],
render() {
return h(
'div',
{ class: 'empty-box' },
[
h('img', { attrs: { src: this.imgSrc } }),
h('p', {}, this.message)
]
)
}
})
🔍 特点
- 手动构建虚拟 DOM;
- 更底层控制;
- 适合框架封装者。
🧩 对比下
写法 | 是否推荐 | 场景 | 备注 |
---|---|---|---|
<script setup> + TSX |
✅ 推荐 | 新项目、快速开发 | 最新最佳实践 |
defineComponent + setup() |
✅ 推荐 | TSX、类型安全 | 灵活性高 |
选项式 API | ⚠️ 可选 | Vue 2 迁移 | 不适合新项目 |
函数组件 | ✅ 推荐 | 无状态组件 | 性能更优 |
类组件 | ❌ 不推荐 | 旧项目兼容 | 已过时 |
异步组件 | ✅ 推荐 | 懒加载 | 提升首屏性能 |
插件式组件 | ✅ 推荐 | 全局组件 | UI 库常用 |
h() 渲染函数 |
✅ 稍微推荐 | 底层封装 | 灵活性强,不好维护 |
如果是新项目开发者,还是建议优先使用:
<script setup>
+ TSX- 或
defineComponent
+ setup()
这两种方式在 Vue 3 生态中最为流行,也最适合 TypeScript + JSX 的现代开发模式。
还有几个写法, 得借助其他库
createReusableTemplate(需要在项目中安装vueuse)
比如:在一个单文件组件中,定义一些可复用的模板代码的话,可以试试以下方法 createReusableTemplate 文档地址;

namedTemplate 、defineRender、setupSFC(需要在项目中安装Vue Macros)
namedTemplate
是 Vue Macros
推出的一个前瞻性的 Vue3 特性,文档地址

xml
<script setup>
const pager = 'top'
</script>
<template name="pager">
<span>This is pager</span>
</template>
<template>
<template v-if="pager === 'top'">
<template is="pager" />
</template>
<span>Here is data table</span>
<template v-if="pager === 'bottom'">
<template is="pager" />
</template>
</template>
defineRender
,只需要关心最终的DOM结构,不需要管状态的维护

xml
<script setup lang="tsx">
// 可以直接传递 JSX
defineRender(
<div>
<span>Hello</span>
</div>,
)
// 或使用渲染函数
defineRender(() => {
return (
<div>
<h1>Hello World</h1>
</div>
)
})
</script>
setupSFC
,需建一个.setup.tsx/.setup.jsx
文件,跟普通的 tsx/jsx
文件相比,每次引入.setup.tsx/.setup.jsx
这个文件,都是一个新的组件实例,状态并不会共享

- 安装
//
import Vue from '@vitejs/plugin-vue'
import VueMacros from 'vue-macros/vite'
export default defineConfig({
plugins: [
VueMacros({
plugins: {
vue: Vue({
include: [/\.vue$/, /\.setup\.[cm]?[jt]sx?$/],
// ⬆️ 需要添加 setup 模式
}),
},
}),
],
})
- 使用
//
defineProps<{
foo: string
}>()
defineEmits<{
(evt: 'change'): void
}>()
export default () => (
<div>
<h1>Hello World</h1>
</div>
)
还有个非常6的库,Vine , 文档地址

总结
条条大路通罗马,不论你哪种写法,最终都会编译和处理为一个 Vue 组件对象(Component Object),但是在实际项目中最好统一组件类型风格
- Vue 组件最终形态的结构
{
name: 'MyComponent',
props: { /* ... */ },
emits: { /* ... */ },
setup?: () => any,
data?: () => any,
methods: { /* ... */ },
template?: string | Function,
render?: Function,
components: { /* ... */ },
directives: { /* ... */ },
beforeCreate: Function,
created: Function,
beforeMount: Function,
mounted: Function,
beforeUpdate: Function,
updated: Function,
beforeUnmount: Function,
unmounted: Function,
errorCaptured: Function,
// 其他配置...
}