【组件】vue3组件写法大全

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()methodscomputed 等选项;
  • 模板使用字符串或外部模板文件;
  • 不适合复杂逻辑封装。

📌 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)

namedTemplateVue 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,
  // 其他配置...
}
相关推荐
止观止14 分钟前
React响应式组件范式:从类组件到Hooks
javascript·react.js·ecmascript
@大迁世界14 分钟前
React 及其生态新闻 — 2025年6月
前端·javascript·react.js·前端框架·ecmascript
LJianK141 分钟前
Java和JavaScript的&&和||
java·javascript·python
红尘散仙1 小时前
Rust 终端 UI 开发新玩法:用 Ratatui Kit 轻松打造高颜值 CLI
前端·后端·rust
mldong1 小时前
mldong-goframe:基于 GoFrame + Vben5 的全栈快速开发框架正式开源!
vue.js·后端·go
新酱爱学习1 小时前
前端海报生成的几种方式:从 Canvas 到 Skyline
前端·javascript·微信小程序
袁煦丞1 小时前
把纸堆变数据流!Paperless-ngx让文件管理像打游戏一样爽:cpolar内网穿透实验室第539个成功挑战
前端·程序员·远程工作
慧慧吖@2 小时前
关于两种网络攻击方式XSS和CSRF
前端·xss·csrf
徐小夕2 小时前
失业半年,写了一款多维表格编辑器pxcharts
前端·react.js·架构
LaoZhangAI3 小时前
Kiro vs Cursor:2025年AI编程IDE深度对比
前端·后端