什么是组件

在 Vue 项目中,组件的核心作用是 拆分 UI 逻辑,提高代码复用性。组件 (Component) 是 Vue.js 最强大的功能之一,它允许开发者将 UI 拆分成独立的、可复用的部分,每个组件都封装了自己的 HTML、CSS 和 JavaScript 逻辑(它是html、css、js等的一个聚合体,),在vue中表现为一个自定义的component标签。组件不仅有助于代码复用,还能提高可维护性和开发效率,封装性和隔离性非常强。

一个 Vue 组件通常由三部分组成:

  • <template> 定义组件的结构(HTML)
  • <script>定义组件的逻辑(JavaScript)
  • <style>定义组件的样式(CSS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<!-- 组件的 HTML 结构 -->
<div>
<h1>{{ message }}</h1>
<button @click="changeMessage">点击我</button>
</div>
</template>

<script>
export default {
data() {
return {
message: "Hello, Vue!"
};
},
methods: {
changeMessage() {
this.message = "点击按钮!";
}
}
};
</script>

<style scoped>
/* 组件的样式 */
h1 {
color: blue;
}
</style>

组件的使用

Vue 组件的使用流程一般分为以下几个步骤:

  1. 创建组件,一个单独的**.vue文件
  2. 注册组件,有不同的注册方法
  3. 在模板中使用组件(template),把组件当做普通的html标签来用就可以了,如<Header></Header>
  4. 组件间通信(props / emit / Vuex 等)
  5. 组件生命周期管理

组件注册

在 Vue 中,组件必须先注册才能在模板中使用,注册方式分为:

  1. 全局注册:全局注册的组件可以在 任何组件 中直接使用:
  2. 局部注册:组件只能在注册它的组件中使用

(1) 全局注册

main.js 中注册组件:

1
2
3
4
5
6
7
import { createApp } from 'vue'
import App from './App.vue'
import MyComponent from './components/MyComponent.vue' //引入组件

const app = createApp(App)
app.component('MyComponent', MyComponent)
app.mount('#app')

然后在 App.vue 或其他组件中使用:

1
<MyComponent />

(2) 局部注册

1
2
3
4
5
6
7
8
9
<script>
import MyComponent from './components/MyComponent.vue';

export default {
components: {
MyComponent
}
};
</script>

然后在 template 中使用:

1
<MyComponent />

组件间通信

为什么要组件间通信?

组件中的数据来自哪里?接口。

如果每个子组件都向接口请求数据,那么当组件很多时,发送的数据请求就越来,就会给后端带来负担,并且前端响应加载或者网页打开的速度也比较慢,可以一次性将所有数据都发送给父组件,然后由组件间通信进行数据分发,能有效减少请求。

为什么让父组件统一获取数据?

在开发环境中,通常由父组件统一获取数据,然后通过**props 传递给子组件,这样可以集中管理数据,提高可维护性。父组件请求 API,数据传递给多个子组件**。图片数据也能传的。通过URL或者Base64编码传值。

  1. 数据统一管理:避免多个子组件各自请求数据,导致代码冗余、数据不一致。
  2. 提高性能:减少不必要的网络请求(多个子组件请求可能会重复请求相同的数据)。
  3. 方便数据共享:如果多个子组件都需要相同的数据,父组件可以一次请求后分发,提高复用性。

实际开发时,组件往往不是独立工作的,它们需要相互传递数据和触发事件,比如:

  • 父组件需要向子组件传递数据(如表单的默认值)
  • 子组件需要向父组件反馈信息(如用户的输入)
  • 多个兄弟组件之间需要共享数据(如购物车商品数量)

举个例子: 假设在开发一个 餐厅点餐系统,包含以下界面:

  1. 菜单列表(父组件):展示多个菜品
  2. 单个菜品(子组件):显示菜品具体信息,并允许用户点菜
  3. 购物车(父组件):展示已点的菜品和总价

需求

  • 用户点击 “点餐” 按钮后,购物车(另一个组件)需要实时更新
  • 菜品信息是由 菜单列表 传递给 单个菜品组件
  • 当购物车更新时,菜单界面不需要重新渲染整个列表

流程

父组件给子组件传值

假设父组件TodoList.vue代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
<h2>任务列表</h2>
<TodoItem :task="taskContent" />
</div>
</template>

<script>
import TodoItem from "./TodoItem.vue";

export default {
components: { TodoItem },
data() {
return {
taskContent: "学习 Vue 组件" // 任务内容
};
}
};
</script>

子组件TodoItem.vue

1
2
3
4
5
6
7
8
9
<template>
<li>任务:{{ task }}</li>
</template>

<script>
export default {
props: ["task"] // 接收来自父组件的 `task`
};
</script>

(1)父组件在子组件标签上绑定数据,假设子组件为TodoItem ,则绑定方法如下

<TodoItem :task="taskContent" />

(2)子组件使用 props 接收数据

1
2
3
4
5
6
7
8
9
<script>
export default {
props: ["task"] //子组件接受父组件传来的数据,数组形式
props: { //对象形式
task: String, //可以在 props 里指定数据类型,避免数据格式错误
default: "默认任务" //如果父组件没有传递值,可以设置默认值:
}
};
</script>

(3)子组件在模板中直接使用父组件传来的 props 数据

1
2
3
<template>
<li>{{ task }}</li>
</template>

props 可以是对象或数组,如步骤(2)所示

流程

子组件给父组件传值

子组件向父组件传值通常使用 事件机制($emit,即 子组件触发事件,父组件监听事件。一般是点击,或者出发别的事件。

子组件:ChildComponent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<button @click="sendData">点击传值</button>
</template>

<script>
export default {
methods: {
sendData() {
this.$emit("custom-event", "子组件传来的数据");
}
}
};
</script>

父组件:ParentComponent.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div>
<ChildComponent @custom-event="handleChildData" />
<p>父组件收到数据:{{ receivedData }}</p>
</div>
</template>

<script>
import ChildComponent from "./ChildComponent.vue";

export default {
components: { ChildComponent },
data() {
return {
receivedData: "" // 存储子组件传来的数据
};
},
methods: {
handleChildData(data) {
this.receivedData = data; // 更新父组件的数据
console.log("父组件收到数据:", data);
}
}
};
</script>

(1)子组件使用 $emit 触发事件,并传递数据

<button @click="$emit('custom-event', '我是子组件的数据')">点击传值</button>

(2)父组件:监听子组件事件,并获取数据

<ChildComponent @custom-event="handleChildData" />

(3)父组件的方法接收子组件的数据

1
2
3
4
5
methods: {
handleChildData(data) {
console.log("收到子组件的数据:", data);
}
}

流程2

兄弟组件之间传值

兄弟组件不能直接通信,常见的方式有:

  1. 使用父组件作为桥梁(推荐)
  2. 使用事件总线(Vue 2)
  3. 使用全局状态管理(Vuex/Pinia)

总结3

Vue组件引用顺序

import 语句的顺序在 Vue 应用中是有讲究的。

  • 基础框架必须最先引入:这是最基础的,因为后续所有的插件和组件都依赖于 Vue 核心功能。import { createApp } from "vue";
  • 插件和其样式:先引入插件本身,然后是插件的样式文件,这确保了样式能够正确覆盖默认样式
1
2
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
  • 图标库依赖于 Element Plus,所以要在 Element Plus 之后引入

import * as ElementPlusIconsVue from "@element-plus/icons-vue";

  • 最后引入应用的主组件,因为它可能使用到前面引入的所有依赖。

import App from "./App.vue";

引用顺序


使用子组件组合成web页面

我们对网站最朴素的理解,应该是一个页面对应于一个html文件,从某一个页面跳转到另一个页面,就是从一个html跳转到另一个html,这确实是js、jq时代,前端的跳转逻辑。

但是现在vue框架里只有一个html——index.html,所有的页面内容,都以js的方式,插入到了这个index.html页面中。现在只有一个html文件了,那么我们如何实现在点击某个按钮时,跳转到另一个页面?要跳转页面的名称是什么?地址是什么?需不需要传什么参数过去?这所有的操作现在都需要由js来完成,而这部分功能封装成一个插件就是router,需要使用 Vue Router 来实现页面之间的跳转。

Vue.js 是一个基于组件的框架,整个用户界面都是由一个个可复用的组件构成的。 在 Vue Router 中,将每一个路由(例如 /register)都映射到一个特定的组件。当用户访问这个路由时,Vue Router 会渲染对应的组件,通过路由导航到的“页面”通常都是由一个或多个 Vue 组件构成的,即一个展示页面也是一个组件,而不是一个html。

多页面跳转

当需要实现多页面跳转时,就不能再像预览单个组件那样直接在 App.vue 中渲染所有内容了。需要使用 Vue Router 来管理你的页面和组件,并实现自由的路由。

首先,需要将想要作为独立页面的内容封装成一个个单独的 .vue 组件。例如,如果有首页、关于页、注册页,需要创建 HomePage.vueAboutUsPage.vueRegisterPage.vue 等文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
假设将 `LoginForm.vue` 和 `TypingEffect.vue` 这两个子组件组合到了一个新的 `LoginPage.vue` 组件中。这个 `LoginPage.vue` 组件在视觉上呈现了我想要的登录页面的效果,就像一个完整的 HTML 页面一样。但是,本质上 `LoginPage.vue` 仍然是一个 Vue 组件。

通过将 LoginForm 和 TypingEffect 组合到 LoginPage.vue 中,创建了一个更高层次的组件,它负责渲染整个登录页面的内容和布局。然后,可以将 LoginPage.vue 这个组件交给 Vue Router 来管理,当用户访问特定的路由时,Vue Router 就会渲染这个 LoginPage.vue 组件,从而显示出我的登录页面。

# 推荐目录结构如下,views/ (或者 pages/)这个目录下存放的是构成应用不同页面的组件。这些组件通常会引入并使用 components/ 目录下的通用组件,将它们组合成一个完整的页面。LoginPage.vue 应该放在这里。

├── public/
├── src/
│ ├── assets/ # 静态资源 (图片、字体等)
│ ├── components/ # 通用、可复用的 UI 组件 (例如:按钮、输入框、模态框、你的 LoginForm 和 TypingEffect)
│ ├── layouts/ # 应用的布局组件 (例如:包含页头、侧边栏、页脚的布局)
│ ├── router/ # 路由配置
│ ├── store/ # Vuex 或 Pinia 状态管理 (如果使用)
│ ├── services/ # 与后端 API 交互的服务
│ ├── utils/ # 工具函数
│ ├── views/ # 页面级组件 (通常由多个通用组件组合而成,例如:LoginPage、 HomePage、RegisterPage等)
│ ├── App.vue # 根组件
│ ├── main.js # 应用入口文件
│ └── ...
├── .gitignore
├── package.json
└── ...

然后配置路由并定义路由,将不同的 URL 路径映射到创建的页面组件。

最后在 App.vue 中使用 <router-view>,需要使用 <router-view> 组件来告诉 Vue Router 在哪里渲染当前路由匹配到的组件。

通过以上步骤,就可以将你的组件组合成不同的页面,并实现自由的路由和页面之间的跳转了。

参考文献:vue基础教程(5)——十分钟吃透vue路由router

组合页面