建站教程

建站教程

Products

当前位置:首页 > 建站教程 >

美团组件化事件总线方案改进:ModularEventBus(微信小程序之组件化)

GG网络技术分享 2025-03-18 16:13 0


美团组件化事件总线方案改进:ModularEventBus

前言

大家好,我是小彭。2 年前,我们在 为了组件化改造学习十几家大厂的技术博客 这篇文章里收集过各大厂的组件化方案。其中,有美团收银团队分享的组件化总线框架 modular-event 让我们印象深刻。然而,美团并未将该框架开源,我们只能望梅止渴。

在学习和借鉴美团 modular-event 方案中很多优秀的设计思想后,我亦发现方案中依然存在不一致风险和不足,故我决定对方案进行改进并向社区开源。项目主页为 Github · ModularEventBus,演示 Demo 可直接下载: Demo apk。

欢迎提 Issue 帮助修复缺陷,欢迎提 Pull Request 增加新的 Feature,有用请点赞给 Star,给小彭一点创作的动力,谢谢。


这篇文章是 组件化系列文章第 5 篇,相关 Android 工程化专栏完整文章列表:

一、Gradle 基础:

  • 1、Gradle 基础 :Wrapper、Groovy、生命周期、Project、Task、增量
  • 2、Gradle 插件:Plugin、Extension 扩展、NamedDomainObjectContainer、调试
  • 3、Gradle 依赖管理
  • 4、Maven 发布:SHAPSHOT 快照、uploadArchives、Nexus、AAR
  • 5、Gradle 插件案例:EasyPrivacy、so 文件适配 64 位架构、ABI

二、AGP 插件:

  • 1、AGP 构建过程
  • 2、AGP 常用配置项:Manifest、BuildConfig、buildTypes、壳工程、环境切换
  • 3、APG Transform:AOP、TransformTask、增量、字节码、Dex
  • 4、AGP 代码混淆:ProGuard、R8、Optimize、Keep、组件化
  • 5、APK 签名:认证、完整性、v1、v2、v3、Zip、Wallet
  • 6、AGP 案例:多渠道打包

三、组件化开发:

  • 1、方案积累:有赞、蘑菇街、得到、携程、支付宝、手淘、爱奇艺、微信、美团
  • 2、组件化架构基础
  • 3、ARouter 源码分析
  • 4、组件化案例:通用方案
  • 5、组件化案例:组件化事件总线框架(本文)
  • 6、组件化案例:组件化 Key-Value 框架

四、AOP 面向切面编程:

  • 1、AOP 基础
  • 2、Java 注解
  • 3、Java 注解处理器:APT、javac
  • 4、Java 动态代理:代理模式、Proxy、字节码
  • 5、Java ServiceLoader:服务发现、SPI、META-INF
  • 6、AspectJ 框架:Transform
  • 7、Javassist 框架
  • 8、ASM 框架
  • 9、AspectJ 案例:限制按钮点击抖动

五、相关计算机基础

  • 1、Base64 编码
  • 2、安全传输:加密、摘要、签名、CA 证书、防窃听、完整性、认证

1. 认识事件总线

1.1 事件总线的优点

事件总线框架最大的优点是 ”解耦“,即事件发布者与事件订阅者的解耦,事件的发布者不需要关心是否有人订阅该事件,也不需要关心是谁订阅该事件,代码耦合度较低。因此,事件总线框架更适合作为全局的事件通信方案,或者组件间通信的辅助方案。

1.2 事件总线的缺点

然而,成也萧何败萧何。有人觉得事件总线好用,亦有人觉得事件总线不好用,归根结底还是因为事件总线太容易被滥用了,用时一时爽,维护火葬场。我将事件总线框架存在的问题概括为以下 5 种常见问题:

  • 1、消息难溯源: 在阅读源码的过程中,如果需要查找发布事件或订阅事件的地方,只能通过查找事件引用的方式进行溯源,增大了理解代码逻辑的难度。特别是当项目中到处是临时事件时,难度会大大增加;
  • 2、临时事件滥用: 由于框架对事件定义没有强制约束,开发者可以随意地在项目的各个角落定义事件。导致整个项目都是临时事件飞来飞去,增大后期维护的难度;
  • 3、数据类型转换错误: LiveDataBus 等事件总线框架需要开发者手动输入事件数据类型,当订阅方与发送方使用不同的数据类型时,会发生类型转换错误。在发生事件命名冲突时,出错的概率会大大增加,存在隐患;
  • 4、事件命名重复: 由于框架对事件命名没有强制约束,不同组件有可能定义重名的事件,产生逻辑错误。如果重名的事件还使用了不同的数据类型,还会出现类型转换错误,存在隐患;
  • 5、事件命名疏忽: 与 ”事件命名重复“ 类似,由于框架对事件命名没有检查,有可能出现开发者复制粘贴后忘记修改事件变量值的问题,或者变量值拼写错误(例如 login_success 拼写为 login_succese),那么订阅方将永远收不到事件。

