vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3 单文件组件

Vue3 单文件中不同的组件

作者:FlyBeautySky

在Vue 3的单文件组件(.vue 文件)中,组件可以从多个层面来理解,下面我将详细说明哪些部分算是组件,以及如何识别它们,感兴趣的朋友跟随小编一起看看吧

在 Vue 3 的单文件组件(.vue 文件)中,组件可以从多个层面来理解。下面我将详细说明哪些部分算是组件,以及如何识别它们。

📁 组件定义的多个层次

1.文件本身就是一个组件

这是最直观的理解:每个 .vue 文件本身就是一个 Vue 组件。

<!-- UserCard.vue - 这个文件本身就是一个组件 -->
<template>
  <div class="user-card">
    <h3>{{ user.name }}</h3>
    <p>{{ user.email }}</p>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const user = ref({
  name: '张三',
  email: 'zhangsan@example.com'
})
</script>

识别要点

2.组件中的子组件

在单个 .vue 文件中,可以在 <template> 中使用的自定义标签都是组件。

示例1:使用全局注册的组件

<!-- ParentComponent.vue -->
<template>
  <div>
    <!-- 这些都是组件 -->
    <el-button>按钮组件</el-button>      <!-- Element Plus 组件 -->
    <el-input placeholder="输入框组件" /> <!-- Element Plus 组件 -->
    <router-view />                     <!-- Vue Router 组件 -->
  </div>
</template>

示例2:使用局部注册的组件

<!-- ParentComponent.vue -->
<template>
  <div>
    <!-- 这些都是子组件 -->
    <ChildComponentA />
    <ChildComponentB />
    <UserAvatar :user="currentUser" />
  </div>
</template>
<script setup>
// 导入的组件
import ChildComponentA from './ChildComponentA.vue'
import ChildComponentB from './ChildComponentB.vue'
import UserAvatar from './UserAvatar.vue'
// 在 <script setup> 中,导入的组件会自动在模板中可用
</script>

3.动态组件

使用 Vue 的 <component> 标签配合 :is 属性也是组件。

<template>
  <div>
    <!-- 动态组件 -->
    <component :is="currentComponent" />
    <!-- 也可以是异步组件 -->
    <Suspense>
      <template #default>
        <AsyncComponent />
      </template>
      <template #fallback>
        <LoadingSpinner />
      </template>
    </Suspense>
  </div>
</template>
<script setup>
import { shallowRef, defineAsyncComponent } from 'vue'
import HomePage from './HomePage.vue'
import AboutPage from './AboutPage.vue'
// 动态组件
const currentComponent = shallowRef(HomePage)
// 异步组件
const AsyncComponent = defineAsyncComponent(() =>
  import('./AsyncComponent.vue')
)
</script>

4.内置组件

Vue 3 提供的内置组件,虽然不写在文件中,但使用它们也算作组件。

<template>
  <!-- 内置组件 -->
  <Teleport to="body">
    <Modal />
  </Teleport>
  <KeepAlive>
    <component :is="currentComponent" />
  </KeepAlive>
  <Transition name="fade">
    <div v-if="show">过渡内容</div>
  </Transition>
</template>

5.函数式组件

在同一个文件中定义的函数式组件。

<!-- FunctionalComponentExample.vue -->
<template>
  <div>
    <!-- 使用函数式组件 -->
    <DynamicHeading :level="1">标题1</DynamicHeading>
    <DynamicHeading :level="2">标题2</DynamicHeading>
  </div>
</template>
<script setup>
import { h } from 'vue'
// 函数式组件 - 这也是一个组件
const DynamicHeading = (props, { slots }) => {
  return h(`h${props.level}`, {}, slots.default())
}
// 定义 props
DynamicHeading.props = ['level']
</script>

6.渲染函数组件

<script> 中使用渲染函数定义的组件。

<!-- RenderFunctionExample.vue -->
<script>
import { h, defineComponent } from 'vue'
// 渲染函数组件
const CustomButton = defineComponent({
  props: {
    type: {
      type: String,
      default: 'button'
    }
  },
  setup(props, { slots }) {
    return () => h(
      'button',
      {
        class: 'custom-button',
        type: props.type
      },
      slots.default()
    )
  }
})
export default defineComponent({
  components: {
    CustomButton
  }
})
</script>
<template>
  <CustomButton>点击我</CustomButton>
</template>

7.JSX 组件

如果在 Vue 3 中使用 JSX,那么在同一个文件中定义的 JSX 函数也是组件。

