Vue2 组件通信与事件处理深度指南

This commit is contained in:
bunny 2025-06-21 22:46:40 +08:00
parent e6e280d8f1
commit f2b0e74cd2
4 changed files with 269 additions and 3 deletions

View File

@ -8,7 +8,8 @@
<!-- <Demo4 /> -->
<!-- <Demo6 /> -->
<!-- <Todo /> -->
<Demo8 />
<!-- <Demo8 /> -->
<Demo9 />
</div>
</template>
@ -19,11 +20,12 @@
// import Demo4 from "@/views/demo4/index.vue";
// import Demo5 from "@/views/demo5/index.vue";
// import Todo from "@/views/todo/index.vue";
import Demo8 from "@/views/demo8/index.vue";
// import Demo8 from "@/views/demo8/index.vue";
import Demo9 from "@/views/demo9/index.vue";
export default {
name: "App",
// components: { Demo1, Demo2, Demo3 },
components: { Demo8 },
components: { Demo9 },
};
</script>

View File

@ -0,0 +1,186 @@
# Vue2 组件通信与事件处理深度指南
## 一、组件通信核心方式
### 1. 父子组件通信模式
#### Props 向下传递
```javascript
// 父组件
<Child :title="pageTitle" />
// 子组件
props: {
title: String
}
```
#### Events 向上传递
```javascript
// 子组件
this.$emit('update', newValue)
// 父组件
<Child @update="handleUpdate" />
```
### 2. 引用直接访问
```javascript
// 父组件
<Child ref="childRef" />
methods: {
callChildMethod() {
this.$refs.childRef.childMethod()
}
}
```
## 二、事件系统高级用法
### 1. 原生事件绑定
```html
<!-- 必须添加.native修饰符 -->
<StudentInfo @click.native="handleClick" />
```
### 2. 手动事件管理
```javascript
// 绑定事件推荐在mounted钩子中
mounted() {
this.$refs.studenInfo.$on("getStudentName", this.getStudentName)
}
// 解绑事件必须在beforeDestroy中清理
beforeDestroy() {
this.$refs.studenInfo.$off("getStudentName")
}
```
### 3. 事件参数传递
```javascript
// 子组件触发时可带多个参数
this.$emit('event-name', arg1, arg2, ...)
// 父组件接收所有参数
handler(arg1, arg2, ...rest) {
// 处理参数
}
```
## 三、最佳实践建议
### 1. 事件命名规范
- 使用 kebab-case 命名(如 `student-updated`
- 避免与原生事件重名(添加业务前缀)
- 语义化命名(如 `form-submitted`
### 2. 安全通信模式
```javascript
// 添加事件存在性检查
if (this._events['custom-event']) {
this.$emit('custom-event', data)
}
// 使用try-catch包裹可能出错的事件处理
try {
this.$emit('critical-action', payload)
} catch (error) {
console.error('事件处理失败:', error)
}
```
### 3. 性能优化方案
```javascript
// 防抖处理高频事件
methods: {
handleInput: _.debounce(function() {
this.$emit('input-changed', this.value)
}, 300)
}
```
## 四、高级通信场景
### 1. 跨级组件通信
```javascript
// 祖先组件
provide() {
return {
appData: this.sharedData
}
}
// 后代组件
inject: ['appData']
```
### 2. 动态事件处理器
```javascript
// 根据状态切换处理函数
<Child @custom-event="handlerMap[currentHandler]" />
data() {
return {
currentHandler: 'default',
handlerMap: {
default: this.handleDefault,
special: this.handleSpecial
}
}
}
```
### 3. 事件总线模式
```javascript
// event-bus.js
import Vue from 'vue'
export default new Vue()
// 组件A
EventBus.$emit('data-updated', payload)
// 组件B
EventBus.$on('data-updated', handler)
```
## 五、常见问题解决方案
1. **事件未触发排查**
- 检查组件引用是否正确(`ref` 命名)
- 确认事件名称完全匹配(大小写敏感)
- 验证事件绑定时机(确保在 mounted 之后)
2. **内存泄漏预防**
```javascript
beforeDestroy() {
// 清除所有自定义事件监听
this.$off()
// 清除事件总线监听
EventBus.$off('data-updated', this.handler)
}
```
3. **异步事件处理**
```javascript
// 返回Promise的事件处理
async handleEvent() {
try {
await this.$nextTick()
const result = await this.$emitAsync('async-event')
// 处理结果
} catch (error) {
// 错误处理
}
}
```
## 六、与Vue3的对比
| 特性 | Vue2 | Vue3 |
| -------- | -------------- | -------------------- |
| 事件定义 | 隐式声明 | `emits` 选项显式声明 |
| 原生事件 | 需要 `.native` | 自动识别 |
| 移除API | - | 移除 `$on`, `$off` |
| 性能 | 基于观察者 | 基于Proxy的响应式 |

View File

@ -0,0 +1,31 @@
<template>
<div class="card">
<div class="card-header">
<h3>学生姓名{{ studenName }}</h3>
</div>
<div class="card-body">
<div class="btn-group">
<button class="btn btn-primary" @click="onEmitStudentName">
向父组件传递学生姓名
</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: "StudentInfo",
data() {
return {
studenName: "Bunny",
};
},
methods: {
onEmitStudentName() {
this.$emit("getStudentName", this.studenName);
},
},
};
</script>

View File

@ -0,0 +1,47 @@
<template>
<div class="container">
<h4>
学生姓名
<span class="badge text-bg-secondary"> {{ studenName }}</span>
</h4>
<!-- 需要在前面加上native否则会当成自定义事件 -->
<StudentInfo ref="studenInfo" @click.native="showMessage" />
</div>
</template>
<script>
import StudentInfo from "./components/SutdentInfo.vue";
export default {
name: "Demo-9",
components: { StudentInfo },
data() {
return {
studenName: "",
};
},
methods: {
/* 获取学生名 */
getStudentName(name, ...params) {
console.log(name, params);
this.studenName = name;
},
/* 点击子组件显示消息 */
showMessage() {
alert("点击子组件触发点击事件。。。");
},
},
mounted() {
this.$refs.studenInfo.$on("getStudentName", this.getStudentName);
// thisVue
// this.$refs.studenInfo.$on("getStudentName", (name, ...params) => {
// console.log(name, params);
// this.studenName = name;
// });
},
};
</script>