前言
最近正在使用Taro开发小程序,遇到了许多问题,首先遇到的便是页面间传递数据的问题,记录一下解决的过程和最后采取的方案,也供大家参考。
方案一 (Taro.eventCenter) ❌
跨页面通信,首先想到的便是消息的方式,好在Taro提供了一个全局消息中心: Taro.eventCenter。 用法如下:
Taro.eventCenter.on('eventName',handler) // 监听一个事件,接受参数
Taro.eventCenter.trigger('eventName') // 触发一个事件,传参
Taro.eventCenter.off('eventName',handler) // 取消监听一个事件某个 handler
开始操练起来
A页面触发事件
await navigateTo({url: 'page B'}
Taro.eventCenter.trigger('eventName',data)
B页面监听事件
useEffect(() => {
Taro.eventCenter.on('eventName', (data) => {
// do something
})
}, [])
理想很丰满,现实很骨感,这样是无效的,因为在A页面事件触发之后,B页面还没监听上。别急,那我加一个setTimeout等一等呢?
await navigateTo({url: 'page B'}
setTimeout(() => {
Taro.eventCenter.trigger('eventName',data)
},200)
测试是可以!但这肯定不是我们想要的。方案一至此被放弃。
思考🤔
然后我们再来思考,我们能不能在监听的时候再去消费数据呢?这样就需要满足两点要求:
-
A页面跳转的时候先存储数据
-
B页面订阅时候再去消费数据
于是乎,想到这,RxJS中的BehaviorSubject的身影渐渐浮出水面
不了解的同学可以先看一下相关介绍:BehaviorSubject
方案二 (RxJS BehaviorSubject) ✅
先简单理解两个对象:
-
Observable,可观察对象,数据生产者 ,产生数据的一方。
-
Observer,观察者,数据消费者 ,接收数据的一方。
再来说BehaviorSubject:
既是生产者又是消费者,它有一个“当前值”的概念。它保存了发送给消费者的最新值,当有新的观察者订阅时,会立即从 BehaviorSubject 那接收到“当前值”,在定义一个 BehaviorSubject 时需要有初始值。
用法:
import { BehaviorSubject } from 'rxjs';
const subject = new BehaviorSubject(0); // 创建,一个number类型的初始值
subject.subscribe({
next: (v) => console.log(`observerA: ${v}`) // 消费数据
});
subject.next(1);// 生产数据
subject.next(2);// 生产数据
subject.subscribe({
next: (v) => console.log(`observerB: ${v}`) // 消费数据
});
subject.next(3);
// 打印结果
// observerA: 0 A订阅当前值是0,初始值
// observerA: 1
// observerA: 2
// observerB: 2 B订阅当前值是2
// observerA: 3
// observerB: 3
继续操练起来!
先实现两个方便创建和订阅BehaviorSubject的hooks
- useBehaviorSubject 用来创建
import { BehaviorSubject } from 'rxjs'
import { useConst } from 'whooks'
function useBehaviorSubject<T>(init: T) {
const behaviorSubject = useConst(new BehaviorSubject<T>(init))
useEffect(() => {
return () => {
behaviorSubject.unsubscribe()
}
}, [behaviorSubject])
return behaviorSubject
}
export default useBehaviorSubject
其中有个useConst,还有下边使用的useConstCallback,是我自己常用的hooks,里边就是包了下ref。
- useBehaviorSubscription 用来消费
import type { BehaviorSubject } from 'rxjs'
function useBehaviorSubscription<T>(subject: BehaviorSubject<T>, next: (value: T) => void) {
useEffect(() => {
const subscription = subject.subscribe(next)
return () => {
subscription.unsubscribe()
}
}, [subject, next])
}
export default useBehaviorSubscription
创建全局countBehaviorSubject
为了方便管理,我们创建一个GlobalProvider,把countBehaviorSubject放到Context中
const useGlobalProviderFacade = () => {
const countBehaviorSubject = useBehaviorSubject(0)
return {
countBehaviorSubject
}
}
export type GlobalContextType = ReturnType<typeof useGlobalProviderFacade>
export const GlobalContext = React.createContext<GlobalContextType>({} as GlobalContextType)
export const GlobalProvider: React.FC = ({ children }) => {
const globalData = useGlobalProviderFacade()
return <GlobalContext.Provider value={globalData}>{children}</GlobalContext.Provider>
}
export const useGlobal = useContext(GlobalContext)
再来看效果💥
A页面发布数据
const { countBehaviorSubject } = useGlobal()
const onCreated = async () => {
countBehaviorSubject.next(1)
await Taro.navigateTo({url: 'page B'})
}
B页面消费数据
const { countBehaviorSubject } = useGlobal()
const next = useConstCallBack((value) => {
// do something
})
useBehaviorSubscription(countBehaviorSubject, next)
使用结果,可行,至此,在页面中调用简单的几行代码就可以实现跨页面的通信了。
const subject = useBehaviorSubject(init:T) // 创建
subject.next(data:T) // 发布
useBehaviorSubscription(subject,handle:(value:T)=> void) // 订阅
总结
- 巧借RxJS的BehaviorSubject的东风,解决了页面的通信问题。
- 解决问题的根本点在跳转页面之前把生产的数据存储了起来,在跳转后的页面中实现订阅时再消费。
- 在后边的自定义带权限的tabBar的过程中也用到了这个,对个人来讲还是比较便利的。
最后
- 第一次做小程序的项目,也没什么经验,考虑到开发效率,直接就用Taro了。
- 解决问题的过程中,可能也走了弯路,大家有什么好的方案也可以提供一下。
- 最后,还是希望能给第一次用Taro做小程序遇到同样问题的同学提供一些思路。