1.3 ModularEventBus 的解决方案

ModularEventBus 组件化事件总线框架的优点是: 在保持发布者与订阅者的解耦的优势下,解决上述事件总线框架中存在的通病。 具体通过以下 5 个手段实现:

  • 1、事件声明聚合: 发布者和订阅者只能使用预定义的事件,严格禁止使用临时事件,事件需要按照约定聚合定义在一个文件中(解决临时事件滥用问题);
  • 2、区分不同组件的同名事件: 在定义事件时需要指定事件所属 moduleName,框架自动使用 "[moduleName]$[eventName]" 作为最终的事件名(解决事件命名重复问题);
  • 3、事件数据类型声明: 在定义事件时需要指定事件的数据类型,框架自动使用该数据类型发送和订阅事件(解决数据类型转换错误问题);
  • 4、接口强约束: 运行时使用事件类发布和订阅事件,框架自动使用事件定义的事件名和数据类型,而不需要手动输入事件名和数据类型(解决事件命名命名错误);
  • 5、APT 生成接口类: 框架在编译时使用 APT 注解处理器自动生成事件接口类。

1.4 与美团 modular-event 对比有哪些什么不同?

  • modular-event 使用静态常量定义事件,为什么 ModularEventBus 用接口定义事件?美团 modular-event 使用常量引入了重复信息,存在不一致风险。例如开发者复制一行常量后,只修改常量名但忘记修改值,这种错误往往很难被发现。而 ModularEventBus 使用方法名作为事件名,方法返回值作为事件数据类型,不会引入重复信息且更加简洁。

modular-event 事件定义

  • modular-event 使用动态代理,为什么 ModularEventBus 不需要?美团 modular-event 使用动态代理 API 统一接管了事件的发布和订阅,但考虑到这部分代理逻辑非常简单(获取事件名并交给 LiveDataBus 完成后续的发布和订阅逻辑),且框架本身已经引入了编译时 APT 技术,完全可以在编译时生成这部分代理逻辑,没必要使用动态代理 API。
  • 更多特性支持:此外 ModularEventBus 还支持生成事件文档、空数据拦截、泛型事件、自动清除空闲事件等特性。

2. ModularEventBus 能做什么?

ModularEventBus 是一款帮助 Android App 解决事件总线滥用问题的框架,亦可作为组件化基础设施。 其解决方案是通过注解定义事件,由编译时 APT 注解处理器进行合法性检查和自动生成事件接口,以实现对事件定义、发布和订阅的强约束。

2.1 常见事件总线框架对比

以下从多个维度对比常见的事件总线框架( ✅ 良好支持、✔️ 支持、❌ 不支持):


2.2 ModularEventBus 特性一览

1、事件强约束

✅ 支持零配置快速使用;

✅ 支持 APT 注解处理器自动生成事件接口类;

✅ 支持编译时合法性校验和警告提示;

✅ 支持生成事件文档;

✅ 支持增量编译;

2、Lifecycle 生命周期感知

✅ 内置基于 LiveData 的 LiveDataBus;

✅ 支持自动取消订阅,避免内存泄漏;

✅ 支持安全地发送事件与接收事件,避免产生空指针异常或不必要的性能损耗;

✅ 支持永久订阅事件;

✅ 支持自动清除没有关联订阅者的空闲 LiveData 以释放内存;

3、更多特性支持

✅ 支持 Java / Kotlin;

✅ 支持 AndroidX;

✅ 支持订阅 Sticky 粘性事件,支持移除事件;

