一、父子组件通信
1. Props(父→子)
父组件通过props向子组件传递数据,子组件需要显式声明接收的props
dart
// 父组件
<ChildComponent :title="pageTitle" :content="pageContent" />
// 子组件
props: {
title: String, // 基础类型检查
content: { // 高级配置
type: String,
required: true,
default: 'Default content'
}
}
2. 自定义事件(子→父)
子组件通过$emit
触发事件,父组件通过v-on
监听
typescript
// 子组件
methods: {
submitForm() {
this.$emit('form-submit', {
username: this.username,
password: this.password
})
}
}
// 父组件
<ChildComponent @form-submit="handleSubmit" />
methods: {
handleSubmit(formData) {
// 处理表单数据
}
}
3. style和class
父组件传递的样式和类会自动合并到子组件根元素
javascript
// 父组件
<ChildComponent
class="theme-dark"
style="margin: 10px; padding: 20px;"
/>
// 子组件渲染结果
<div class="child-component theme-dark" style="margin: 10px; padding: 20px;">
<!-- 子组件内容 -->
</div>
4. attribute(非Prop特性)
attribute(非Prop特性)
是指父组件传递给子组件,但子组件没有在props
中显式声明的属性。这些特性会自动绑定到子组件的根元素上。
javascript
// 父组件
<ChildComponent data-id="123" aria-label="Description" />
// 子组件(未在props中声明这些属性)
<div data-id="123" aria-label="Description">
<!-- 子组件内容 -->
</div>
-
this.$attrs
访问 : 在子组件中,可以通过this.$attrs
访问所有非Prop特性:javascriptmounted() { console.log(this.$attrs) // 包含所有未在props中声明的属性 }
-
使用场景:
- 当需要将多个属性批量传递给子组件时
- 创建高阶组件(HOC)时传递未知属性
- 需要手动控制属性绑定位置时
-
禁用自动继承 : 在子组件选项中设置
inheritAttrs: false
可以禁用自动绑定到根元素:javascriptexport default { inheritAttrs: false, // ... }
-
手动绑定示例:
javascript<template> <div> <input v-bind="$attrs" /> </div> </template>
-
包含的特性:
- 普通HTML属性(如
id
,class
,data-*
等) - 自定义属性(非props声明的)
- 事件监听器(可通过
$listeners
单独访问)
- 普通HTML属性(如
javascript
// 父组件
<ChildComponent
data-id="123"
aria-label="description"
custom-attr="value"
/>
// 子组件
export default {
props: ['customAttr'], // 显式声明customAttr
mounted() {
console.log(this.$attrs)
// 输出: { "data-id": "123", "aria-label": "description" }
// customAttr不会出现在$attrs中,因为它已在props中声明
}
}
5. $listeners
包含父组件中所有v-on事件监听器(不含.native修饰符)
typescript
// 父组件
<ChildComponent @focus="handleFocus" @input="handleInput" />
// 子组件
<template>
<div>
<input v-on="$listeners" />
</div>
</template>
6. v-model(双向绑定)
语法糖,相当于value属性+input事件
javascript
// 父组件
<ChildComponent v-model="message" />
// 等价于
<ChildComponent :value="message" @input="message = $event" />
// 子组件实现
props: ['value'],
methods: {
updateValue(newVal) {
this.$emit('input', newVal)
}
}
7. .sync修饰符
双向绑定的语法糖(Vue 2.3+)
kotlin
// 父组件
<ChildComponent :title.sync="doc.title" />
// 子组件更新
this.$emit('update:title', newTitle)
8. parent 和
children
直接访问父/子实例(慎用)
kotlin
// 子组件访问父组件方法
this.$parent.parentMethod()
// 父组件访问第一个子组件
this.$children[0].childMethod()
9. slots 和
scopedSlots
内容分发相关API
javascript
// 具名插槽
<template v-slot:header>
<h1>标题</h1>
</template>
// 作用域插槽
<template v-slot:item="slotProps">
<span>{{ slotProps.item.text }}</span>
</template>
11. ref
给子组件添加引用标识后访问
javascript
// 父组件
<ChildComponent ref="childComp" />
methods: {
callChildMethod() {
this.$refs.childComp.childMethod()
}
}
二、跨组件通信
1. Provide 和 Inject
祖先组件提供数据,后代组件注入
javascript
// 祖先组件
provide() {
return {
appContext: this.appData,
changeContext: this.updateContext
}
}
// 后代组件
inject: ['appContext', 'changeContext'],
methods: {
updateData() {
this.changeContext(newData)
}
}
2. router(路由参数)
通过路由传递参数
php
// 传递参数
this.$router.push({
name: 'user',
params: { userId: '123' },
query: { token: 'abc' }
})
// 获取参数
const userId = this.$route.params.userId
const token = this.$route.query.token
3. Vuex(状态管理)
集中式状态管理
javascript
// store定义
const store = new Vuex.Store({
state: { count: 0 },
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
})
// 组件中使用
computed: {
count() {
return this.$store.state.count
}
},
methods: {
increment() {
this.$store.commit('increment')
},
incrementAsync() {
this.$store.dispatch('incrementAsync')
}
}
4. store模式(简单状态管理)
小型应用替代Vuex的简单方案
javascript
// store.js
export const store = {
debug: true,
state: {
user: null
},
setUser(newUser) {
if (this.debug) console.log('setUser triggered with', newUser)
this.state.user = newUser
}
}
// 组件中使用
import { store } from './store'
computed: {
currentUser() {
return store.state.user
}
},
methods: {
updateUser(user) {
store.setUser(user)
}
}
5. Event Bus(事件总线)
创建中央事件总线进行组件间通信
javascript
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 组件A发送事件
import { EventBus } from './event-bus'
EventBus.$emit('notification', { message: 'Hello!' })
// 组件B接收事件
import { EventBus } from './event-bus'
created() {
EventBus.$on('notification', (payload) => {
console.log(payload.message) // 'Hello!'
})
},
// 组件销毁前移除监听
beforeDestroy() {
EventBus.$off('notification')
}
三、通信方式选择指南
场景 | 推荐方式 | 备注 |
---|---|---|
简单父子通信 | Props/Events | 最基础、最常用的方式 |
父子双向绑定 | v-model/.sync | 表单类组件常用 |
跨层级传递数据 | Provide/Inject | 适合组件库开发 |
全局状态管理 | Vuex | 中大型项目首选 |
临时全局事件 | Event Bus | 简单场景使用,注意销毁监听 |
路由相关数据 | 路由参数 | 页面间传递数据 |
简单状态共享 | Store模式 | 小型项目替代Vuex |
四、最佳实践建议
-
优先使用Props/Events:保持组件独立性
-
避免过度使用
parent/
children:会破坏组件封装性 -
Vuex使用原则:
- 多个组件共享的状态才放入Vuex
- 组件私有状态应保留在组件内部
-
Event Bus注意事项:
- 事件名建议使用常量
- 组件销毁前务必移除监听
-
Provide/Inject适用场景:
- 组件库开发
- 深层嵌套组件通信
- 普通业务代码慎用