掌握 Vue 3 组件的多种写法:从基础到进阶

更新日期: 2025-06-27阅读: 80标签: 组件

打开 vue 3 的官方文档,你会看到介绍两种主要的组件编写方式:选项式 api (Options API) 和 组合式 API (Composition API)。文档提供了两种风格的代码示例,方便你根据自己的喜好选择。

实际上,围绕这两种核心范式,Vue 3 组件可以衍生出多种不同的具体写法。这些写法都基于相同的 Vue 核心系统,背后的概念也是相通的,只是使用的接口和代码组织方式不同。日常开发中,你通常不需要用到所有写法,但了解它们的存在和原理非常有用。

当你阅读 Vue 文档时,有时可能会对一些示例或 API 的作用感到模糊。或者,在查看 Vue 源码时,可能会遇到一些不太理解的逻辑。这时,了解 Vue 组件的不同写法,尤其是理解它们如何一步步构建和渲染,就能帮你拨开迷雾。先知道怎么用,再去探究怎么实现,学习曲线会更平缓。

阅读本文,你将收获:

  • 理解 Vue 组件渲染的基本原理。

  • 清楚 defineComponent、h、createVnode 的作用和区别。

  • 明白渲染函数和 JSX 的关系与差异。

  • 快速上手在 Vue 中使用 JSX。

  • 能轻松看懂 Vue 文档中的各种示例和 API 说明。

  • 能识别和理解不同的 Vue 组件写法。

  • 无论你是新手还是有一定经验的开发者,都能对 Vue 有更深入的认识。

文章内容由浅入深,帮助你轻松理解。


一、最常用:<script setup> 语法糖 (SFC)

这是目前 Vue 3 最流行和推荐的写法。它使用 .vue 单文件组件 (SFC):

  • 结构 (<template>): 用类似 html 的模板描述 UI。

  • 逻辑 (<script setup>): 使用 setup 语法糖编写组件逻辑(响应式状态、计算属性、函数等)。setup 内部的代码就像在 setup() 函数内部一样,但更简洁,无需手动暴露变量和函数。

  • 样式 (<style>): 编写组件样式。

这种方式将所有相关代码放在一个 .vue 文件中,便于维护。

<template>
  <div>{{ name }}</div>
</template>

<script setup lang="ts">
import { ref } from "vue";
const name = ref("天气好"); // 定义响应式数据
</script>

<style scoped>
/* 作用域样式 */
</style>

在 App.vue 中使用:

<template>
  <User /> <!-- 像使用普通标签一样使用组件 -->
</template>

<script setup lang="ts">
import User from "./User.vue"; // 导入组件
</script>

注意: 后续所有写法最终目的都是导出一个可用的组件,在父组件中引入和使用的方式(如上所示)是完全一样的,因此不再重复展示父组件代码。


二、Vue 2 风格:选项式 API (SFC)

这是 Vue 2 的经典写法,Vue 3 仍然支持。

  • 结构 (<template>): 同上。

  • 逻辑 (<script>): 导出一个包含特定选项(data, methods, computed, lifecycle hooks 等)的普通对象。这些选项告诉 Vue 如何创建组件实例。

  • 样式 (<style>): 同上。

<template>
  <div>{{ name }}</div>
</template>

<script lang="ts">
export default {
  data() {
    return {
      name: "天气好"
    };
  }
};
</script>

<style></style>

使用 defineComponent 辅助函数:

虽然直接导出对象 Vue 也能识别,但使用 defineComponent 有巨大优势:

<template>
  <div>{{ name }}</div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  data() {
    return {
      name: "天气好"
    };
  }
});
</script>

<style></style>
  • 核心价值: 提供强大的 TypeScript 类型推导。它能让你在编写选项时获得完善的类型提示和检查,显著提升开发体验和代码健壮性。它本身也是一个工厂函数,让代码看起来更规范。


三、Vue 3 选项式:结合 setup 选项 (SFC)

