← 返回目录

第10课:组件通信

课程概述

本课程将深入讲解UniApp/Vue.js中的组件通信机制,包括父子组件通信、跨组件通信、事件总线、provide/inject等多种通信方式。掌握组件通信技术对于构建复杂应用至关重要。

1. 父子组件通信

父子组件通信是最常见的组件通信方式,父组件通过props向子组件传递数据,子组件通过$emit向父组件触发事件。

1.1 父传子:Props

父组件通过props向子组件传递数据,子组件通过props选项接收数据。







1.2 子传父:$emit

子组件通过$emit触发自定义事件,父组件通过v-on监听事件。







1.3 Props验证

Vue提供了完整的props验证机制,可以确保组件接收到的数据符合预期。

export default {
    props: {
        basicProp: String,
        multipleTypes: [String, Number],
        requiredProp: {
            type: String,
            required: true
        },
        defaultProp: {
            type: Number,
            default: 100
        },
        objectDefault: {
            type: Object,
            default: () => ({
                name: '默认值',
                age: 0
            })
        },
        customValidator: {
            type: String,
            validator: function(value) {
                return ['success', 'warning', 'danger'].includes(value)
            }
        },
        asyncProp: {
            type: Function,
            default: function() {
                return Promise.resolve()
            }
        }
    }
}
注意:

2. provide/inject

provide/inject用于跨层级组件通信,祖先组件通过provide提供数据,后代组件通过inject注入数据。

2.1 基本用法







2.2 响应式provide/inject

默认情况下,provide/inject不是响应式的。如果需要响应式,可以使用Vue的reactive或ref。





提示: provide/inject适用于以下场景:

3. 事件总线(Event Bus)

事件总线是一种发布-订阅模式的通信方式,适用于任意组件之间的通信。

3.1 创建事件总线

// utils/eventBus.js
import Vue from 'vue'

class EventBus {
    constructor() {
        this.events = {}
    }

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

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

    emit(event, payload) {
        if (!this.events[event]) return
        
        this.events[event].forEach(callback => {
            callback(payload)
        })
    }

    once(event, callback) {
        const onceCallback = (payload) => {
            callback(payload)
            this.off(event, onceCallback)
        }
        this.on(event, onceCallback)
    }

    clear() {
        this.events = {}
    }
}

export default new EventBus()

3.2 使用事件总线










3.3 使用Vue实例作为事件总线

// main.js
import Vue from 'vue'
import App from './App'

Vue.prototype.$eventBus = new Vue()

new Vue({
    render: h => h(App)
}).$mount('#app')

注意:

4. $refs和$parent

$refs用于直接访问子组件实例,$parent用于访问父组件实例。

4.1 使用$refs访问子组件







4.2 使用$parent访问父组件







注意:

5. 插槽通信

插槽是Vue提供的一种内容分发机制,也可以用于组件通信。

5.1 作用域插槽







6. Vuex状态管理

Vuex是Vue的官方状态管理库,适用于大型应用的复杂状态管理。

6.1 基本结构

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        count: 0,
        userInfo: null,
        todos: []
    },
    mutations: {
        INCREMENT(state) {
            state.count++
        },
        SET_USER(state, userInfo) {
            state.userInfo = userInfo
        },
        ADD_TODO(state, todo) {
            state.todos.push(todo)
        },
        REMOVE_TODO(state, todoId) {
            state.todos = state.todos.filter(todo => todo.id !== todoId)
        }
    },
    actions: {
        increment({ commit }) {
            commit('INCREMENT')
        },
        async fetchUser({ commit }, userId) {
            const res = await uni.request({
                url: `/api/users/${userId}`
            })
            commit('SET_USER', res.data)
        },
        addTodo({ commit }, todo) {
            commit('ADD_TODO', todo)
        },
        removeTodo({ commit }, todoId) {
            commit('REMOVE_TODO', todoId)
        }
    },
    getters: {
        doubleCount: state => state.count * 2,
        completedTodos: state => state.todos.filter(todo => todo.completed),
        todoCount: state => state.todos.length
    },
    modules: {
        user: {
            namespaced: true,
            state: {
                profile: null,
                settings: {}
            },
            mutations: {
                SET_PROFILE(state, profile) {
                    state.profile = profile
                }
            },
            actions: {
                fetchProfile({ commit }) {
                    // 获取用户资料
                }
            }
        }
    }
})

6.2 在组件中使用Vuex




7. 实际应用案例

7.1 购物车组件通信







7.2 表单验证通信







8. 通信方式对比

通信方式 适用场景 优点 缺点
Props/$emit 父子组件通信 简单直接,数据流清晰 跨层级传递繁琐
provide/inject 跨层级组件通信 避免props逐层传递 不是响应式,难以追踪
事件总线 任意组件通信 灵活,解耦 难以维护,容易混乱
$refs/$parent 直接访问组件实例 可以直接调用方法 破坏封装性
Vuex/Pinia 复杂状态管理 集中管理,易于调试 学习成本高,可能过度设计

9. 最佳实践

组件通信最佳实践:

10. 课程总结

本课程详细讲解了UniApp/Vue.js中的组件通信机制,包括:

掌握组件通信技术后,你可以构建更加灵活和可维护的应用架构。下一课我们将学习状态管理的相关知识。