✅ 支持 Generic 泛型事件,如 List<String> 事件;

✅ 支持拦截空数据;

✅ 支持只发布事件不携带数据的无数据事件;

✅ 支持延迟发送事件;

✅ 支持有序接收事件。


3. ModularEventBus 快速使用

  • 1、添加依赖

模块级 build.gradle

plugins {

id 'com.android.application' // 或 id 'com.android.library'

id 'org.jetbrains.kotlin.android'

id 'kotlin-kapt'

}

dependencies {

// 替换成最新版本

implementation 'io.github.pengxurui:modular-eventbus-api:1.0.4'

kapt 'io.github.pengxurui:modular-eventbus-compiler:1.0.4'

...

}

  • 2、定义事件数据类型(可选): 定义事件关联的数据类型,对于只发布事件而不需要携带数据的场景,可以不定义事件类型。

UserInfo.kt

data class UserInfo(val userName: String)

  • 3、定义事件: 使用接口定义事件名和事件数据类型,并使用 @EventGroup 注解修饰该接口:

LoginEvents.kt

@EventGroup

interface LoginEvents {

// 事件名:login

// 事件数据类型:UserInfo

fun login(): UserInfo

// 事件名:logout

fun logout()

}

  • 4、执行注解处理器: 执行 Make Project 或 Rebuild Project 等多种方式都可以触发注解处理器,处理器将根据事件定义自动生成相应的事件接口。例如,LoginEvents 对应的事件类为:

EventDefineOfLoginEvents.java

/**

* Auto generate code, do not modify!!!

* @see com.pengxr.sampleloginlib.events.LoginEvents

*/

@SuppressWarnings("unchecked")

public class EventDefineOfLoginEvents implements IEventGroup {

private EventDefineOfLoginEvents() {

}

public static IEvent<UserInfo> login() {

return (IEvent<UserInfo>) (ModularEventBus.INSTANCE.createObservable("com.pengxr.sampleloginlib.events.LoginEvents$login", UserInfo.class, false, true));

}

public static IEvent<Void> logout() {

return (IEvent<Void>) (ModularEventBus.INSTANCE.createObservable("com.pengxr.sampleloginlib.events.LoginEvents$logout", Void.class, true, false));

}

}

  • 5、订阅事件: 使用 EventDefineOfLoginEvents 事件类提供的静态方法订阅事件:

订阅者示例

// 以生命周期感知模式订阅事件(不需要手动注销订阅)

EventDefineOfLoginEvents.login().observe(this) { value: UserInfo? ->

// Do something.

}

// 以永久模式订阅事件(需要手动注销订阅)

EventDefineOfLoginEvents.logout().observeForever { _: Void? ->

// Do something.

}

  • 6、发布事件: 使用 EventDefineOfLoginEvents 提供的静态方法发布事件:

发布者示例

EventDefineOfLoginEvents.login().post(UserInfo("XIAOPENG"))

EventDefineOfLoginEvents.logout().post(null)

  • 7、添加混淆规则(如果使用了 minifyEnabled true):

-dontwarn com.pengxr.modular.eventbus.generated.**

-keep class com.pengxr.modular.eventbus.generated.** { *; }

-keep @com.pengxr.modular.eventbus.facade.annotation.EventGroup class * {*;} # 可选


4. 完整使用文档

4.1 定义事件

  • 使用注解定义事件:@EventGroup 注解: @EventGroup 注解用于定义事件组,修饰于 interface 接口上,在该类中定义的每个方法均视为一个事件定义;@Event 注解: @Event 注解用于事件组中的事件定义,亦可省略。

模板程序如下:

com.pengxr.sample.events.MainEvents.kt

// 事件组

@EventGroup

interface MainEvents {

// 事件

// @Event 可以省略

@Event

fun open(): String

}

亦兼容将 @EventGroup 修饰于 class 类而非 interface 接口,但会有编译时警告: Annotated @EventGroup on a class type [IllegalEvent], expected a interface. Is that really what you want?

错误示例

@EventGroup

class IllegalEvent {

fun illegalEvent() {

}

}

  • 使用 @Ignore 注解忽略定义: 使用 @Ignore 注解可以排除事件类或事件方法,使其不被视为事件定义。

示例程序

// 可以修饰于事件组