<!-- JsxComponentExample.vue -->
<script>
import { defineComponent } from 'vue'
// JSX 组件
const JsxButton = defineComponent({
  props: ['onClick'],
  setup(props, { slots }) {
    return () => (
      <button onClick={props.onClick} class="jsx-button">
        {slots.default()}
      </button>
    )
  }
})
export default defineComponent({
  components: {
    JsxButton
  }
})
</script>

🎯 实际项目示例分析

让我们通过一个完整的示例来识别其中所有的组件:

<!-- App.vue -->
<template>
  <div id="app">
    <!-- 1. 路由组件(动态组件) -->
    <router-view />
    <!-- 2. UI 框架组件 -->
    <el-button @click="showModal = true">打开弹窗</el-button>
    <el-dialog v-model="showModal" title="提示">
      <span>这是一个弹窗</span>
    </el-dialog>
    <!-- 3. 内置组件 -->
    <Teleport to="body">
      <!-- 4. 全局注册的组件 -->
      <GlobalNotification />
    </Teleport>
    <!-- 5. 局部注册的组件 -->
    <Header :title="appTitle" />
    <Sidebar :menus="menuItems" />
    <MainContent>
      <!-- 6. 插槽中的组件 -->
      <UserList :users="users" />
    </MainContent>
    <Footer />
    <!-- 7. 条件渲染的组件 -->
    <template v-if="user.isAdmin">
      <AdminPanel />
    </template>
    <!-- 8. 循环渲染的组件 -->
    <template v-for="item in featuredItems" :key="item.id">
      <FeaturedCard :item="item" />
    </template>
    <!-- 9. 动态组件 -->
    <component :is="currentTabComponent" />
  </div>
</template>
<script setup>
import { ref, shallowRef, computed, defineAsyncComponent } from 'vue'
import { useRouter } from 'vue-router'
import { ElButton, ElDialog } from 'element-plus'
// 10. 导入的组件
import Header from './components/Header.vue'
import Sidebar from './components/Sidebar.vue'
import MainContent from './components/MainContent.vue'
import Footer from './components/Footer.vue'
import UserList from './components/UserList.vue'
// 11. 异步组件
const AdminPanel = defineAsyncComponent(() => 
  import('./components/AdminPanel.vue')
)
// 12. 全局组件(已经在 main.js 中注册)
// GlobalNotification 已在全局注册
// 13. 函数式组件
const StatusBadge = (props, context) => {
  // 渲染函数返回虚拟节点
  return h('span', {
    class: `badge badge-${props.type}`,
    style: { backgroundColor: props.color }
  }, context.slots.default())
}
StatusBadge.props = ['type', 'color']
// 14. 动态组件定义
const tabs = {
  home: defineAsyncComponent(() => import('./views/Home.vue')),
  about: defineAsyncComponent(() => import('./views/About.vue')),
  contact: shallowRef({
    template: '<div>联系我们</div>'
  })
}
const currentTab = ref('home')
const currentTabComponent = computed(() => tabs[currentTab.value])
// 响应式数据
const showModal = ref(false)
const appTitle = ref('我的应用')
const menuItems = ref([/* ... */])
const users = ref([/* ... */])
const user = ref({ isAdmin: true })
const featuredItems = ref([/* ... */])
</script>
<style scoped>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
}
</style>

📊 组件分类总结

序号组件类型示例定义位置使用方式
1文件组件App.vue 本身独立的 .vue 文件被其他文件导入
2子组件Header, Sidebar其他 .vue 文件在模板中作为标签使用
3UI 框架组件el-button, el-dialogElement Plus 库在模板中作为标签使用
4内置组件router-view, TeleportVue 3 核心在模板中作为标签使用
5动态组件<component :is="...">当前文件或导入通过 :is 动态切换
6异步组件defineAsyncComponent()通过函数定义延迟加载
7函数式组件StatusBadge 函数在当前文件定义通过函数调用或标签
8渲染函数组件使用 h() 函数<script>通过渲染函数
9全局组件GlobalNotificationmain.js 注册在任何地方使用
10插槽组件<UserList> 在插槽内子组件通过插槽传递

🔍 如何识别组件

1.在模板中识别

<template>
  <!-- 这些都是组件 -->
  <ComponentName />          <!-- 自定义组件 -->
  <el-button />              <!-- UI 框架组件 -->
  <router-view />            <!-- 内置组件 -->
  <component :is="comp" />   <!-- 动态组件 -->
</template>

