第12节——生命周期

news2024/11/25 13:55:02

一、概念

生命周期指 React 组件从装载至卸载的全过程,这个过程内置多个函数供开发者在组件的不同阶段执行需要的逻辑。

状态组件主要通过 3 个生命周期阶段来管理,分别是 挂载阶段(MOUNTING),更新阶段(UPDATING)和卸载阶段(UNMOUNT)。

从纵向划分,可以划分为 Render 阶段和 Commit 阶段

Render 阶段:纯净且不包含副作用,可能会被 React 暂停、中止或重新启动

Commit 阶段:可以使用 DOM,运行副作用,安排更新

img

二、挂载阶段触发生命周期

1、constructor

构造函数(这个并不是react的生命周期)

语法

class A extends React.Component {
 /**
   *
   * @param {*} props 继承 React.Component 的属性方法,它是不可变的 read-only
   * @param {*} context 全局上下文。
   * @param {*} updater 包含一些更新方法的对象
   * 比如this.setState 最终调用的是 this.updater.enqueueSetState
   */
  constructor(props, context, updater) {
     super(props) 
  }
}

触发时机

在组件初始化的时候触发一次。

使用场景

1、设置初始化状态

因为组件的生命周期中任何函数都可能要访问 State,那么整个周期中第一个被调用的构造函数便是初始化 state 最理想的地方;

2、绑定成员函数上下文引用

因为在 ES6 语法下,类的每个成员函数在执行时的 this 并不是和类实例自动绑定的;

而在构造函数中 this 就是当前组件实例,所以,为了方便将来调用,可以将这个实例的特定函数绑定 this 为当前类实例;

建议定义函数方法时直接使用箭头函数,就无须在构造函数中进行函数的 bind 操作。

2、static getDerivedStateFromProps

语法

  /**
   *
   * @param {*} nextProps 当前的props
   * @param {*} prevState 修改前的state
   * 该生命周期函数必须有返回值,
   * 它需要返回一个对象来更新 State,
   */
  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.num) {
      return {
        ...prevState,
        num: nextProps.num * 2
      }
    }
    return prevState;
  }

触发时机

该函数会在组件化实例化后和重新渲染前调用(生成 VirtualDOM 之后,实际 DOM 挂载之前),意味着无论是父组件的更新、props 的变化或通过 setState 更新组件内部的 State,它都会被调用。

注意

在组件装载和更新阶段都会触发。

如果父组件导致了组件的重新渲染,即使属性没有更新,这一方法也会被触发;

如果你只想处理 props 的前后变化,你需要将上一个 props 值存到 state 里作为镜像;

该生命周期函数是一个静态函数,所以函数体内无法访问指向当前组件实例的指针 this;

使用场景

该生命周期函数被设计成静态方法的目的是为了保持该方法的纯粹。通过根据父组件输入的 props 按需更新 state,这种 state 叫做衍生 state,返回的对象就是要增量更新的 state,除此之外不应该在里面执行任何操作。

通过设计成静态方法,能够起到限制开发者无法访问 this 也就是实例的作用,这样就不能在里面调用实例方法或者 setState 以破坏该生命周期函数的功能。

这个生命周期函数也经历了一些波折,原本它是被设计成 初始化、父组件更新 和 接收到 Props 才会触发,现在只要渲染就会触发,也就是 初始化 和 更新阶段 都会触发

import React from "react";

export default class LearnLife extends React.Component {
  state = {
    num: 1,
  };
  /**
   *
   * @param {*} nextProps 当前的props
   * @param {*} prevState 修改前的state
   * 该生命周期函数必须有返回值,
   * 它需要返回一个对象来更新 State,
   * 或者返回 null 表达不更新
   */
  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.num) {
      return {
        ...prevState,
        num: nextProps.num * 2
      }
    }
    return nextProps;
  }

  render() {
    return (
      <div>
        <div>父组件2倍的num -- {this.state.num}</div>
        <div>父组件传过来的值 -- {this.props.num}</div>
      </div>
    );
  }
}

