likes
comments
collection
share

微信小程序:如何做到爷孙组件通讯

作者站长头像
站长
· 阅读数 3

背景

我们公司项目使用的是原生微信小程序,很多东西都逐渐组件化,但存在多层嵌套或者需要监听A一个变量的变化=> 触发B、C组件进行相应的事件。。。

例子:一个很深层级的Tabs组件,Tab的点击触发页面滚动到最顶部,一个按钮组件点击也回到最顶部。

核心思路

我们如果经常使用Vue做B端开发的帅哥会想到以下常见的解决方案

微信小程序:如何做到爷孙组件通讯

状态管理

常见的状态管理有ReduxVuexMobX

ReduxVuexMobX
语言主要与React结合使用,但也可用于其他框架。针对Vue.js。不与特定框架绑定,可用于React、Vue等。
工作原理单一不可变的状态树,通过纯函数来处理状态变化。基于Flux架构,有状态、变更和行为三个核心概念。基于可观察的数据结构,状态可直接变更。
异步异步操作需要使用中间件(例如Redux Thunk)。使用actions处理异步操作。内置支持异步操作。
严格性严格的单向数据流,更多的概念,如actions、reducers和store。严格单向数据流,但比Redux更简单。松散的单向数据流,更自由。
可预测性可追踪、可重现的状态变化。易于追踪和调试。相对较灵活,可能需要更多的注意力。
学习曲线相对较高,需要理解一些概念。相对较低,特别适用于Vue.js项目。相对较低,特别适用于那些希望更自由的状态管理方式的开发者。
适用项目大型项目,需要严格的状态管理和可维护性.中小型项目,需要一种简单而直观的状态管理.中小型项目或者需要跨框架的项目,注重简洁性和灵活性.