Vue 3 在选项式 API 中新增了 setup 选项。

  • 结构 (<template>): 同上。

  • 逻辑 (<script>): 在选项中定义一个 setup 函数。这个函数可以使用组合式 API (ref, reactive, onMounted 等)。setup 返回的对象中的属性,可以在模板和选项的 this 上下文中访问。

  • 样式 (<style>): 同上。

<template>
  <div>{{ name }}</div>
</template>

<script lang="ts">
import { ref } from "vue";

export default {
  setup() {
    const name = ref("天气好");
    return { name }; // 暴露给模板和`this`
  }
};
</script>

<style></style>

搭配 defineComponent:

<template>
  <div>{{ name }}</div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup(props, context) { // defineComponent 提供 setup 参数的类型提示
    const name = ref("天气好");
    return { name };
  }
});
</script>

<style></style>

要点: 一旦使用了 setup 选项,通常就不再需要(也不建议混合使用)data、methods 等传统选项。主要逻辑应集中在 setup 中,并优先使用组合式 API。<script setup> 语法糖本质上是对这种写法的极大简化。


四、深入底层:渲染函数 (Render Functions)

Vue 的模板 (<template>) 最终都会被编译成 渲染函数。渲染函数负责返回一个描述组件 UI 的 虚拟 dom (VNode) 树。Vue 提供了直接编写渲染函数的能力,让你完全掌控渲染过程。

1. 使用 render 选项 (SFC)

<script lang="ts">
import { defineComponent, h } from "vue"; // 导入 h 函数 (createVnode 的别名)

export default defineComponent({
  data() {
    return { name: "天气好" };
  },
  render() {
    // 使用 h 函数创建 VNode
    return h('div', this.name); // 相当于 <div>天气好</div>
  }
});
</script>

<style></style>
  • 关键点:

    • 不再需要 <template> 块。

    • h 函数 (或 createVnode) 是构建 VNode 的基础工具。它接收元素/组件名、属性/Props、子节点等参数。

    • v-if, v-for, 插槽等模板功能在渲染函数中都有对应的 JavaScript 实现方式(详见官方文档)。render 选项和 <template> 是互斥的,只能选其一。

2. setup 中返回渲染函数 (SFC)

<script lang="ts">
import { defineComponent, h, ref } from "vue";

export default defineComponent({
  setup() {
    const name = ref("天气好");
    // 返回一个渲染函数
    return () => h('div', name.value); // 注意:模板中自动解包 ref,h函数中需要 .value
  }
});
</script>

<style></style>
  • 关键点:

    • 如果 setup 返回一个函数,这个函数会被当作渲染函数使用,此时不需要 render 选项,也不需要 <template>。

    • 重要区别: 在 h 函数(或 JSX)内部使用 ref 时,必须访问 .value,因为这里没有模板的自动解包机制。

3. defineComponent 直接传入 setup 函数 (SFC)

<script lang="ts">
import { defineComponent, h, ref } from "vue";

export default defineComponent(() => { // defineComponent 直接接收 setup 函数
  const name = ref("天气好");
  return () => h('div', name.value); // 返回渲染函数
});
</script>

<style></style>

这是上一种写法的更简洁形式。


五、拥抱 JSX

JSX 是一种 JavaScript 的语法扩展,允许你在 JavaScript 代码中书写类似 HTML 的结构。它提供了比直接写 h 函数调用更直观、更接近模板的体验,同时又保留了 JavaScript 的灵活性和强大功能。在 Vue 中使用 JSX 需要配置相应的编译器支持(通常是 @vue/babel-plugin-jsx 或 @vitejs/plugin-vue-jsx)。

1. 在 render 选项中使用 JSX (SFC - <script lang="tsx">)

<script lang="tsx">
import { defineComponent } from "vue";

export default defineComponent({
  data() {
    return { name: "天气好" };
  },
  render() {
    // 使用 JSX 替代 h 函数
    return <div>{this.name}</div>;
  }
});
</script>