3、UNSAFE_componentWillMount(componentWillMount)

此生命周期函数将在 React v17 正式废弃。

触发时机

在构造函数和装载组件(将 DOM 树渲染到浏览器中)之间触发。装载组件后将执行 render 渲染函数。因此在此生命周期函数里使用 setState 同步设置组件内部状态 state 将不会触发重新渲染。

注意

避免在该方法中引入任何的副作用(Effects)或订阅(Subscription)。对于这些使用场景,建议提前到构造函数中

4、render

渲染函数(这个并不是react的生命周期)

概念

它是一个仅仅用于渲染的纯函数,返回值完全取决于 state 和 props,不能在函数中任何修改 state、props、请求数据等具有副作用的操作,不能读写 DOM 信息,也不能和浏览器进行交互(例如 setTimeout)。如果需要和浏览器交互,在 componentDidMount() 中或者其它生命周期方法完成相关事务。

此渲染函数并不做实际的渲染动作(渲染到 DOM 树),它返回的只是一个 JSX 的描述对象(及组件实例),何时进行真正的渲染是有 React 库决定的。而 React 肯定是要把所有组件返回的结果综合起来,才能知道如何产生真实的 DOM 树。也就是说,只有 React 库调用所有组件的渲染函数之后,才有可能完成 DOM 装载,这时候才会依次调用 componentDidMount 函数作为装载的收尾。

注意

请勿在此函数中使用 setState 方法;

请勿在此函数中修改 props、state 以及数据请求等具有副作用的操作

5、componentDidMount

语法

class Life extends React.component {

  componentDidMount () {
    
  }
}

触发时机

组件完全挂载到网页上后触发。

使用场景

发送网络请求;任何依赖于 DOM 的初始化操作;添加事件监听;如果使用了 Redux 之类的数据管理工具,也能触发 action 处理数据变化逻辑

为什么在这个生命周期里面发送网络请求

之所以在 componentDidMount 中而不是在构造函数中进行数据请求的原因在于:如果数据加载完毕后,即 state 已经有值了,但是组件还没有渲染出来,会报错。但是这里有一些把数据拉取提前到 constructor 函数的思路:在 contructor 函数中,通过 Promise 来进行数据的请求,并且绑定到当前实例对象上,然后在 componentDidMount 中执行 Promise 把数据更新到 state 上

三、更新阶段触发的生命周期

1、UNSAFE_componentWillReceiveProps(componentWillReceiveProps)

此生命周期函数将在 React v17 正式废弃。推荐使用 static getDerivedStateFromProps() 代替

语法

/*
 nextProps 当前的props

*/
componentWillReceiveProps(nextProps) {
   
}

触发时机

当父组件的渲染函数被调用,在渲染函数中被渲染的子组件就会经历更新阶段,不管父组件传给子组件的 props 有没有改变,都会触发该生命周期函数。当组件内部调用 setState 更新内部状态 state 时触发更新阶段不会触发该函数

2、shouldComponentUpdate

语法

在一个更新生命周期中,组件及其子组件将根据该方法返回的布尔值来决定是否继续这次更新过程(重新渲染)。这样你可以在必要的时候阻止组件的渲染生命周期(Render Lifecycle)方法,避免不必要的渲染

 /**
   * 
   * @param {*} nextProps 当前的props
   * @param {*} nextState 当前的state
   * @returns 
   		根据逻辑判断返回 true 表示继续进行组件渲染,否则将停止组件渲染过程。
     默认返回 true,也就是说,只要组件触发了更新,组件就一定会更新
   */
  shouldComponentUpdate (nextProps, nextState) {

    return true
  }

触发时机

每次组件因为 state 和 props 变化而更新时,在重新渲染前该生命周期函数都会触发,让 React 知道当前 state 或 props 的改变是否影响组件的输出(渲染)。

使用场景

