2025-04-06
小程序
00

目录

页面生命周期
初始化
更新时
卸载时
react16.4新增生命周期
小程序的其它钩子
函数组件生命周期
组件生命周期
错误处理钩子
APP生命周期

本文以taro@3.3.3react@16.10讲解Taro开发小程序的生命周期,

  • 页面生命周期
  • 组件生命周期
  • APP生命周期
  • 错误处理钩子

Tarojs官网

页面生命周期

初始化

class页面组件初始化时生命周期

tsx
import React from 'react'; import { View } from '@tarojs/components'; class Index extends React.Component { // 可以使用所有的 React 生命周期方法 constructor(...args: any[]) { console.log('1. constructor 仅初始化执行一次'); super(args); } componentWillMount() { console.log('2. componentWillMount 仅初始化执行一次', getCurrentInstance().router?.params); } componentDidMount() { console.log('4. componentDidMount 仅初始化执行一次'); } // 小程序生命周期 onLoad onLoad(options: unknown) { console.log('5. onLoad 仅初始化执行一次', '这里能拿到页面参数', options); } // 小程序生命周期 onShow onShow() { console.log('6. onShow'); } // 小程序生命周期 onReady onReady() { console.log('7. onReady 仅初始化执行一次'); } render() { console.log('3. render'); return <View className='index' > <View>AAAA</View> </View>; } } export default Index;

补充

关于页面参数,Taro非常灵活,可以在任意生命周期钩子 如 constructor中获取页面参数

tsx
import { getCurrentInstance } from '@tarojs/taro'; class Index extends React.Component { constructor(...args: any[]) { console.log(getCurrentInstance().router?.params); super(args); } } export default Index;

更新时

tsx
import React from 'react'; import { View } from '@tarojs/components'; class Index extends React.Component { state = { count : 0, } shouldComponentUpdate() { console.log('1. shouldComponentUpdate', this.state.count === 2); if (this.state.count === 2) { return false; } return true; } componentWillUpdate() { console.log('2. componentWillUpdate 随页面或子组件更新执行多次'); } componentDidUpdate(prevProps: Readonly<{}>, prevState: Readonly<{}>, snapshot?: any): void { console.log('4. componentDidUpdate 随页面或子组件更新执行多次'); } render() { console.log('3. render'); return <View className='index' > <View>{this.state.count}--</View> </View>; } } export default Index;

注意事项

  1. 这里面没有再列初始化的生命周期, 它们晚于初始化的生命周期
  2. shouldComponentUpdate 返回false 会阻断后面的生明周期执行

卸载时

小程序还有两个生命周期onShowonHide 对应taro页面组件中的componentDidShowcomponentDidHide

  • componentDidShow会在小程序切到前台, 返回当前页面执行
  • componentDidHide会在小程序切到后台, 或者跳转到其他页面执行

class页面组件卸载时生命周期

tsx
import React from 'react'; import { View } from '@tarojs/components'; class Index extends React.Component { onUnload() { console.log('onUnload'); // 实测这里不会触发 // 一般情况下建议使用 React 的 componentWillUnmount 生命周期处理页面卸载时的逻辑。 // 当某些特殊情况需要在页面的 onUnload 的同一个事件循环中实现逻辑时才使用它(如对小程序的生命周期执行顺序有强依赖关系时) } // 小程序生命周期 onHide componentDidHide() { console.log('onHide'); } componentWillUnmount() { console.log('componentWillUnmount'); } render() { return <View className='index' > <View>AAAA</View> </View>; } } export default Index;

页面卸载有两种场景

  1. 页面重定向
  2. 返回前一个页面

以上两种场景小程序的处理逻辑一致,实测表现如下

  • 微信小程序 和支付宝小程序 都不会触发小程序的 onUnload,所以我们只能用 componentWillUnmount
  • 当微信小程序会触发 componentWillUnmount
  • 支付宝小程序会依次触发componentDidHide、componentWillUnmount

注意一个特殊场景: 当页面重定向到自身页面,会先触发这个页面的卸载,然后重新执行这个页面的初始化生命周期

react16.4新增生命周期

我们知道react16.4新增了两个生命周期static getDerivedStateFromPropsgetSnapshotBeforeUpdate

  • static getDerivedStateFromProps
    • 它会取代了 componentWillMountcomponentWillUpdatecomponentWillReceiveProps
  • getSnapshotBeforeUpdate
    • render之后,在componentDidUpdate之前。
    • 在最近⼀次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获⼀些信息(例如,滚动位置)。

小程序的其它钩子

tsx
import React from 'react'; import { View } from '@tarojs/components'; class Index extends React.Component { // 对应 onPullDownRefresh, // 除了 componentDidShow/componentDidHide 之外, // 所有页面生命周期函数名都与小程序相对应 onPullDownRefresh() { console.log('onPullDownRefresh'); } render() { return <View className='index' > <View>AAAA</View> </View>; } } export default Index;

函数组件生命周期

函数组件,页面生命周期

tsx
import React, { useEffect, useState } from 'react'; import { View } from '@tarojs/components'; import { useReady, useDidShow, useDidHide, usePullDownRefresh } from '@tarojs/taro'; function Index() { // 可以使用所有的 React Hooks const [count, setCount] = useState(0); useEffect(() => { // 相当于componentDidMount console.log('useEffect 1'); const timer = setInterval(() => { setCount((_count) => { const newCount = _count + 1; if (newCount === 2) { clearInterval(timer); } return newCount; }); }, 1000); return () => { // 相当于 componentWillUnmount console.log('clear 1'); }; }, []); useEffect(() => { // 相当于componentDidMount + componentDidUpdate(有依赖项的情况) console.log('useEffect 2'); }, [count]); /* 对应 onLoad Taro v3.5.0+ 开始支持 useLoad(() => { console.log('onLoad'); }); */ // 对应 onReady useReady(() => { console.log('useReady'); }); // 对应 onShow useDidShow(() => { console.log('useDidShow'); }); // 对应 onHide useDidHide(() => { console.log('useDidHide'); }); // Taro 对所有小程序页面生命周期都实现了对应的自定义 React Hooks 进行支持 // 详情可查阅:【Hooks】 usePullDownRefresh(() => {}); return <View className='index' > <View>XX</View> <View>{count}</View> </View>; } export default Index;

这里我们对标一下页面组件

  • 初始化时的 constructorcomponentWillMount 钩子没有了
  • 小程序的钩子 useLoad 目前项目不支持,需要升级 taro3.5+
  • 更新时
    • 没有shoudleComponentUpdate
      • 你需要对state 进行diff 决定是否setData
      • 对于props变更需要在父组件同级使用 useMemo包裹子组件
    • 没有componentWillUpdate
    • useEffect的第二个参数是更新依赖,相当于componentDidUpdate
  • 卸载时 useEffect的返回函数 对应 componentWillUnmount

组件生命周期

组件的生命周期钩子与页面相比

  • 少了小程序的页面级别钩子onLoadonReadyonShowonHide
  • 多了一个componentWillReceiveProps钩子
    • 如果props改变,会依次触发
    • componentWillReceivePropsshouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate
  • 父子组件钩子的执行顺序
    • 初始化时image.png
    • 更新时顺序
    • 卸载时 先卸载父组件,再卸载子组件;支付宝小程序页面组件多一个 componentDidHide
  • static getDerivedStateFromProps会替代三个will,比页面级组件多了一个componentWillReceiveProps

错误处理钩子

react有两个处理错误的生命周期 static getDerivedStateFromErrorcomponentDidCatch 它们的作用是

  • static getDerivedStateFromError 它将抛出错误错误并返回一个值以更新state, 通常是UI降级使用
  • componentDidCatch 处理副作用 如错误上报

以下面的代码,在taro3.3中实测

tsx
import React from 'react'; import { View } from '@tarojs/components'; import ClassComp from './ClassComp'; class Index extends React.Component { state = { isError : false, } static getDerivedStateFromError() { // 此生命周期在后代组件抛出错误后被调用。它将抛出错误错误并返回一个值以更新state; console.log('static getDerivedStateFromError--'); return { isError : true, }; } componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { // 主要用于副作用提交 如错误上报 console.log('componentDidCatch--'); } render() { return <View className='index' > <View>AAAA</View> {/* 子组件我做了特殊处理 渲染三秒后就报错 */} {this.state.isError ? '遇到了错误' : <ClassComp count={this.state.count}></ClassComp>} </View>; } } export default Index;

实测结果如下

  • static getDerivedStateFromError能捕捉子组件中同步渲染的错误。
  • 页面及子组件中异步的错误将被 Taro.onError捕获到。
  • 如果页面在渲染过成功发生同步报错,没办法捕获,会进紧接着触发页面的卸载componentWillUnmount以及子组件的卸载componentWillUnmount
  • 如果想捕获页面级错误有什么办法呢?可以在app.tsx中捕获吗?
  • 在支付宝小程序中当跳转到不存在的页面,Taro.onError也会捕获到.
  • app的onUnhandledRejection可以捕获页面及子组件的Promise报错

总结

  • 页面中同步渲染过程出现错误尤其要注意!!因为没有狗子能捕获它
  • 可以通过封装一个ErrorBound组件包裹子组件,时期能恢复过来

ErrorBound.tsx

tsx
import React from 'react'; import { View } from '@tarojs/components'; class Index extends React.Component<{children: React.ReactNode}>{ state = { isError : false, } componentWillUnmount(): void { console.log('ErrorBound componentWillUnmount'); } static getDerivedStateFromError() { return { isError : true, }; } componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { // 主要用于副作用提交 如错误上报 console.log('ErrorBound componentDidCatch--'); } restore = () =>{ this.setState({isError: false}) } render() { return <View className='index' > <View>ErrorBound</View> {this.state.isError ? <View onClick={this.restore}>遇到了错误,点击重试</View> : this.props.children} </View>; } } export default Index;

APP生命周期

tsx
import React, { Component } from 'react' class App extends Component { // 可以使用所有的 React 生命周期方法 componentDidMount() { console.log('1. APP componentDidMount'); } // 对应 onLaunch onLaunch(launchParams) { console.log('2. APP onLaunch'); } // 对应 onShow componentDidShow() { console.log('3. APP componentDidShow'); } // 对应 onHide componentDidHide() { console.log('APP componentDidHide'); } // Taro v3.5.0+ 开始支持 // onError (error) {} // Taro v3.5.10+ 开始支持 // onUnhandledRejection (Object){} render() { // 在入口组件不会渲染任何内容,但我们可以在这里做类似于状态管理的事情 return this.props.children } } export default App

我测试了下有以下结论

  1. APP的生命周期执行顺序 componentDidMountonLaunchcomponentDidShow, APP的生命周期早于页面的生命周期
  2. app.tsx里面不要写任何模版内容 除了context, 因此对应React页面更新的钩子,都不能用
  3. onPageNotFound 启动时页面不存在(测试微信会触发,支付宝不知道怎么测)
  • 跳转一个不存在的页面,不会触发
  • 这个应该是打开小程序时页面不存在触发

本文作者:郭郭同学

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!