@Ignore

@EventGroup

interface IgnoreEvent {

// 亦可修饰于事件

@Ignore

fun ignoredMethod()

fun method()

}

  • 使用 @Deprecated 注解提示过时: 使用 @Deprecated 注解可以标记事件为过时。与 @Ignore 不同是,@Deprecated 修饰的类或方法依然是有效的事件定义。

示例程序

// 虽然过时,但依然是有效的事件定义

@Deprecated("Don't use it.")

@EventGroup

interface DeprecatedEvent {

@Deprecated("Don't use it.")

fun deprecatedMethod()

}

  • 定义事件数据类型: 事件方法返回值即表示事件数据类型,支持泛型(如 List<String>),支持不携带数据的无数据事件。以下均为合法定义:

Java 示例程序

// 事件数据类型为 String

String stringEventInJava();

// 事件数据类型为 List<String>

List<String> listEventInJava();

// 以下均视为无数据事件

void voidEventInJava1();

Void voidEventInJava2();

Kotlin 示例程序

// 事件数据类型为 String

fun stringEventInKotlin(): String

// 事件数据类型为 List<String>

fun listEventInKotlin(): List<String>

// 以下均视为无数据事件

fun voidEventInKotlin1()

fun voidEventInKotlin2(): Unit

fun voidEventInKotlin3(): Unit?

  • 定义事件数据可空性: 使用 @Nullable 或 @NonNull 注解表示事件数据可空性,默认为可空类型。以下均为合法定义:

Java 示例程序

@NonNull

String nonNullEventInJava();

@Nullable

String nullableEventInJava();

// 默认视为 @Nullable

String eventInJava();

Kotlin 示例程序

fun nonNullEventInKotlin(): String

// 提示:Kotlin 编译器将返回类型上的 ? 号视为 @org.jetbrains.annotations.Nullable

fun nullableEventInKotlin(): String?

以下为支持的可空性注解:

org.jetbrains.annotations.Nullable

android.annotation.Nullable

androidx.annotation.Nullable

org.jetbrains.annotations.NotNull

android.annotation.NonNull

androidx.annotation.NonNull

  • 定义自动清除事件: 支持配置在事件没有关联的订阅者时自动被清除(以释放内存),默认值为 false。可以使用 @EventGroup 注解或 @Event 注解进行修改,以 @Event 的取值优先。

示例程序

@EventGroup(autoClear = true)

interface MainEvents {

@Event(autoClear = false)

fun normalEvent(): String

// 继承 @EventGroup 中的 autoClear 取值

fun autoClearEvent(): String

}

  • 定义事件所属组件名: 为避免不同组件中的事件名重复,框架自动使用 "[moduleName]$[eventName]" 作为最终的事件名。默认使用事件组的 [全限定类名] 作为 moduleName,可以使用 @EventGroup 注解进行修改。

示例程序

com.pengxr.sample.events.MainEvents.kt

@EventGroup(moduleName = "main")

interface MainEvents {

fun open(): String

}

4.2 执行注解处理器

在完成事件定义后,执行 Make Project 或 Rebuild Project 等多种方式都可以触发注解处理器,处理器将根据事件定义自动生成相应的事件接口。例如, MainEvents 对应的事件接口为:

com.pengxr.modular.eventbus.generated.events.com.pengxr.sample.events.EventDefineOfMainEvents.java

/**

* Auto generate code, do not modify!!!

* @see com.pengxr.sample.events.MainEvents

*/

@SuppressWarnings("unchecked")

public class EventDefineOfMainEvents implements IEventGroup {

private EventDefineOfMainEvents() {

}

public static IEvent<String> open() {

return (IEvent<String>) (ModularEventBus.INSTANCE.createObservable("main$open", String.class, false, false));

}

}

EventDefineOfMainEvents 中的静态方法与 MainEvent 事件组中的每个事件一一对应,直接通过静态方法即可获取事件实例,而不再通过手动输入事件名字符串或事件数据类型,故可避免事件名错误或数据类型错误等问题。

所有的事件实例均是 IEvent 泛型接口的实现类,例如 open 事件属于 IEvent<String> 类型的事件实例。发布事件和订阅事件需要用到 IEvent 接口中定义的一系列 post 方法和 observe 方法,IEvent 接口的完整定义如下:

IEvent.kt

interface IEvent<T> {

/**

* 发布事件,允许在子线程发布

*/

@AnyThread

fun post(value: T?)

/**

* 延迟发布事件,允许在子线程发布

*/

@AnyThread

fun postDelay(value: T?, delay: Long)

/**

* 延迟发布事件,在准备发布前会检查 producer 处于活跃状态,允许在子线程发布

*

* @param producer 发布者的 LifecycleOwner

*/

@AnyThread

fun postDelay(value: T?, delay: Long, producer: LifecycleOwner)

/**

* 发布事件,允许在子线程发布,确保订阅者按照发布顺序接收事件

*/

@AnyThread

fun postOrderly(value: T?)

/**

* 以生命周期感知模式订阅事件(不需要手动注销订阅)

*/

@AnyThread

fun observe(consumer: LifecycleOwner, observer: Observer<T?>)

/**

* 以生命周期感知模式粘性订阅事件(不需要手动注销订阅)

*/

@AnyThread

fun observeSticky(consumer: LifecycleOwner, observer: Observer<T?>)

/**

* 以永久模式订阅事件(需要手动注销订阅)

*/

fun observeForever(observer: Observer<T?>)

/**

* 以永久模式粘性订阅事件(需要手动注销订阅)

*

* @param observer Event observer.

*/

@AnyThread

fun observeStickyForever(observer: Observer<T?>)

/**

* 注销订阅者

*/

@AnyThread

fun removeObserver(observer: Observer<T?>)

/**

* 移除事件,关联的订阅者关系也会被解除

*/

@AnyThread

fun removeEvent()

}

4.3 订阅事件

使用 IEvent 接口定义的一系列 observe() 接口订阅事件,使用示例:

示例程序

// 以生命周期感知模式订阅(不需要手动注销订阅)

EventDefineOfMainEvents.open().observe(this) {

// do something.

}

// 以生命周期感知模式、且粘性模式订阅(不需要手动注销订阅)

EventDefineOfMainEvents.open().observeSticky(this) {

// do something.

}

val foreverObserver = Observer<String?> {

// do something.

}

// 以永久模式订阅(需要手动注销订阅)

EventDefineOfMainEvents.open().observeForever(foreverObserver)

// 以永久模式,且粘性模式订阅(需要手动注销订阅)

EventDefineOfMainEvents.open().observeStickyForever(foreverObserver)

// 移除观察者

EventDefineOfMainEvents.open().removeObserver(foreverObserver)

4.4 发布事件

使用 IEvent 接口定义的一系列 post() 接口发布事件,使用示例:

示例程序

// 发布事件,允许在子线程发布

EventDefineOfMainEvents.open().post("XIAO PENG")

// 延迟发布事件,允许在子线程发布

EventDefineOfMainEvents.open().postDelay("XIAO PENG", 5000)

// 延迟发布事件,在准备发布前会检查 producer 处于活跃状态,允许在子线程发布。

EventDefineOfMainEvents.open().postDelay("XIAO PENG", 5000, this)

// 发布事件,允许在子线程发布,确保订阅者按照发布顺序接收事件

EventDefineOfMainEvents.open().postOrderly("XIAO PENG")

// 移除事件

EventDefineOfMainEvents.open().removeEvent()

4.5 更多功能

  • 生成事件文档(可选): 支持生成事件文档,需要在 Gradle 配置中开启:

模块级 build.gradle

// 需要生成事件文档的模块就增加配置:

android {

defaultConfig {

javaCompileOptions {

annotationProcessorOptions {

arguments = [

MODULAR_EVENTBUS_GENERATE_DOC: "enable",

MODULAR_EVENTBUS_MODULE_NAME : project.getName()

]

}

}

}

}

文档生成路径: build/generated/source/kapt/[buildType]/com/pengxr/modular/eventbus/generated/docs/eventgroup-of-[MODULAR_EVENTBUS_MODULE_NAME].json

  • 配置(可选):debug(Boolean): 调试模式开关;throwNullEventException(Boolean): 非空事件发布空数据时是否抛出 NullEventException 异常,在 release 模式默认为只拦截不抛出异常,在 debug 模式默认为拦截且抛出异常;setEventListener(IEventListener): 全局监听接口。