如果性能是个瓶颈,尤其是有几十个甚至上百个组件的时候,使用 shouldComponentUpdate 可以优化渲染效率,提升应用的性能;

使用 React.PureComponent 组件基类能自动实现一个 shouldComponentUpdate 生命周期钩子,可以默认为组件更新校验,但是只会对更新数据进行浅层对照;

通常用于条件渲染,优化渲染的性能

例子

实现点击按钮num+1 当num为偶数据的时候视图不渲染

import React from "react";

export default class ShouldComponentUpdate extends React.Component {
  state = {
    num: 1,
  };

  /**
   *
   * @param {*} nextProps 当前的props
   * @param {*} nextState 当前的state
   * @returns return false 不渲染 true 渲染
   */
  shouldComponentUpdate(nextProps, nextState) {
    /**
     * 偶数的时候不渲染
     */
    return nextState.num % 2 !== 0;
  }

  add() {
    this.setState({
      num: this.state.num + 1,
    });
  }

  render() {
    return (
      <div>
        <div>{this.state.num}</div>
        <button onClick={() => this.add()}>+1</button>
      </div>
    );
  }
}

3、UNSAFE_componentWillUpdate(componentWillUpdate)

此生命周期函数将在 React v17 正式废弃。建议使用componentWillRreceiveProps代替

触发时机

这个方法是一个更新生命周期中重新渲染执行之前的最后一个方法。你已经拥有了下一个属性和状态,他们可以在这个方法中任由你处置。你可以利用这个方法在渲染之前进行最后的准备

注意

此钩子函数在初始化渲染的时候不会被触发;

请勿在此函数中使用 setState 方法,会导致循环调用。

4、render

渲染函数。与上文所提及的 render 生命周期函数一致,用于输出 JSX 并经过 React 处理后渲染至浏览器

5、getSnapshotBeforeUpdate

语法

  /**
   * 
   * @param {*} prevProps 更新前的props
   * @param {*} prevState 更新前的state
   * @returns 返回值可以在componentDidUpdate中接收
   */
  getSnapshotBeforeUpdate (prevProps, prevState) {

    return null
  }

触发时机

该生命周期函数会在组件即将挂载时触发,它的触发在 render 渲染函数之后。由此可见,render 函数并没有完成挂载操作,而是进行构建抽象 UI(也就是 Virtual DOM)的工作。该生命周期函数执行完毕后就会立即触发 componentDidUpdate 生命周期钩子。

使用场景

该生命周期函数能让你捕获某些从 DOM 中才能获取的(可能会变更的)信息(例如,元素重新渲染后页面各种定位位置的变更等)。比如网页滚动位置,不需要它持久化,只需要在组件更新以后能够恢复原来的位置即可

注意

该生命周期函数返回的值将作为第三个参数传递给 componentDidUpdate,我们可以利用这个通道保存一些不需要持久化的状态,用完即可舍弃。

(这个生命周期不是经常需要的,但可以用于在恢复期间手动保存滚动位置的情况。)

该函数的出现是为了 React 17 的异步渲染而准备的

6、componentDidUpdate

语法

  /**
   * 
   * @param {*} prevProps 更新前的props
   * @param {*} prevState 更新前的state
   * @param {*} snapshot getSnapshotBeforeUpdate传过来的
   */
  componentDidUpdate (prevProps, prevState, snapshot) {

  }

触发时机

组件每次重新渲染后触发,相当于首次渲染(初始化)之后触发 componentDidMount

注意

在该生命周期中使用 setState 时,必须加 if 条件判断,通过判断 prevProps、prevState 和 this.state 之间的数据变化,来判断是否执行相关的 state 变更逻辑,这使得尽管在 componentDidUpdate 中调用了 setState 进行再更新,但是直至条件不成立,就不会造成程序死循环。

此生命周期函数不会在初始化渲染的时候触发

总结