<style></style>
  • 关键点: 必须将 <script> 标签的 lang 属性设置为 tsx (或 jsx)。

2. setup 中返回 JSX 渲染函数 (SFC - <script lang="tsx">)

<script lang="tsx">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup() {
    const name = ref("天气好");
    return () => <div>{name.value}</div>; // 返回 JSX 渲染函数,注意 .value
  }
});
</script>

<style></style>

3. defineComponent 直接传入 setup 函数 + JSX (SFC - <script lang="tsx">)

<script lang="tsx">
import { defineComponent, ref } from "vue";

export default defineComponent(() => {
  const name = ref("天气好");
  return () => <div>{name.value}</div>; // JSX
});
</script>

<style></style>

4. 直接导出 JSX 元素 (不推荐 - SFC - <script lang="tsx">)

<script lang="tsx">
import { ref } from "vue";

const name = ref("天气好");
// 直接导出 JSX 元素 (一个 VNode)
const User = <div>{name.value}</div>;

export default User;
</script>

<style scoped></style>
  • 严重缺点: 这种方式导出的组件 无法接收 props。因为组件函数没有被定义,外部无法传递参数给它。通常不建议使用。

5. defineComponent + 函数参数 + Props (SFC - <script lang="tsx">)

<script lang="tsx">
import { defineComponent } from "vue";

export default defineComponent(
  (props) => { // setup 函数接收 props
    return () => <div>{props.userName}</div>;
  },
  {
    // 第二个参数定义组件选项,主要是 props
    props: {
      userName: String // 定义 prop 类型
    }
  }
);
</script>

<style></style>

6. 在 <template> 内使用局部 JSX 组件 (SFC - <script setup lang="tsx">)

<template>
  <User /> <!-- 使用局部定义的 JSX 组件 -->
</template>

<script setup lang="tsx">
import { ref } from "vue";
const name = ref("天气好");
// 定义一个局部的 JSX 组件 (变量)
const User = <div>{name.value}</div>;
</script>

<style></style>
  • 用途: 适合在组件内部复用一些非常小的、不需要单独抽离的 UI 片段(例如一个带有复杂类名的 div)。

7. 函数式组件 (形式一 - SFC - <script setup lang="tsx">)

<template>
  <User :user-name="name" /> <!-- 注意属性名传递方式 -->
</template>

<script setup lang="tsx">
import { ref } from "vue";
const name = ref("天气好");
// 定义一个函数式组件
const User = (props: { "user-name": string }) => { // 注意属性名匹配
  return <>{props["user-name"]}</>;
};
</script>

<style></style>
  • 关键点:

    • 这种在 .vue 文件内定义的函数式组件,其 props 不会自动进行“连字符 (kebab-case) 到小驼峰 (camelCase)”的转换。传递属性时需要严格匹配函数参数中定义的属性名格式(通常直接用 props.xxx 访问,传递时也用 :xxx)。

    • 用途同上,主要用于内部复用片段。


六、独立 JSX/TSX 文件写法

你可以完全脱离 .vue 文件的框架,将组件写在 .jsx 或 .tsx 文件中。

1. 选项式 API + JSX (.tsx)

// User.tsx
import { defineComponent } from 'vue';

export default defineComponent({
  data() {
    return { name: '天气好' };
  },
  render() {
    return <div>{this.name}</div>;
  }
});
// 或者省略 defineComponent (不推荐,失去类型推导)
export default {
  data() { ... },
  render() { ... }
};

2. 组合式 API (setup) + JSX (.tsx)

// User.tsx
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const name = ref('天气好');
    return () => <div>{name.value}</div>;
  }
});

3. defineComponent 直接传入 setup 函数 + JSX (.tsx)

// User.tsx
import { defineComponent, ref } from 'vue';

export default defineComponent(() => {
  const name = ref('天气好');
  return () => <div>{name.value}</div>;
});

4. Vue 的函数式组件 (形式二 - .tsx)