微信小程序原生可以采取Mobx,有一个专门的第三方库mobx-miniprogram(点击查看使用方法(后续可能会出一章专门讲这个的文章)。

在这里有一种大材小用的感觉,还增加了项目的大小深受小程序包大小限制的苦dddd,当然还有一种最简单的状态管理,利用小程序的全局app.js,但可能不利于后续维护且不适用于复杂的状态管理。

provide/inject API

在Vue.js中,provideinject 是一对用于父组件向子组件传递数据的高级选项。它们的原理基于 Vue.js 的依赖注入系统,允许父组件在组件树中提供数据,而子组件可以通过 inject 选项来接收这些数据。

原理简述

  1. provide 的作用

    • 在父组件中,通过 provide 选项提供数据
    • provide 选项的值可以是对象或一个函数,返回一个对象。这个对象中包含需要传递给子组件的数据。
    // ParentComponent.vue
    export default {
      provide() {
        return {
          providedData: this.someData,
        };
      },
      data() {
        return {
          someData: 'Hello from Parent',
        };
      },
    };
    
  2. inject 的作用

    • 在子组件中,通过 inject 选项来接收从父组件提供的数据。
    • inject 的值可以是一个数组,包含需要注入的属性名,也可以是一个对象,指定属性名和对应的默认值。
    // ChildComponent.vue
    export default {
      inject: ['providedData'],
      mounted() {
        console.log(this.providedData); // Accessing the provided data
      },
    };
    

原理解析

  • 当子组件需要注入数据时,Vue.js 在组件的 inject 选项中查找提供该数据的所有父级组件。
  • 如果找到匹配的父级组件,子组件将接收到那个父级组件 provide 选项中对应的数据。
  • 如果没有找到匹配的父级组件,子组件将按照 inject 选项中定义的默认值来处理。

通过这种方式,Vue.js 实现了一种非常灵活的数据传递机制,允许在组件树中的任意层次进行数据的提供和接收。这也使得组件之间的耦合度降低,同时增加了灵活性。


小程序应用

在微信小程序中,并没有类似 Vue.js 中 provideinject 的内置机制,通常涉及到 全局数据管理事件订阅发布模式

以下是一个简单的示例,但主要适用于静态的全局变量:

  1. 创建一个全局数据管理模块(globalData.js)
// globalData.js

const dataStore = {};

export function provide(key, value) {
  dataStore[key] = value;
}

export function inject(key) {
  return dataStore[key];
}
  1. 在需要提供数据的组件中使用 provide
// pages/index/index.js

import { provide } from '../../utils/globalData';

Page({
  onLoad: function () {
    provide('providedData', 'Hello from Index');
  },
});
  1. 在需要使用数据的组件中使用 inject:
// pages/child/child.js

import { inject } from '../../utils/globalData';

Page({
  onLoad: function () {
    const providedData = inject('providedData');
    console.log(providedData); // Accessing the provided data
  },
});

发布订阅模式

发布订阅模式是一种设计模式,用于处理对象之间的松散耦合,其中一个对象(发布者或主题)发布事件,而其他对象(订阅者)订阅这些事件。

原理解析

  1. 发布者(Subject/Publisher) 负责维护事件的注册和通知订阅者的机制。
  2. 订阅者(Observer/Subscriber) 注册对特定事件的兴趣,并在事件发生时得到通知。
  3. 事件(Event) 表示发布者发生的某个动作或状态改变。
  4. 事件通道(Event Channel) 是发布者和订阅者之间的通信媒介,用于传递事件信息。

原谅我的懒惰,不想用其他软件画了,讲究看吧。。。

订阅者3
订阅者2
订阅者1
发布者
发布者: 创建主题
发布者: 增加订阅者
发布者: 通知订阅者
接收通知
接收通知
接收通知
开始
结束

自定义实现

  1. 定义一个事件发布类 定义一个类,而不是一个Maps,是防止出现不同页面之间变量或事件的冲突。
class EventEmitter {
  static callbacks = {}

  constructor(id) {
    this.id = id
  }

  on(event, callback) {
    if (!EventEmitter.callbacks[event]) {
      EventEmitter.callbacks[event] = []
    }
    EventEmitter.callbacks[event].push(callback)
  }

  emit(event, data) {
    if (EventEmitter.callbacks[event]) {
      EventEmitter.callbacks[event].forEach((callback) => {
        callback(data)
      })
    } else {
      console.error('EventEmitter: no event named ' + event + ' found')
    }
  }
}


export default EventEmitter

2.订阅事件

const emitter = new EventEmitter('PageName')
emitter.on('event', (data) => { console.log('Event received:', data); });

3.触发事件

const emitter = new EventEmitter('PageName')
emitter.emit('event', { message: 'Hello, mitt!' });

适用第三方库mitt

mitt 是一个简单且灵活的事件总线库,属于发布-订阅模式中的一种。它提供了一个基于 JavaScript 事件机制的轻量级实现,用于处理事件的订阅和发布。通过 on 方法订阅事件,通过 emit 方法触发事件。

import mitt from 'mitt';

// 创建事件总线
const emitter = mitt();

// 订阅事件
emitter.on('event', (data) => {
  console.log('Event received:', data);
});

// 触发事件
emitter.emit('event', { message: 'Hello, mitt!' });

相比采用类的静态属性和方法,而 mitt 库则更倾向于通过实例化来创建事件总线对象。同时mitt 库通过每个实例来维护事件回调,从而避免了这个问题。每个事件总线实例都有自己的回调存储,确保了更好的隔离性。

如果你需要更多的特性、更好的隔离性和更丰富的错误处理,可以考虑使用成熟的库,如 mitt

总结

状态管理(MobX)模仿Vue的provide/inject API发布订阅模式
优点- 可以在全局状态中存储数据。- 提供响应式状态变化。- 适合大型应用。- 轻量级的组件通信方式。- 易于在组件树中传递数据。- 提供松散耦合的通信方式。- 支持组件相互独立。
缺点- 引入可能较繁琐。- 学习曲线较高。- 灵活性可能不如全局状态管理。- 需要手动模拟 provide/inject。- 需要额外的事件系统。- 事件追踪可能较难。
适用场景- 复杂的状态逻辑。- 需要全局响应式状态。- 小型应用。- 轻量级通信需求。- 组件解耦。- 灵活通信需求。