微信小程序提供了页面通信通信解决方案EventChannel,但实际使用中需要在wx.navigateTo跳转页面时使用,且需要在跳转前声明监听的事件,如下图
这是一种页面间的通信,但是局限性过于明显,仅可以在跳转间的页面之间建立通信,A跳转B可以建立通信关系,A不跳转G就不可以建立通信关系,在实际开发中如果某个注册页面的信息想做回显,我们可以使用重新请求、放到storage中、glabalData、eventbus全局通信等,但是肯定不能用navigateTo建立eventbus信道进行传值,从交互层面是完全不可接收的。
这时我们就需要一个全局的eventbus来进行通信了,下面讲解一下微信小程序如何搭建全局eventbus进行通信。
注:eventbus传值,如果没有对引用类型进行深拷贝,那么会将引用传过去导致错误。
首先,我们需要开发页面扩展功能,我们知道每一个页面都是一个Page({}),传入的是一个对象,对象中包含双向绑定的数据、生命周期函数、自定义函数,这里我们需要增加页面的生命周期函数。
原理可以参考这篇文章: 小程序页面(Page)扩展
其中我们需要这5个文件:
其中config.js是小程序全局构造函数App、Page扩展规则配置项,eventBus是eventbus的实现,index是将eventbus扩展的页面上,然后再app.js中引入index文件即可,pageExtends即页面扩展方法,public是初始化eventbus的方法。
使用方法:
A页面声明:
B页面触发:
以下为源码
config.js源码:
/*
* @Author: 徐强国
* @Date: 2022-08-15 15:43:32
* @Description: Page公共方法扩展
*/
const EventBus = require('./eventBus')
let eventBus
// 初始化页面的eventbus,事件用法参照dom2事件
export const initEventBus = (pageObj) => {
// let eventBus = new EventBus();
if (!eventBus) {
eventBus = new EventBus();
} else {
}
pageObj['$on'] = function () {
let argu = Array.prototype.slice.call(arguments);
eventBus.on(...argu)
}
pageObj['$off'] = function () {
let argu = Array.prototype.slice.call(arguments);
eventBus.off(...argu)
}
pageObj['$emit'] = function () {
let argu = Array.prototype.slice.call(arguments);
eventBus.emit(...argu)
}
// 创建页面声明的自定义事件
let events = pageObj['events'];
if (Array.isArray(events)) {
events.forEach((event, index) => {
if (typeof event === 'string') {
eventBus.createEvent(event)
} else {
console.error(`==请传入String类型的事件名称== index:${index}`, events)
}
})
} else if (typeof events !== 'undefined') {
console.error('==events字段已被占用,用于声明当前页面需要创建的自定义事件,值为字符串数组== events:', events)
}
}
module.exports = {
onLoad(options) {
this.$initPage()
},
$initPage() {
if (!this.$on) {
initEventBus(this)
}
},
}
eventBus.js源码
/**
* @authors 徐强国
* @date 2022-8-8
* eventBus,订阅/发布
*
*/
// 是否是字符串
function isString(str) {
return typeof str === 'string'
}
// 是否是函数
function isFunction(fn) {
return typeof fn === 'function'
}
// 消息中心
class MessageHub {
constructor() {
this.pubDictionary = {}
}
// 创建发布者
createEvent(name, isGlobal) {
if (!isString(name)) {
console.error(`==请传入创建事件的名称 name==`)
return false
}
let _pub = this.pubDictionary[name]
if (_pub) {
if (!isGlobal) {
console.warn(`==${name} 事件已存在==`)
}
return _pub
} else {
let pub = new Publish(name, this)
this.pubDictionary[name] = pub
return pub
}
}
removeEvent(name) {
if (!isString(name)) {
console.error(`==请传入删除事件的名称 name==`)
return false
}
delete this.pubDictionary[name]
}
on(name, callback, mark) {
if (!isString(name)) {
console.error(`==请传入监听事件的名称 name==`)
return false
}
console.log('ononoonon这里的区文体', this.pubDictionary, callback, mark)
if (!isFunction(callback)) {
console.error(`==请传入监听事件的回调函数 callback==`)
return false
}
let pub = this.pubDictionary[name]
if (pub) {
let watcher = new Watcher(pub.dep, callback, mark)
pub.dep.addSub(watcher)
} else {
console.error(`==尚未创建 ${name} 事件==`)
}
}
off(name, callback) {
if (!isString(name)) {
console.error(`==请传入监听事件的名称 name==`)
return false
}
if (!isFunction(callback)) {
console.error(`==请传入监听事件的回调函数 callback==`)
return false
}
let pub = this.pubDictionary[name]
pub.dep.removeSub(callback)
}
emit(name, val) {
if (!isString(name)) {
console.error(`==请传入触发事件的名称 name==`)
return false
}
console.log('这里的区文体emit', this.pubDictionary)
let pub = this.pubDictionary[name]
if (pub) {
pub.refresh(val)
} else {
console.warn(`==${name} 事件不存在==`)
}
}
clearEvent() {
this.pubDictionary = {}
}
}
// 发布者
class Publish {
constructor(name, messageHub) {
this.name = name
this.messageHub = messageHub
this.dep = new Dep(this)
}
refresh(val) {
this.dep.notify(val)
}
}
// 订阅者
class Watcher {
constructor(dep, run, mark) {
this.dep = dep
this.run = run
this.mark = mark || ''
}
update() {
let val = this.dep.value
let run = this.run
run(val)
}
}
// 依赖收集
class Dep {
constructor(pub) {
this.pub = pub
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
removeSub(run) {
let sub = this.subs.filter(item => item.run === run)[0]
remove(this.subs, sub)
}
notify(val) {
this.value = val
let subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
function remove(arr, el) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === el) {
arr.splice(i, 1)
return true
}
}
return false
}
module.exports = MessageHub
pageExtends.js源码
/**
* @authors 徐强国
* @date 2022-08-15
* 小程序全局构造函数App、Page扩展
*/
const { appLiftTimes, pageLiftTimes } = require('./config');
// 判断是否是App的生命周期及原始方法
function isAppLiftTimes (name, fn) {
if (typeof fn === 'function') {
return appLiftTimes.indexOf(name) > -1
}
return false
}
// 判断是否是Page的生命周期及原始方法
function isPageLiftTimes(name, fn) {
if (typeof fn === 'function') {
return pageLiftTimes.indexOf(name) > -1
}
return false
}
// 函数混入
function rewriteFn(context, name, fn) {
if (context[name]) {
let originFn = context[name];
context[name] = function (e) {
let argu = Array.prototype.slice.call(arguments);
fn.apply(this, argu);
return originFn.apply(this, argu)
}
} else {
context[name] = fn
}
}
// 是否是对象
function isObject(obj) {
return obj !== null && typeof obj === 'object'
}
// 重写App
const originApp = App;
const appExtendsList = [];
App = function (obj) {
// app拓展方法
appExtendsList.forEach(item => {
rewriteFn(obj, item.key, item.value)
})
return originApp(obj)
}
const appExtends = function (key, value) {
if (isAppLiftTimes(key, value)) {
appExtendsList.push({ key, value })
} else {
console.error('==*App暂不支持非生命周期的扩展*==', key)
}
}
// 重写Page
const originPage = Page;
const pageExtendsList = [];
Page = function (obj) {
let illegalKeys = Object.keys(obj).filter(key => /^\$+/.test(key));
if (illegalKeys.length) {
// throw new Error(`Page中自定义属性禁止以 \$ 开头, ${illegalKeys.join(', ')}`)
console.error(`Page中自定义属性禁止以 \$ 开头, ${illegalKeys.join(', ')}`)
}
// 页面拓展方法
pageExtendsList.forEach(item => {
// 非生命周期属性只能拓展一次
if (isPageLiftTimes(item.key, item.value)) {
rewriteFn(obj, item.key, item.value)
} else {
if (typeof obj[item.key] === 'undefined') {
obj[item.key] = item.value;
} else {
console.error(`Page中已拓展 ${item.key} 属性`, obj[item.key])
}
}
})
return originPage(obj)
}
const pageExtends = function (key, value) {
// Page拓展属性,非生命周期的属性必须以 $ 开头
if (/^\$+/.test(key) || isPageLiftTimes(key, value)) {
if (isPageLiftTimes(key, value) || !pageExtendsList.filter(item => item.key === key).length) {
pageExtendsList.push({ key, value })
} else {
console.warn(`==*Page中已扩展 ${key} 属性*==`)
}
} else {
console.warn(`==*Page中拓展属性必须以 \$ 开头*==`, `\n key: ${key}`)
}
}
const AppPlus = {
appExtends: function (mixinObj, value) {
if (typeof mixinObj === 'string') {
appExtends(mixinObj, value)
} else if (isObject(mixinObj)) {
Object.keys(mixinObj).forEach(key => {
appExtends(key, mixinObj[key])
})
} else {
console.warn('==*请传入 对象 或者 key, value*==')
}
},
pageExtends: function (mixinObj, value) {
if (typeof mixinObj === 'string') {
pageExtends(mixinObj, value)
} else if (isObject(mixinObj)) {
Object.keys(mixinObj).forEach(key => {
pageExtends(key, mixinObj[key])
})
} else {
console.warn('==*请传入 对象 或者 key, value*==')
}
}
}
module.exports = AppPlus
public.js源码
/*
* @Author: 徐强国
* @Date: 2022-08-15 15:43:32
* @Description: Page公共方法扩展
*/
const EventBus = require('./eventBus')
let eventBus
// 初始化页面的eventbus,事件用法参照dom2事件
export const initEventBus = (pageObj) => {
// let eventBus = new EventBus();
if (!eventBus) {
eventBus = new EventBus();
} else {
}
pageObj['$on'] = function () {
let argu = Array.prototype.slice.call(arguments);
eventBus.on(...argu)
}
pageObj['$off'] = function () {
let argu = Array.prototype.slice.call(arguments);
eventBus.off(...argu)
}
pageObj['$emit'] = function () {
let argu = Array.prototype.slice.call(arguments);
eventBus.emit(...argu)
}
// 创建页面声明的自定义事件
let events = pageObj['events'];
if (Array.isArray(events)) {
events.forEach((event, index) => {
if (typeof event === 'string') {
eventBus.createEvent(event)
} else {
console.error(`==请传入String类型的事件名称== index:${index}`, events)
}
})
} else if (typeof events !== 'undefined') {
console.error('==events字段已被占用,用于声明当前页面需要创建的自定义事件,值为字符串数组== events:', events)
}
}
module.exports = {
onLoad(options) {
this.$initPage()
},
$initPage() {
if (!this.$on) {
initEventBus(this)
}
},
}
index.js源码
/*
* @Author: 徐强国
* @Date: 2022-08-15 15:18:12
* @Description: 小程序提供扩展App、Page扩展入口
*
*
* AppPlus提供拓展App及Page的接口,校验自定义属性命名
* @param appExtends
* @parm pageExtends
*
* 传入一个对象,此对象的属性及方法将混入App或者Page实例中
* 生命周期函数将与自定义的声明周期混合,且先执行,
* 其他属性只能以$开头,且不可覆盖、混入,应避免名称重复
*/
const AppPlus = require('./pageExtends')
const Public = require('./public')
AppPlus.pageExtends(Public)
代码下载:https://github.com/TobinXu/MiniProgramEventBus/tree/main
出个 DEMO?
安卓的eventBus吗😀