// User.tsx
import { ref } from 'vue';

// 1. 定义组件函数
function User(props: { userName: string }) {
  // 内部状态 (注意:每次渲染都会重新创建,不是响应式的!)
  // const localName = ref('内部状态'); // 通常避免在函数式组件内部使用 ref
  return <div>{props.userName}</div>;
}

// 2. 附加静态属性 (可选,用于定义 props, emits 等)
User.props = {
  userName: String
};
// User.emits = ['someEvent']; // 如果需要定义 emits

// 3. 导出
export default User;
  • 关键点:

    • 这是 .tsx 文件特有的写法,允许直接导出一个函数作为组件。

    • 与 React 的区别: Vue 的函数式组件主要是为了性能优化(无实例),内部通常应该是无状态的(无 this,避免使用 ref/reactive)。如果需要状态管理,通常应该使用有状态组件(选项式或组合式)。

    • Props 定义: 通过在函数上附加 props 静态属性来定义。

    • Vue 2 vs Vue 3: 在 Vue 2 中,函数式组件是主要优化手段;在 Vue 3 中,组合式 API 本身性能已经很好,函数式组件的优化意义相对减小,但它仍然是一种有效的编写简单、无状态组件的方式。


小结:理解与选择

Vue 组件的核心范式是 选项式 API 和 组合式 API。本文展示的多种写法,都是在这两大范式基础上,结合不同的语法(模板、渲染函数、JSX)和文件组织方式(.vue SFC、.tsx)衍生出来的。

  • .vue 文件 (SFC): 是 Vue 的“一等公民”,完美支持模板、选项式、组合式 (<script setup>)、渲染函数以及 JSX(需 lang="tsx")。模板因其简洁性、内置优化和更友好的学习曲线,是官方推荐的主要方式。

  • JSX: 提供比纯 h 函数更直观的渲染函数写法,比模板更灵活的 JavaScript 表达能力。适合需要高度动态化或复杂逻辑控制 UI 的场景。主要在 .tsx 文件或 SFC 的 <script lang="tsx"> 中使用。

  • 渲染函数 (h/createVnode): 是 Vue 渲染的底层基础,理解它有助于理解模板和 JSX 的编译结果。直接手写适用于需要极致控制或编写高阶组件/渲染工具库的情况。

  • defineComponent: 强烈推荐使用,尤其在 TypeScript 项目中,它能提供至关重要的类型推导支持。

  • 函数式组件: 在 .tsx 文件中可以直接导出函数形式。主要用于编写简单、无状态、可复用的展示型组件。在 .vue 文件内,函数通常用于定义局部可复用的 JSX 片段。

为什么了解这些写法有用?

  1. 理解原理: 了解模板如何编译成渲染函数,理解 h 函数的作用,能让你更深入地理解 Vue 的工作原理。

  2. 阅读文档与源码: 文档示例和源码中会用到各种写法,熟悉它们能让你更容易理解。

  3. 处理特殊情况: 有时会遇到必须使用渲染函数或 JSX 才能优雅解决的问题(例如动态生成高度复杂的组件结构)。

  4. 社区代码: 开源库或遗留代码可能使用不同的写法,了解它们有助于你理解和维护。

  5. 技术选型: 理解各种写法的优缺点,让你能根据项目需求和团队习惯做出更合适的选择。


个人建议:如何选择

  • 新项目首选: <script setup> 语法糖 + 模板。这是目前 Vue 3 最主流、最简洁、最高效的开发方式,官方大力推荐,工具链支持最好。

  • 需要 JSX/渲染函数时:

    • 如果主要在 SFC 内使用,推荐在 .vue 文件中使用 setup 选项(或 defineComponent 直接传入函数)并返回 JSX 渲染函数 (<script lang="tsx">)。这样可以利用 SFC 的样式隔离等特性。

    • 如果需要编写大量纯逻辑组件或与 React 生态有较多交互,可以考虑使用 .tsx 文件 + defineComponent + setup (或直接传入函数)

  • 选项式 API: 适合 Vue 2 迁移项目、偏好基于选项组织代码的开发者、或者非常简单的组件。

  • defineComponent: 总是使用它,尤其是在 TypeScript 项目中,它能带来巨大的类型安全收益。

  • 函数式组件 (.tsx 文件): 仅在需要编写简单、无状态、可复用的展示组件时考虑。避免在其中使用响应式状态 (ref/reactive) 和生命周期钩子。

