【组件】vue3组件写法大全

810 阅读6分钟

Jym好😘,我是珑墨,今天给大家分享  不同姿势写vue3组件  ,嘎嘎的😍,看下面。

vue3组件虽然多种骚写法。可以都尝尝鲜,但是咱不能在项目中各种滥用,还是得保持项目同类型写法,除非特殊操作


1752632662865.png

📌 1. <script setup> + TSX(推荐)

这是目前最主流的写法,适合现代 Vue 3 + Vite + TypeScript 项目,简洁高效,一顿cv操作。

    // .vue文件
    <script setup lang="tsx">
    import { defineProps } from 'vue'

    const props = defineProps({
      message: String,
      imgSrc: String
    })
    
    //写法1. tsx填充内容, script标签必须加tsx,否则无法解析
    const TsxCont = () => {
        return (<>
            <img v-show={true} src={imgSrc} />
            <p>{ message }</p>
        </>)
     }
     
     //写法2. 用h函数写,效率最高,但是难看
    const HCont = () => {
    return h('div', [ // 外层div元素可以用Fragment
        h('img', {
            src: imgSrc,
            style: { display: 'block' } // 对应v-show={true}
        }),
        h('p', message)
    ])
    
    //写法3. 使用render函数
    const cont = {
    render(h) {
        return h('div', [
            h('img', {
                attrs: { src: imgSrc },
                style: { display: 'block' }
            }),
            h('p', this.message)
        ])
    }
    }
    
   // 或者直接使用render函数包装dom元素(不建议这样做,影响性能、无法享受Vue的响应式系统、无生命周期、内存泄露等)
const contWithRender = () => {
const container = document.createElement('div') // 必须是 DOM 元素
const vnode = h('div', [
  h('img', {
    attrs: { src: imgSrc },
    style: { display: 'block' }
  }),
  h('p', this.message)
])
render(vnode, container) // 将 VNode 渲染到容器中
return container

}

    //4. 如果使用第三方ui组件
    actionColumn: {
    // 自定义表格列
      customRender({ column, record }) {
        return <TableAction actions={tableActionHandle(column, record/>
      }
    }
}
 }
    </script>
    <template>
      <div class="empty-box">
      <!-- 注意:模板中使用上面1~3写法,需要用到渲染组件(底层其实就是h渲染函数) -->
      
      // 用渲染函数
        <component :is="TsxCont" />
        
     // 直接当做组件使用
     <TsxCont /> <HCont />
     
     // render 函数 contWithRender调用
     <div v-html="contWithRender().innerHTML"></div>
                 
        <!-- 直接调用{{TsxCont()}}写法不成立 --> 
        <!--因为:
            -   模板语法:Vue 的模板使用 {{ }} 插值语法
            -   JSX 语法:返回的是虚拟 DOM 对象,不是字符串
            -   冲突:模板期望字符串,但 JSX 返回的是对象
        --> 
        <slot />
      </div>
    </template>

🔍 特点

  • 使用 <script setup> 简化逻辑;
  • 支持 JSX 模板;
  • 类型推导友好;
  • 开发体验更佳。

📌 2. defineComponent + setup() 函数(组合式 API)

适用于需要类型支持或使用 TSX 的项目,现在基本上都用ts了吧。

// .tsx文件
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时,直接用render函数
  render() {
    return (
      <div class="empty-box">
        <img src={props.imgSrc} />
        <p>{props.message}</p>
      </div>
    )
  },
})

🔍 特点

  • 显式调用 setup(),或者静态的render处理;
  • 可返回 JSX;
  • 支持 TypeScript 类型;
  • 更灵活地控制渲染逻辑。

📌 3. 选项式 API(Options API)

适合从 Vue 2 迁移的项目,结构清晰,但不如组合式 API 灵活。

// .tsx文件
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)

适用于无状态组件,性能更好,但不能使用生命周期钩子。

// .tsx文件
import { defineComponent } from 'vue'
const cont = () => (
      <div class="empty-box">
        <img src={props.imgSrc} />
        <p>{props.message}</p>
      </div>
    )
export default defineComponent({
  name: 'EmptyBox',
  props: ['message', 'imgSrc'],
  setup(props) {
    return cont
  }
})

🔍 特点

  • 没有 this 上下文;
  • 没有响应式数据;
  • 更轻量,适合纯展示组件。

📌 5. 类组件(Class Component)

适合熟悉面向对象编程风格的开发者,有点像react操作,但官方已不推荐。 原因是Vue 的设计哲学是越来越倾向于 组合式 API(Composition API)  和 函数式编程风格,而类组件属于面向对象的写法,与 Vue 3 的发展方向不一致。再说,太依赖额外插件和装饰器语法((如 @Component@Prop())),不便于和其他类型组件融合。

// .tsx文件
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>,这是目前最常见的。

// .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)

适用于懒加载组件,提升首屏性能。

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() 注册全局组件)

适用于全局通用组件,如按钮、弹窗、空状态等。

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,常用于底层封装,不太建议复杂组件和业务操作,因为确实难维护,还难看的要死😁。。

// .tsx文件
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 文档地址

image.png

namedTemplate 、defineRender、setupSFC(需要在项目中安装Vue Macros)

namedTemplate 是 Vue Macros 推出的一个前瞻性的 Vue3 特性,文档地址

image.png

<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结构,不需要管状态的维护

image.png

<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这个文件,都是一个新的组件实例,状态并不会共享

image.png

  • 安装
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文档地址

image.png


总结

条条大路通罗马,不论你哪种写法,最终都会编译和处理为一个 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,
  // 其他配置...
}