识别规则

2.在 script 中识别

// 这些都是组件的定义或导入
import UserCard from './UserCard.vue'           // 导入组件
const AsyncComp = defineAsyncComponent(...)     // 定义异步组件
const FunctionalComp = () => h('div', ...)      // 定义函数式组件
export default { components: { ... } }          // 注册组件

3.在实际代码中识别

<template>
  <!-- 组件示例 -->
  <div>
    <!-- 1. 原生 HTML 元素 -->
    <div>这是 HTML 元素</div>
    <button>这是 HTML 按钮</button>
    <!-- 2. Vue 组件 -->
    <MyComponent />                    <!-- 自定义组件 -->
    <el-button>按钮</el-button>        <!-- UI 库组件 -->
    <router-link to="/">首页</router-link>  <!-- 路由组件 -->
    <!-- 3. 内置组件 -->
    <Transition>
      <div v-if="show">内容</div>
    </Transition>
    <!-- 4. 动态组件 -->
    <component :is="currentView" />
  </div>
</template>

🎯 快速识别技巧

技巧1:查看导入语句

// 这些导入的都是组件
import Button from './Button.vue'           // 文件组件
import { ElButton } from 'element-plus'     // UI 库组件
import { RouterView } from 'vue-router'     // 内置组件

技巧2:查看组件注册

// Options API
export default {
  components: {
    Button,      // 局部注册的组件
    ElButton,    // UI 组件
    RouterView   // 内置组件
  }
}
// Composition API (<script setup>)
// 导入的组件自动注册

技巧3:查看模板使用

<template>
  <!-- 组件特征 -->
  <!-- 1. 属性绑定 -->
  <Component :prop="value" @event="handler" />
  <!-- 2. 插槽使用 -->
  <Component>
    <template #header>标题</template>
    内容
  </Component>
  <!-- 3. 作用域插槽 -->
  <Component v-slot="{ item }">
    {{ item.name }}
  </Component>
</template>

📈 实际项目中的应用

项目结构示例

src/
├── components/           # 可复用组件
│   ├── common/          # 通用组件
│   │   ├── Button.vue
│   │   ├── Input.vue
│   │   └── Modal.vue
│   └── layout/          # 布局组件
│       ├── Header.vue
│       ├── Sidebar.vue
│       └── Footer.vue
├── views/               # 页面组件
│   ├── Home.vue
│   ├── About.vue
│   └── User/
│       ├── List.vue
│       └── Detail.vue
└── App.vue             # 根组件

组件使用示例

<!-- Home.vue -->
<template>
  <div class="home">
    <!-- 布局组件 -->
    <AppHeader />
    <AppSidebar />
    <!-- 页面内容 -->
    <main class="content">
      <!-- 功能组件 -->
      <SearchBar v-model="searchText" />
      <UserList :users="filteredUsers" />
      <!-- UI 组件 -->
      <el-pagination
        :current-page="currentPage"
        :total="total"
        @current-change="handlePageChange"
      />
      <!-- 内置组件 -->
      <router-link to="/about">关于我们</router-link>
      <!-- 动态组件 -->
      <component :is="getComponentByType(user.type)" />
    </main>
    <!-- 布局组件 -->
    <AppFooter />
  </div>
</template>

🎯 总结

在 Vue 3 的单文件组件中,以下这些都算是组件

  1. 文件本身:每个 .vue 文件就是一个组件
  2. 导入的子组件:从其他文件导入的 Vue 组件
  3. UI 框架组件:如 Element Plus、Ant Design Vue 等
  4. 内置组件:Vue 3 提供的 <component><Transition>
  5. 路由组件:Vue Router 提供的 <router-view><router-link>
  6. 动态组件:通过 <component :is="..."> 动态渲染的组件
  7. 异步组件:通过 defineAsyncComponent() 定义的组件
  8. 函数式组件:通过函数定义的渲染函数组件
  9. 全局组件:在应用级别注册的组件
  10. 渲染函数组件:在 <script> 中使用 h() 函数定义的组件

核心要点

简单的识别方法

在 Vue 模板中,不是标准 HTML 标签的元素,基本上都是组件。标准 HTML 标签包括:divspanpabuttoninput 等约 100 个元素。其他自定义标签都是组件。

通过理解这些概念,您就能准确地识别 Vue 3 单文件中的各种组件,并正确地使用它们来构建应用。

到此这篇关于Vue3 单文件中不同的组件的文章就介绍到这了,更多相关Vue3 单文件组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文