最终选择哪种写法,取决于项目复杂度、团队规范、个人偏好以及具体组件的需求。Vue 的灵活性让你能够选择最适合当前任务的工具。理解各种写法的本质,能让你更自信地驾驭 Vue 开发。

希望这篇文章帮你理清了 Vue 3 组件的多种写法!如果你也想尝试在 Vue 项目中使用 JSX,记得查阅官方文档或相关插件(如 @vitejs/plugin-vue-jsx)的配置指南。

链接: https://fly63.com/article/detial/12842

Vuetify基于vue2.0,为移动而生的组件框架

Vuetify 支持SSR(服务端渲染),SPA(单页应用程序),PWA(渐进式Web应用程序)和标准HTML页面。 Vuetify是一个渐进式的框架,试图推动前端开发发展到一个新的水平。

Vue中插槽的作用_Vue组件插槽的使用以及调用组件内的方法

通过给组件传递参数, 可以让组件变得更加可扩展, 组件内使用props接收参数,slot的使用就像它的名字一样, 在组件内定义一块空间。在组件外, 我们可以往插槽里填入任何元素。slot-scope的作用就是把组件内的数据带出来

react 函数子组件(Function ad Child Component)

函数子组件(FaCC )与高阶组件做的事情很相似, 都是对原来的组件进行了加强,类似装饰者。FaCC,利用了react中children可以是任何元素,包括函数的特性,那么到底是如何进行增强呢?

Vue和React组件之间的传值方式

在现代的三大框架中,其中两个Vue和React框架,组件间传值方式有哪些?组件间的传值是灵活的,可以有多种途径,父子组件同样可以使用EventBus,Vuex或者Redux

vue.js自定义组件directives

自定义指令:以v开头,如:v-mybind。bind的作用是定义一个在绑定时执行一次的初始化动作,观察bind函数,它将指令绑定的DOM作为一个参数,在函数体中,直接操作DOM节点为input赋值。

vue中prop属性传值解析

prop的定义:在没有状态管理机制的时候,prop属性是组件之间主要的通信方式,prop属性其实是一个对象,在这个对象里可以定义一些数据,而这些数据可以通过父组件传递给子组件。 prop属性中可以定义属性的类型,也可以定义属性的初始值。

Web组件简介

Web组件由三个独立的技术组成:自定义元素。很简单,这些是完全有效的HTML元素,包含使用一组JavaScript API制作的自定义模板,行为和标记名称(例如,<one-dialog>)。

web组件调用其他web资源

web组件可以直接或间接的调用其他web资源。一个web组件通过内嵌返回客户端内容的另一个web资源的url来间接调用其他web资源。在执行时,一个web资源通过包含另一个资源的内容或者转发请求到另一个资源直接调用。

vue中如何实现的自定义按钮

在实际开发项目中,有时我们会用到自定义按钮;因为一个项目中,众多的页面,为了统一风格,我们会重复用到很多相同或相似的按钮,这时候,自定义按钮组件就派上了大用场,我们把定义好的按钮组件导出,在全局引用,就可以在其他组件随意使用啦,这样可以大幅度的提高我们的工作效率。

Vue子组件调用父组件的方法

Vue中子组件调用父组件的方法,这里有三种方法提供参考,第一种方法是直接在子组件中通过this.$parent.event来调用父组件的方法,第二种方法是在子组件里用$emit向父组件触发一个事件,父组件监听这个事件就行了。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!