在 Taro(尤其是使用 React + Taro)中,如果你在 app.js
中异步请求接口设置全局变量,比如用户信息、系统配置等,子页面过早加载可能会访问到尚未设置的变量,导致报错。
🔧 问题原因
Taro 的页面加载是异步并行的。如果你在 app.js
的 onLaunch
或 componentDidMount
中异步请求接口,子页面可能已经挂载并开始执行 onLoad
甚至 render
,这时候你期望的全局变量还没准备好。
✅ 解决方案汇总(推荐程度从高到低)
✅ 1. 使用全局状态管理(推荐:react
+ zustand
或 redux
)
将全局变量(比如用户信息)存入全局状态管理中,并在子页面中使用加载状态判断:
1 2 3 4 5 6 7 8 |
// store/user.ts(Zustand 示例) import { create } from 'zustand' export const useUserStore = create((set) => ({ userInfo: null, setUserInfo: (data) => set({ userInfo: data }), })) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// app.tsx import { useEffect } from 'react' import Taro from '@tarojs/taro' import { useUserStore } from './store/user' function App(props) { const setUserInfo = useUserStore(state => state.setUserInfo) useEffect(() => { Taro.request({ url: 'https://example.com/api/user', }).then((res) => { setUserInfo(res.data) }) }, []) return props.children } |
1 2 3 4 5 6 7 8 9 10 11 |
// 子页面 import { useUserStore } from '../../store/user' const Page = () => { const userInfo = useUserStore(state => state.userInfo) if (!userInfo) return <View>加载中...</View> return <View>欢迎 {userInfo.name}</View> } |
✅ 2. 封装异步初始化流程,子页面等待全局变量准备完毕再执行逻辑
在 app.js
中封装 Promise,子页面等到准备就绪后再执行:
1 2 3 4 5 6 7 8 9 10 11 |
// app.js App({ onLaunch() { this.globalDataReady = this.initGlobalData() }, async initGlobalData() { const res = await Taro.request({ url: '...' }) this.globalData = { userInfo: res.data } } }) |
1 2 3 4 5 6 7 8 9 10 |
// 子页面 Page({ async onLoad() { const app = getApp() await app.globalDataReady const userInfo = app.globalData.userInfo // 再进行渲染或逻辑处理 } }) |
✅ 3. 使用事件机制通知全局变量已准备
Taro 提供了 Taro.eventCenter
(或用小程序原生的 EventBus
),可以在 app.js
设置完成后通知子页面。
1 2 3 4 5 6 |
// app.js Taro.request({ url: '...' }).then((res) => { this.globalData = { userInfo: res.data } Taro.eventCenter.trigger('globalReady', res.data) }) |
1 2 3 4 5 |
// 子页面 Taro.eventCenter.once('globalReady', (userInfo) => { this.setState({ userInfo }) }) |
✅ 4. 子页面中请求全局变量,如果还没设置则等待重试
不推荐频繁轮询,但可以设一个 retry 机制:
1 2 3 4 5 6 7 8 9 |
async function waitForGlobalData(maxTries = 10, delay = 100) { const app = getApp() for (let i = 0; i < maxTries; i++) { if (app.globalData?.userInfo) return app.globalData.userInfo await new Promise((res) => setTimeout(res, delay)) } throw new Error('globalData userInfo not ready') } |
🚫 不推荐的做法
- 直接
getApp().globalData.userInfo
然后使用,不判断是否准备好。 - 用
setTimeout
强行延迟使用,不可控、易出 bug。
✅ 最推荐做法
- 如果你使用的是 React 框架,建议使用 Zustand(或 Redux)配合
loading
状态。 - 如果是原生小程序风格,也建议封装 Promise,或使用事件通知机制。