背景
由于小程序每个页面是由独立的webview来承载,所以当a页面数据更新,b页面无法自动同步到数据,只能手动到b页面重新去拉取,基于这个痛点,利用app.js的全局对象getApp()来做全局信息的订阅和发布,加上对page和component进行proxy,给指定钩子添加上事件监听和解绑
app.js + proxyGlobal
app.js
: 实例化全局代理对象,添加注册和卸载入口
App({
...
// 全局信息,初始化
globalData: {
...
},
// 实例化一个全局代理
onLaunch () {
this.proxyGlobal = new ProxyGlobal(this.globalData)
},
// 获取全局代理实例
// 传入代理对象,则添加一个订阅者,返回最新的全局对象
getAppGlobalData (page) {
return this.proxyGlobal.getGlobalData(page)
},
// 组件或者页面卸载的时候要清空对应的监听器
clearObserver (page) {
this.proxyGlobal.clearObserver(page)
},
})
proxyGlobal.js
: Observer订阅发布类 + ProxyGlobal代理类
Observer
:用于发布和订阅消息
class Observer{
constructor () {
this.observers = []
}
add (observer) {
if (this.observers.some(o => o === observer)) { return }
this.observers.push(observer)
}
remove (observer) {
this.observers.forEach((item, index) => {
if (item === observer) {
this.observers.splice(index, 1)
return
}
})
}
// 这里写死了updateGlobal为触发的消息回调,也可以写成add的时候添加指定的回调方法
notify () {
this.observers.forEach(observer => observer && observer.updateGlobal())
}
}
ProxyGlobal
:用于代理页面和组件,并添加订阅
class ProxyGlobal{
constructor (globalData) {
this.globalData = globalData
this.observers = new Observer()
}
clearObserver (observer) {
this.observers.remove(observer)
}
getGlobalData (observerPage) {
observerPage && this.observers.add(observerPage)
return this.globalData
}
setGlobalData (data) {
this.globalData = data
this.observers.notify()
}
static ProxyPage (originPage) {
Reflect.set(originPage, 'updateGlobal', function () {
console.log('global触发页面update', this)
const pages = getCurrentPages()
if (this === pages[pages.length - 1]) {
const app = getApp()
this.setData({ globalData: app.getAppGlobalData() })
}
})
const proxyPage = new Proxy(originPage, {
get (target, prop) {
if (prop === 'onShow') {
if (!target[prop]) {
throw '代理页面必须添加onShow钩子,用于刷新数据'
}
Reflect.set(target, prop, new Proxy(target[prop], {
apply (target, page, args) {
// console.log('global触发页面onShow')
const app = getApp()
page.setData({ globalData: app.getAppGlobalData() })
return Reflect.apply(target, page, args)
}
}))
}
if (prop === 'onLoad') {
if (!target[prop]) {
throw '代理页面必须添加onLoad钩子,用于添加监听'
}
Reflect.set(target, prop, new Proxy(target[prop], {
apply (target, page, args) {
// console.log('global触发页面onload')
const app = getApp()
page.setData({ globalData: app.getAppGlobalData(page) })
return Reflect.apply(target, page, args)
}
}))
}
if (prop === 'onUnload') {
if (!target[prop]) {
throw '代理页面必须添加onUnload钩子,用于解绑监听'
}
Reflect.set(target, prop, new Proxy(target[prop], {
apply (target, page, args) {
// console.log('global触发页面onUnload')
const app = getApp()
app.clearObserver(page)
return Reflect.apply(target, page, args)
}
}))
}
return Reflect.get(target, prop)
}
})
return proxyPage
}
static ProxyComponent (originComponent) {
Reflect.set(originComponent['methods'], 'updateGlobal', function () {
// console.log('global触发组件update')
const app = getApp()
this.setData({ globalData: app.getAppGlobalData() })
})
const proxyComponent = new Proxy(originComponent, {
get (target, prop) {
if (prop === 'attached') {
if (!target[prop]) {
throw '代理组件必须添加attached参数,用于添加监听'
}
Reflect.set(target, prop, new Proxy(target[prop], {
apply (target, component, args) {
// console.log('global触发组件attached')
const app = getApp()
component.setData({ globalData: app.getAppGlobalData(component) })
return Reflect.apply(target, component, args)
}
}))
}
if (prop === 'detached') {
if (!target[prop]) {
throw '代理组件必须添加detached参数,用于解绑监听'
}
Reflect.set(target, prop, new Proxy(target[prop], {
apply (target, component, args) {
// console.log('global触发组件detached')
const app = getApp()
app.clearObserver(component)
return Reflect.apply(target, component, args)
}
}))
}
return Reflect.get(target, prop)
}
})
return proxyComponent
}
}
最后效果
所有数据由app.js缓存和更新,再通过proxy所有的page和component, 在组件和页面初始化的时候调用getAppGlobalData()来订阅全局信息的更新,再需要全局更新的时候,可以调用app.proxyGlobal.setGlobalData(newData),触发所有订阅者的数据更新,从而达到数据自动更新和同步
This work is licensed under a CC A-S 4.0 International License.