前言
最近有很多同学问,小程序里面如何进行跨页面通信。看了下之前的老代码,基本都是基于onShow
或者localStorage
。虽然可以实现,但是并不怎么优雅。
今天就来聊一聊,小程序的跨页面通信的几种实现方案。或许会有你想要的方案(优雅...)
方式一:onShow + localStorage
业务场景:页面一未登录跳转至登录页面,登录成功后返回页面一,页面一需要更新当前登录态
<!-- 页面一 -->
<template>
<view>
<text>{{ name }}</text>
<view class="login_text">当前是否登录:<text>{{ isLogin ? '是' : '否' }}</text></view>
<button @tap="gotoLogin">跳转登录</button>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import taro, { useDidShow } from '@tarojs/taro'
const name = ref('前端南玖---小程序页面通信')
const loginStatus = taro.getStorageSync('isLogin') || false
const isLogin = ref<boolean>(loginStatus)
const gotoLogin = () => {
taro.navigateTo({
url: '/pages/login/index'
})
}
// 小程序onshow生命周期,从localStorage获取是否登录,更新页面
useDidShow(() => {
const loginStatus = taro.getStorageSync('isLogin') || false
isLogin.value = loginStatus
})
</script>
<!--登录页-->
<template>
<view>
登录页面
<button @tap="login">登录</button>
</view>
</template>
<script setup lang="ts">
import taro from '@tarojs/taro'
const login = () => {
taro.login({
success: function (res) {
console.log('登录成功', res)
taro.setStorageSync('isLogin', true)
taro.navigateBack()
},
fail: function (res) {
console.log('登录失败', res)
}
})
}
</script>
优点: 这种方案可能是最简单的通信方案,比较容易理解
缺点: 如果完成通信后,没有即时清除通信数据,可能会出现问题。另外因为依赖localStorage,而localStorage可能出现读写失败,从面造成通信失败
方式二:onShow + globalData
业务场景同上
这个方案与第一个方案差不多,只不过是将localStorage
换成了globalData
Taro框架想要使用小程序的globalData需要使用Taro
提供的插件 setGlobalDataPlugin
// app.ts
import { setGlobalDataPlugin } from '@tarojs/taro'
const App = createApp({
})
// 注册全局数据
App.use(setGlobalDataPlugin, {
isLogin: false, // 是否登录
})
// 页面一
// ...
import { ref } from 'vue'
import taro, { useDidShow } from '@tarojs/taro'
const app = taro.getApp()
const name = ref('前端南玖---小程序页面通信')
const loginStatus = taro.getStorageSync('isLogin') || false
const isLogin = ref<boolean>(loginStatus)
const gotoLogin = () => {
taro.navigateTo({
url: '/pages/login/index'
})
}
// 使用globalData
useDidShow(() => {
// const loginStatus = taro.getStorageSync('isLogin') || false
console.log('app.globalData', app.isLogin)
const loginStatus = app.isLogin || false
isLogin.value = loginStatus
})
// 登录页
import taro from '@tarojs/taro'
const app = taro.getApp()
const login = () => {
taro.login({
success: function (res) {
console.log('登录成功', res)
app.isLogin = true
taro.navigateBack()
},
fail: function (res) {
console.log('登录失败', res)
}
})
}
优点: 实现简单,容易理解。因为不用读写localStorage
,直接操作内存,所以相比方式1,速度更快,更可靠
缺点: 同方式1一样,要注意globalData
污染
方式三:eventBus发布订阅
我们还可以通过实现一个中央事件总线,通过发布订阅实现跨页面通信。
// eventBus
export default class EventBus {
private static instance: EventBus
private listeners: Record<string, Function[]>
private constructor() {
this.listeners = {}
}
public static getInstance() {
if (!EventBus.instance) {
EventBus.instance = new EventBus()
}
return EventBus.instance
}
public on(event: string, callback: Function) {
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event].push(callback)
}
public off(event: string, callback: Function) {
if (!this.listeners[event]) {
return
}
const index = this.listeners[event].findIndex((listener) => listener === callback)
if (index !== -1) {
this.listeners[event].splice(index, 1)
}
}
public emit(event: string, ...args: any[]) {
if (!this.listeners[event]) {
return
}
this.listeners[event].forEach((listener) => listener(...args))
}
}
// app.ts
import EventBus from './utils/eventBus'
// 注册全局事件总线
App.config.globalProperties.$bus = EventBus.getInstance()
// 页面一
import { onMounted, ref, getCurrentInstance } from 'vue'
import taro, { useDidShow } from '@tarojs/taro'
const $bus = getCurrentInstance()?.appContext.config.globalProperties.$bus
onMounted(() => {
// 订阅登录状态
isLogin.value = $bus.on('loginStatus', (status: boolean) => {
console.log('$bus', status)
isLogin.value = status
})
})
// 登录页
import taro from '@tarojs/taro'
import { getCurrentInstance } from 'vue'
const $bus = getCurrentInstance()?.appContext.config.globalProperties.$bus
const login = () => {
taro.login({
success: function (res) {
console.log('登录成功', res)
// 发布登录状态
$bus.emit('loginStatus', true)
taro.navigateBack()
},
fail: function (res) {
console.log('登录失败', res)
}
})
}
这种方式看着是比前两种优雅了不少,但缺点是需要维护发布的事件,避免重复绑定。
方式四:Taro.eventCenter(taro提供的发布订阅)
Taro 提供了 Taro.Events
来实现消息机制,同时 Taro 还提供了一个全局消息中心 Taro.eventCenter
以供使用,它是 Taro.Events
的实例
import Taro, { Events } from '@tarojs/taro'
const events = new Events()
// 监听一个事件,接受参数
events.on('eventName', (arg) => {
// doSth
})
// 监听同个事件,同时绑定多个 handler
events.on('eventName', handler1)
events.on('eventName', handler2)
events.on('eventName', handler3)
// 触发一个事件,传参
events.trigger('eventName', arg)
// 触发事件,传入多个参数
events.trigger('eventName', arg1, arg2, ...)
// 取消监听一个事件
events.off('eventName')
// 取消监听一个事件某个 handler
events.off('eventName', handler1)
// 取消监听所有事件
events.off()
// 页面一
onMounted(() => {
// 订阅登录状态
taro.eventCenter.on('loginStatusTaro', (status: boolean) => {
console.log('eventCenter', status)
isLogin.value = status
})
})
// 登录页
const login = () => {
taro.login({
success: function (res) {
console.log('登录成功', res)
// 向首页发送数据
// eventChannel.emit('acceptDataFromLoginPage', { data: res.code, loginStatus: true })
// 触发事件,传递参数
taro.eventCenter.trigger('loginStatusTaro', true)
// 发布登录状态
// $bus.emit('loginStatus', true)
taro.navigateBack()
},
fail: function (res) {
console.log('登录失败', res)
}
})
}
方式五:小程序的EventChannel
页面间事件通信通道
- EventChannel.emit(string eventName, any args):触发一个事件
- EventChannel.on(string eventName, function fn):持续监听一个事件
- EventChannel.once(string eventName, function fn):监听一个事件一次,触发后失效
- EventChannel.off(string eventName, function fn):取消监听一个事件。给出第二个参数时,只取消给出的监听函数,否则取消所有监听函数
EventChannel
借助wx.navigateTo
方法,在两个页面之间构建起了数据通道,互相可以通过“派发事件”及“注册这些事件的监听器”来实现基于事件的页面通信。
// 页面一
const gotoLogin = () => {
taro.navigateTo({
url: '/pages/login/index',
events: {
// 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
acceptDataFromLoginPage: function(data) {
console.log('来自登录页的数据', data)
isLogin.value = data.loginStatus
},
},
success: function(res) {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit('acceptDataFromIndexPage', { data: 'nanjiu from index' })
}
})
}
// 登录页
import taro, { getCurrentPages } from '@tarojs/taro'
const current = getCurrentPages().pop()
const eventChannel = current?.getOpenerEventChannel()
eventChannel.on('acceptDataFromIndexPage', function(data) {
console.log('来自首页的数据', data)
})
const login = () => {
taro.login({
success: function (res) {
console.log('登录成功', res)
eventChannel.emit('acceptDataFromLoginPage', { data: res.code, loginStatus: true })
taro.navigateBack()
},
fail: function (res) {
console.log('登录失败', res)
}
})
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。