uni-app组件通信完全指南:从基础到进阶 0 次阅读

组件通信是uni-app开发中的重要环节,选择合适的通信方式对提升代码质量和开发效率至关重要。本文将详细介绍各种组件通信方案的使用场景和最佳实践。

1. 基础通信方式

1.1 Props/Emit

父组件向子组件传值:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!-- 父组件 -->
<template>
<child-component
:title="title"
:list="dataList"
@on-click="handleClick"
/>
</template>

<script>
export default {
data() {
return {
title: '标题',
dataList: ['item1', 'item2']
}
},
methods: {
handleClick(data) {
console.log('子组件点击事件:', data)
}
}
}
</script>

<!-- 子组件 -->
<template>
<view>
<text>{{title}}</text>
<view
v-for="(item, index) in list"
:key="index"
@click="onClick(item)"
>
{{item}}
</view>
</view>
</template>

<script>
export default {
props: {
title: {
type: String,
default: ''
},
list: {
type: Array,
default: () => []
}
},
methods: {
onClick(item) {
this.$emit('on-click', item)
}
}
}
</script>

1.2 Provide/Inject

跨层级组件通信:

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
31
32
33
<!-- 父组件 -->
<script>
export default {
provide() {
return {
theme: this.theme,
updateTheme: this.updateTheme
}
},
data() {
return {
theme: 'light'
}
},
methods: {
updateTheme(newTheme) {
this.theme = newTheme
}
}
}
</script>

<!-- 子组件(任意层级) -->
<script>
export default {
inject: ['theme', 'updateTheme'],
methods: {
toggleTheme() {
this.updateTheme(this.theme === 'light' ? 'dark' : 'light')
}
}
}
</script>

2. 进阶通信方案

2.1 EventBus

适用于任意组件间通信:

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
31
32
33
// utils/event-bus.js
class EventBus {
constructor() {
this.events = {}
}

on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(callback)
}

emit(eventName, data) {
if (this.events[eventName]) {
this.events[eventName].forEach(callback => {
callback(data)
})
}
}

off(eventName, callback) {
if (this.events[eventName]) {
if (callback) {
this.events[eventName] = this.events[eventName].filter(cb => cb !== callback)
} else {
delete this.events[eventName]
}
}
}
}

export default new EventBus()

使用示例:

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
31
<!-- 组件A -->
<script>
import eventBus from '@/utils/event-bus'

export default {
methods: {
sendMessage() {
eventBus.emit('message', { type: 'success', content: '操作成功' })
}
}
}
</script>

<!-- 组件B -->
<script>
import eventBus from '@/utils/event-bus'

export default {
mounted() {
eventBus.on('message', this.handleMessage)
},
beforeDestroy() {
eventBus.off('message', this.handleMessage)
},
methods: {
handleMessage(data) {
console.log('收到消息:', data)
}
}
}
</script>

2.2 Vuex状态管理

复杂组件通信推荐使用:

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
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
userInfo: null,
cartList: []
},
mutations: {
setUserInfo(state, info) {
state.userInfo = info
},
addToCart(state, item) {
state.cartList.push(item)
}
},
actions: {
async updateUserInfo({ commit }) {
const info = await getUserInfo()
commit('setUserInfo', info)
}
},
getters: {
cartCount: state => state.cartList.length
}
})

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 组件中使用 -->
<script>
import { mapState, mapMutations, mapActions } from 'vuex'

export default {
computed: {
...mapState(['userInfo', 'cartList']),
...mapGetters(['cartCount'])
},
methods: {
...mapMutations(['setUserInfo', 'addToCart']),
...mapActions(['updateUserInfo']),
async init() {
await this.updateUserInfo()
}
}
}
</script>

3. 特殊场景处理

3.1 跨页面通信

1
2
3
4
5
6
7
8
9
10
11
12
13
// 页面A
uni.$on('pageMessage', this.handleMessage)
uni.$emit('pageMessage', { data: 'hello' })

// 页面B
uni.$once('pageMessage', data => {
console.log('收到一次性消息:', data)
})

// 注意在页面销毁时解除监听
onUnload() {
uni.$off('pageMessage', this.handleMessage)
}

3.2 组件ref通信

直接访问组件实例:

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
<!-- 父组件 -->
<template>
<child-component ref="child" />
<button @click="callChildMethod">调用子组件方法</button>
</template>

<script>
export default {
methods: {
callChildMethod() {
this.$refs.child.someMethod()
}
}
}
</script>

<!-- 子组件 -->
<script>
export default {
methods: {
someMethod() {
console.log('子组件方法被调用')
}
}
}
</script>

4. 最佳实践建议

4.1 选择合适的通信方式

  1. 父子组件:优先使用props/emit
  2. 跨层级组件:考虑provide/inject
  3. 复杂数据管理:使用Vuex
  4. 简单全局通信:使用EventBus
  5. 临时跨页面:使用uni.$emit/$on

4.2 性能优化

  1. 避免过度使用EventBus
  2. 及时解除事件监听
  3. Vuex中合理划分模块
  4. 减少不必要的数据监听

5. 常见问题

5.1 数据同步问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 父组件数据变化子组件不更新
export default {
props: {
list: {
type: Array,
default: () => []
}
},
watch: {
list: {
handler(newVal) {
// 深度监听数据变化
this.handleListChange(newVal)
},
deep: true
}
}
}

5.2 事件解绑问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default {
data() {
return {
handlers: []
}
},
methods: {
addEventHandler(handler) {
this.handlers.push(handler)
eventBus.on('someEvent', handler)
}
},
beforeDestroy() {
// 统一解绑事件
this.handlers.forEach(handler => {
eventBus.off('someEvent', handler)
})
}
}

6. 总结

  1. 了解各种通信方式的特点
  2. 根据场景选择合适的方案
  3. 注意性能和内存问题
  4. 做好事件解绑和销毁
  5. 保持代码的可维护性

如果觉得文章对你有帮助,欢迎点赞、评论、分享,你的支持是我继续创作的动力!

上一篇 uni-app性能优化实战:从加载到渲染的全方位提升
下一篇 uni-app数据存储全攻略:从本地存储到持久化方案
感谢您的支持!
微信赞赏码 微信赞赏
支付宝赞赏码 支付宝赞赏