巧借RxJS东风,实现Taro小程序跨页面通信

2,621 阅读3分钟

前言


最近正在使用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做小程序遇到同样问题的同学提供一些思路。