相比装载阶段的生命周期函数,更新阶段的生命周期函数使用的相对来说要少一些。常用的是 getDerivedStateFromProps、shouldComponentUpdate,前者经常用于根据新 props 的数据去设置组件的 State,而后者则是常用于优化,避免不必要的渲染

四、卸载阶段

1、componentWillUnmount

语法

componentWillUnmount () {
  
}

触发时机

在组件卸载和销毁之前触发。可以利用这个生命周期方法去执行任何清理任务

使用场景

用于注销事件监听器;取消网络请求;取消定时器;解绑 DOM 事件。

注意

在该方法中调用 setState 不会触发 render,因为所有的更新队列,更新状态都被重置为 null

五、为什么react17后删除了那些生命周期

react 打算在17版本推出新的 Async Rendering,提出一种可被打断的生命周期,而可以被打断的阶段正是实际 dom 挂载之前的虚拟 dom 构建阶段,也就是要被去掉的三个生命周期。本身这三个生命周期所表达的含义是没有问题的,但 react 官方认为我们(开发者)也许在这三个函数中编写了有副作用的代码,所以要替换掉这三个生命周期,因为这三个生命周期可能在一次 render 中被反复调用多次

六、捕获错误(一般了解)

1、static getDerivedStateFromError

语法

import React from "react";

export default class GetDerivedStateFromError extends React.Component {
  
  /**
   * 
   * @param {*} error 错误信息
   * @returns 
   * 它接收抛出的错误作为参数并且需要返回值用于更新 State
   */
  static getDerivedStateFromError(error) {
    return 
  }
  render () {
    return (
      <div>
        <div>1</div>
      </div>
    )
  }
}

触发时机

该生命周期函数会在子孙组件抛出错误时执行

使用场景+注意点

该生命周期函数中可用于修改 state 以显示错误提醒的 UI,或者将错误信息发送到服务端进行 Log 用于后期分析;

在捕获到错误的瞬间,React 会在这次渲染周期中将这个组件渲染为 null,这就有可能导致他的父组件设置他上面的 ref 获得 null 而导致一些问题

和componentDidCatch相比

这个生命周期函数与 getDerivedStateFromProps 类似,唯一的区别是他只有在出现错误的时候才触发,他相对于 componentDidCatch 的优势是在当前的渲染周期中就可以修改 State,以在当前渲染就可以出现错误的 UI,而不需要一个 null 的中间态。

而这个方法的出现,也意味着以后出现错误的时候,修改 state 应该放在这里去做,而后续收集错误信息之类的放到 componentDidCatch 里面

例子

import React from "react";
import AComponent from './a'

export default class ErrorComponent extends React.Component {
  
  state = {
    isShowError: false
  }

  /**
   * 
   * @param {*} error 错误信息
   * @returns 
   * 它接收抛出的错误作为参数并且需要返回值用于更新 State
   */
  static getDerivedStateFromError(error) {
    console.log(error)
    return {
      isShowError: true
    }
  }
  render () {
    if (this.state.isShowError) {
      return (
        <div>组件内部发生错误</div>
      )
    }
    return (
      <div>
        <div>1</div>
        {/* 当前组件会触发一个错误 */}
        <AComponent></AComponent>
      </div>
    )
  }
}

2、componentDidCatch

语法

componentDidCatch () {
  
}

触发时机

该生命周期函数会在子孙组件抛出错误时触发。

相关使用和 getDerivedStateFromError 几乎一致

3、错误边界(Error Boundary)

是 React 组件,并不是损坏的组件树。错误边界捕捉发生在子组件树中任意地方的 JavaScript 错误,打印错误日志,并且显示回退的用户界面。错误边界捕捉渲染期间、在生命周期方法中和在它们之下整棵树的构造函数中的错误。

可以定制一个只有 componentDidCatch 生命周期函数的 ErrorBoundary 组件。当捕获错误,则显示错误提示,如果没有捕获到错误,则显示子组件。

将需要捕获错误的组件作为 ErrorBoundary 的子组件渲染,一旦子组件抛出错误,整个应用依然不会崩溃,而是被 ErrorBoundary 捕获