示例程序

ModularEventBus.debug(true)

.throwNullEventException(true)

.setEventListener(object : IEventListener {

override fun <T> onEventPost(eventName: String, event: BaseEvent<T>, data: T?) {

Log.i(TAG, "onEventPost: $eventName, event = $event, data = $data")

}

})


5. 未来功能规划

  • 支持跨进程 / 跨 App:LiveEventBus 框架支持跨进程 / 跨 App,未来根据使用反馈考虑实现该 Feature;
  • 支持替换内部 EventBus 工厂:ModularEventBus 已预设计事件总线工厂 IEventFactory,未来根据使用反馈考虑公开该 API;
  • 支持基于 Kotlin Flow 的 IEventFactory 工厂;
  • 编译时检查在不同 @EventGroup 中设置相同 modulaName 且相同 eventName,但事件数据类型不同的异常。

6. 共同成长

  • 欢迎提 Issue 帮助修复缺陷;
  • 欢迎提 Pull Request 增加新的 Feature,让 ModularEventBus 变得更加强大,你的 ID 会出现在 Contributors 中;
  • 欢迎加 作者微信 与作者交流,欢迎加入交流群找到志同道合的伙伴

参考资料

  • Android 消息总线的演进之路:用 LiveDataBus 替代 RxBus、EventBus —— 海亮(美团)著
  • Android 组件化方案及组件消息总线 modular-event 实战 —— 海亮(美团)著

微信小程序之组件化

关于组件化

类似于页面,自定义组件拥有自己的 wxml 模版和 wxss 样式。

官方链接

组件化,反过来理解,写重复的页面,方法,写第二遍就烦了,抽取出来就是组件化,可以理解为公用的方法

对于通用的数据,最先想到或者理应接触的是template,但是template有个缺点,那就是只是页面效果,不会有对应的js操作。

微信小程序组件化,为什么要有自定义组件呢
每个页面有对应的js(JavaScript),json(配置config),wxml(html),wxss(css)还差什么
不挺好的嘛,为什么需要自定义组件呢
当然,对于特定的某个页面而言,是足够了,但是如果是通用的一些页面都需要相同的效果,
或者是相似的效果呢,你还是一步步重复的操作嘛
存在就是合理的,完全可以把自定义组件理解为是一个自定义的标签,页面的一个片段。

当然这只是最基本的
想想,既然是组件component,那对于pages页面而言,就是一对多的关系。
多个页面织入组件,怎么衔接起来呢。
pages页面可以给component组件传递值,自然组件也会对应的想pages自定义的回调方法。

只要是你能想到,想实现的,都会有对应的提供。

实现方式,在目录新建component,会自动生成对应的js,json,wxml,wxss,基本跟pages差不多

看看component

Component({

  

  options: {

    multipleSlots: true // 在组件定义时的选项中启用多slot支持

  },

/**

* 组件的属性列表

*/

properties: {

},

/**

* 组件的初始数据

*/

data: {

},

/**

* 组件的方法列表

*/

methods: {

}

})

options,multipleslots:true 在组建中启用多个slot支持。

properties,里面可以定义一些接收pages页面的属性。

data,组件的数据

method,组件抽取出来的方法

具体用法

首先需要在pages页的json配置一下开启使用组件

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第1张

说说options吧

你可以在component的wxml中

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第2张

通过slot的name属性来指定对应的pages页面内容

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第3张

ok

说说properties吧

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第4张

在自定义标签中传递一个num属性

在组件中可以通过properties来传递

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第5张

获取num值可以通过this.properties.num来获取

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第6张

data用法的话跟page是一个样子的。

最重要的要属于方法事件了

page相对于组件而言,是一个一对多的过程

既然是抽取出来的,组件只能做一些共有的事,私自的一些实现肯定是得通过page自省的。

这就涉及到了回调。比如在组件中添加一个bindtap方法。

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第7张

但是这个组件页面的customevent的方法是绑定的。

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第8张

对应的pages页

微信小程序之组件化 (https://www.wpmee.com/) WordPress使用教程 第9张

在methods中,通过treggerEvent来调用page自己实现的方法。

ok,这是组件化入门。

标签: 小程序

提交需求或反馈

Demand feedback