import React, { Component } from 'react';

class ErrorBoundary extends Component {
  state = { hasError: false };

  render() {
    if (this.state.hasError) {
      return <h1>发生错误.</h1>;
    }
    return this.props.children;
  }

  componentDidCatch(error, info) {
    this.setState({ hasError: true });
  }
}

export default ErrorBoundary;
定制一个只有 componentDidCatch 生命周期函数的 ErrorBoundary 组件。当捕获错误,则显示错误提示,如果没有捕获到错误,则显示子组件。



将需要捕获错误的组件作为 ErrorBoundary 的子组件渲染,一旦子组件抛出错误,整个应用依然不会崩溃,而是被 ErrorBoundary 捕获

```jsx
import React, { Component } from 'react';

class ErrorBoundary extends Component {
  state = { hasError: false };

  render() {
    if (this.state.hasError) {
      return <h1>发生错误.</h1>;
    }
    return this.props.children;
  }

  componentDidCatch(error, info) {
    this.setState({ hasError: true });
  }
}

export default ErrorBoundary;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/970070.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AJAX学习笔记3练习

AJAX学习笔记2发送Post请求_biubiubiu0706的博客-CSDN博客 1.验证用户名是否可用 需求,用户输入用户名,失去焦点-->onblur失去焦点事件,发送AJAX POST请求,验证用户名是否可用 新建表 前端页面 WEB-INF下新建lib包引入依赖,要用JDBC 后端代码 package com.web;import jav…

ModaHub魔搭社区:自动化机器学习框架AutoML

AutoML 自动化机器学习AutoML 是机器学习中一个相对较新的领域,它主要将机器学习中所有耗时过程自动化,如数据预处理、最佳算法选择、超参数调整等,这样可节约大量时间在建立机器学习模型过程中。 自动机器学习 AutoML: 对于 ,令 表示特征向量, 表示对应的目标值。给定…

基于SpringCloudAlibaba实现的NacosConfig

概述 Nacos除了实现了服务的注册发现之外&#xff0c;还将配置中心功能整合在了一起。通过Nacos的配置管理功能&#xff0c;我们可以将整个架构体系内的所有配置都集中在Nacos中存储。这样做的好处主要有以下几点&#xff1a; 分离的多环境配置&#xff0c;可以更灵活的管理权…

基于Java+SpringBoot+Vue前后端分离大学生智能消费记账系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

基于Java+SpringBoot+Vue前后端分离农商对接系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

机器人中的数值优化(十三)——QP二次规划

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

MLOps:掌握机器学习部署:Docker、Kubernetes、Helm 现代 Web 框架

介绍&#xff1a; 在机器学习的动态世界中&#xff0c;从开发模型到将其投入生产的过程通常被认为是复杂且多方面的。 然而&#xff0c;随着 Docker、Kubernetes 等工具以及 FastAPI、Streamlit 和 Gradio 等用户友好的 Web 框架的出现&#xff0c;这一过程变得比以往更加简化…

CS420 课程笔记 P3 - 计数系统基础和 Hex, Decimal, Binary 进制

文章目录 IntroductionInspirationWhy base systemsBinary & HexCounting in binaryAdditional resources Introduction 笔记作者 tips&#xff1a;这一节是关于进制的讲解&#xff0c;推荐观看原视频或学会二进制的读者跳过这一篇&#xff01; 本节课将介绍基本的计算机系…

台球击球角度公式. 包含数学推导

第一步. 物理来分析. 第二步. 数学计算.

【jsvue】联合gtp仿写一个简单的vue框架,以此深度学习JavaScript

用 gtp 学习 Vue 生命周期的原理 lifecycle.js function Vue(options) {// 将选项保存到实例的 $options 属性中this.$options options;// 若存在 beforeCreate 钩子函数&#xff0c;则调用之if (typeof options.beforeCreate function) {options.beforeCreate.call(this);…

Qt 5.15编译及集成Crypto++ 8.7.0笔记

一、背景 为使用AES加密库&#xff08;AES/CBC加解密&#xff09;&#xff0c;选用Crypto 库&#xff08;官网&#xff09;。   最新Crypto C库依次为&#xff1a;8.8.0版本&#xff08;2023-6-25&#xff09;、8.7.0&#xff08;2022-8-7&#xff09;和8.6.0&#xff08;202…

MySQL数据库——多表查询(4)-实例练习、多表查询总结

目录 练习1 练习2 总结 1.多表关系 2.多表查询 进行案例练习之前&#xff0c;需要先增加一个表格 create table salgrade(grade int,losal int, -- 对应等级的最低薪资hisal int -- 对应等级的最高薪资 ) comment 薪资等级表;insert into salgrade values (1,0,3000)…

【高等数学1800】——一元函数微分学的应用

本文仅用于个人学习记录&#xff0c;使用的教材为汤家凤老师的《高等数学辅导讲义》。本文无任何盈利或者赚取个人声望的目的&#xff0c;如有侵权&#xff0c;请联系删除&#xff01; 文章目录 一、入门练习二、基础练习 一、入门练习 本题需要注意在写凸区间时应该是闭区间。…

华为云云服务器评测|前端开发同学的初体验部署贪吃蛇!

文章目录 前言初配置初始化宝塔面板安装Nginx、上传项目修改nginx配置效果展示 前言 作为一名前端同学&#xff0c;我的技能和日常工作主要集中在用户界面的设计和交互上&#xff0c;与服务器产品相关的经验相对较少。正好看到了咱们华为云开展的评测活动&#xff0c;决定借着…

路径规划 | 图解Lazy Theta*算法(附ROS C++/Python/Matlab仿真)

目录 0 专栏介绍1 Theta*算法局限性2 Lazy Theta*算法原理3 Theta* VS. Lazy Theta*4 仿真实现4.1 ROS C实现4.2 Python实现4.3 Matlab实现 0 专栏介绍 &#x1f525;附C/Python/Matlab全套代码&#x1f525;课程设计、毕业设计、创新竞赛必备&#xff01;详细介绍全局规划(图…

【nerfStudio】2-基于nerfStudio训练第一个NeRF模型

训练第一个NeRF模型 训练和运行查看器 以下内容将训练一个_nerfacto_模型: # 下载一些测试数据: ns-download-data nerfstudio --capture-name=poster # 训练模型 ns-train nerfacto --data data/nerfstudio/poster如果一切正常,应该会看到如下的训练进度: 在终端末尾的…

FPGA GTX aurora 8b/10b编解码 PCIE 板对板视频传输,提供2套工程源码加QT上位机源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、GTX 全网最细解读GTX 基本结构GTX 发送和接收处理流程GTX 的参考时钟GTX 发送接口GTX 接收接口GTX IP核调用和使用 4、设计思路框架视频源选择ADV7611解码芯片配置及采集动态彩条视频数据组包GTX aurora 8b/10b数据…

基于永磁同步发电机的风力发电系统连接到可控的三相整流器(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

机器学习——主成分分析PCA

机器学习——主成分分析PCA 文章目录 前言一、原理1.1. PCA介绍1.2. 算法步骤 二、代码实现2.1. 基于numpy实现2.2. 使用sklearn实现2.3. 观察方差分布2.4. 指定方差百分比求分量数 三、优&#xff0c;缺点3.1. 优点3.2. 缺点 总结 前言 当面对一个特征值极多的数据集时&#…

【核磁共振成像】观共享重建

目录 一、K空间关键孔技术-数据采集二、BRISK技术三、TRICKS技术四、实时成像和滑动窗重建五、心电触发电影(CINE)采集六、分段心脏采集和观共享 一、K空间关键孔技术-数据采集 对于笛卡尔K空间&#xff0c;一个相位编码行有时称为一个K空间观。一般情况下&#xff0c;每帧图像…