react源码解析10.commit阶段

news2024/11/17 21:56:40

在render阶段的末尾会调用commitRoot(root);进入commit阶段,这里的root指的就是fiberRoot,然后会遍历render阶段生成的effectList,effectList上的Fiber节点保存着对应的props变化。之后会遍历effectList进行对应的dom操作和生命周期、hooks回调或销毁函数,各个函数做的事情如下

react源码10.1

在commitRoot函数中其实是调度了commitRootImpl函数

//ReactFiberWorkLoop.old.js
function commitRoot(root) {
  var renderPriorityLevel = getCurrentPriorityLevel();
  runWithPriority$1(ImmediatePriority$1, commitRootImpl.bind(null, root, renderPriorityLevel));
  return null;
}

在commitRootImpl的函数中主要分三个部分:

  • commit阶段前置工作

    1. 调用flushPassiveEffects执行完所有effect的任务

    2. 初始化相关变量

    3. 赋值firstEffect给后面遍历effectList用

//ReactFiberWorkLoop.old.js
do {
    // 调用flushPassiveEffects执行完所有effect的任务
    flushPassiveEffects();
  } while (rootWithPendingPassiveEffects !== null);

    //...

  // 重置变量 finishedWork指rooFiber
  root.finishedWork = null;
    //重置优先级
  root.finishedLanes = NoLanes;

  // Scheduler回调函数重置
  root.callbackNode = null;
  root.callbackId = NoLanes;

  // 重置全局变量
  if (root === workInProgressRoot) {
    workInProgressRoot = null;
    workInProgress = null;
    workInProgressRootRenderLanes = NoLanes;
  } else {
  }

    //rootFiber可能会有新的副作用 将它也加入到effectLis
  let firstEffect;
  if (finishedWork.effectTag > PerformedWork) {
    if (finishedWork.lastEffect !== null) {
      finishedWork.lastEffect.nextEffect = finishedWork;
      firstEffect = finishedWork.firstEffect;
    } else {
      firstEffect = finishedWork;
    }
  } else {
    firstEffect = finishedWork.firstEffect;
  }
  • mutation阶段

    ​ 遍历effectList分别执行三个方法commitBeforeMutationEffects、commitMutationEffects、commitLayoutEffects执行对应的dom操作和生命周期

    ​ 在介绍双缓存Fiber树的时候,我们在构建完workInProgress Fiber树之后会将fiberRoot的current指向workInProgress Fiber,让workInProgress Fiber成为current,这个步骤发生在commitMutationEffects函数执行之后,commitLayoutEffects之前,因为componentWillUnmount发生在commitMutationEffects函数中,这时还可以获取之前的Update,而componentDidMountcomponentDidUpdate会在commitLayoutEffects中执行,这时已经可以获取更新后的真实dom了

function commitRootImpl(root, renderPriorityLevel) {
    //...
    do {
      //...
      commitBeforeMutationEffects();
    } while (nextEffect !== null);

    do {
      //...
      commitMutationEffects(root, renderPriorityLevel);//commitMutationEffects
    } while (nextEffect !== null);

  root.current = finishedWork;//切换current Fiber树

  do {
      //...
      commitLayoutEffects(root, lanes);//commitLayoutEffects
    } while (nextEffect !== null);
    //...
}
  • mutation 后

    1. 根据rootDoesHavePassiveEffects赋值相关变量

    2. 执行flushSyncCallbackQueue处理componentDidMount等生命周期或者useLayoutEffect等同步任务

const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;

// 根据rootDoesHavePassiveEffects赋值相关变量
if (rootDoesHavePassiveEffects) {
  rootDoesHavePassiveEffects = false;
  rootWithPendingPassiveEffects = root;
  pendingPassiveEffectsLanes = lanes;
  pendingPassiveEffectsRenderPriority = renderPriorityLevel;
} else {}
//...

// 确保被调度
ensureRootIsScheduled(root, now());

// ...

// 执行flushSyncCallbackQueue处理componentDidMount等生命周期或者useLayoutEffect等同步任务
flushSyncCallbackQueue();

return null;

现在让我们来看看mutation阶段的三个函数分别做了什么事情

  • commitBeforeMutationEffects
    该函数主要做了如下两件事

    1. 执行getSnapshotBeforeUpdate

      在源码中commitBeforeMutationEffectOnFiber对应的函数是commitBeforeMutationLifeCycles在该函数中会调用getSnapshotBeforeUpdate,现在我们知道了getSnapshotBeforeUpdate是在mutation阶段中的commitBeforeMutationEffect函数中执行的,而commit阶段是同步的,所以getSnapshotBeforeUpdate也同步执行

function commitBeforeMutationLifeCycles(
  current: Fiber | null,
  finishedWork: Fiber,
): void {
  switch (finishedWork.tag) {
        //...
    case ClassComponent: {
      if const instance = finishedWork.stateNode;
          const snapshot = instance.getSnapshotBeforeUpdate(//getSnapshotBeforeUpdate
            finishedWork.elementType === finishedWork.type
              ? prevProps
              : resolveDefaultProps(finishedWork.type, prevProps),
            prevState,
          );
        }
}
  1. 调度useEffect
    在flushPassiveEffects函数中调用flushPassiveEffectsImpl遍历pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount,执行对应的effect回调和销毁函数,而这两个数组是在commitLayoutEffects函数中赋值的(待会就会讲到),mutation后effectList赋值给rootWithPendingPassiveEffects,然后scheduleCallback调度执行flushPassiveEffects

相关参考视频讲解:进入学习

function flushPassiveEffectsImpl() {
  if (rootWithPendingPassiveEffects === null) {//在mutation后变成了root
    return false;
  }
  const unmountEffects = pendingPassiveHookEffectsUnmount;
  pendingPassiveHookEffectsUnmount = [];//useEffect的回调函数
  for (let i = 0; i < unmountEffects.length; i += 2) {
    const effect = ((unmountEffects[i]: any): HookEffect);
    //...
    const destroy = effect.destroy;
    destroy();
  }

  const mountEffects = pendingPassiveHookEffectsMount;//useEffect的销毁函数
  pendingPassiveHookEffectsMount = [];
  for (let i = 0; i < mountEffects.length; i += 2) {
    const effect = ((unmountEffects[i]: any): HookEffect);
    //...
    const create = effect.create;
    effect.destroy = create();
  }
}
 componentDidUpdate或componentDidMount会在commit阶段同步执行(这个后面会讲到),而useEffect会在commit阶段异步调度,所以适用于数据请求等副作用的处理

 > 注意,和在render阶段的fiber node会打上Placement等标签一样,useEffect或useLayoutEffect也有对应的effect Tag,在源码中对应export const Passive = /* */ 0b0000000001000000000;
function commitBeforeMutationEffects() {
  while (nextEffect !== null) {
    const current = nextEffect.alternate;
    const effectTag = nextEffect.effectTag;

    // 在commitBeforeMutationEffectOnFiber函数中会执行getSnapshotBeforeUpdate
    if ((effectTag & Snapshot) !== NoEffect) {
      commitBeforeMutationEffectOnFiber(current, nextEffect);
    }

    // scheduleCallback调度useEffect
    if ((effectTag & Passive) !== NoEffect) {
      if (!rootDoesHavePassiveEffects) {
        rootDoesHavePassiveEffects = true;
        scheduleCallback(NormalSchedulerPriority, () => {
          flushPassiveEffects();
          return null;
        });
      }
    }
    nextEffect = nextEffect.nextEffect;//遍历effectList
  }
}
  • commitMutationEffects commitMutationEffects主要做了如下几件事

    1. 调用commitDetachRef解绑ref(第11章hook会讲解)

    2. 根据effectTag执行对应的dom操作

    3. useLayoutEffect销毁函数在UpdateTag时执行

function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
    //遍历effectList
    while (nextEffect !== null) {

      const effectTag = nextEffect.effectTag;
      // 调用commitDetachRef解绑ref
      if (effectTag & Ref) {
        const current = nextEffect.alternate;
        if (current !== null) {
          commitDetachRef(current);
        }
      }

      // 根据effectTag执行对应的dom操作
      const primaryEffectTag =
        effectTag & (Placement | Update | Deletion | Hydrating);
      switch (primaryEffectTag) {
        // 插入dom
        case Placement: {
          commitPlacement(nextEffect);
          nextEffect.effectTag &= ~Placement;
          break;
        }
        // 插入更新dom
        case PlacementAndUpdate: {
          // 插入
          commitPlacement(nextEffect);
          nextEffect.effectTag &= ~Placement;
          // 更新
          const current = nextEffect.alternate;
          commitWork(current, nextEffect);
          break;
        }
            //...
        // 更新dom
        case Update: {
          const current = nextEffect.alternate;
          commitWork(current, nextEffect);
          break;
        }
        // 删除dom
        case Deletion: {
          commitDeletion(root, nextEffect, renderPriorityLevel);
          break;
        }
      }

      nextEffect = nextEffect.nextEffect;
    }
  }
 现在让我们来看看操作dom的这几个函数

 **commitPlacement插入节点:**

 简化后的代码很清晰,找到该节点最近的parent节点和兄弟节点,然后根据isContainer来判断是插入到兄弟节点前还是append到parent节点后
function commitPlacement(finishedWork: Fiber): void {
      //...
    const parentFiber = getHostParentFiber(finishedWork);//找到最近的parent

    let parent;
    let isContainer;
    const parentStateNode = parentFiber.stateNode;
    switch (parentFiber.tag) {
      case HostComponent:
        parent = parentStateNode;
        isContainer = false;
        break;
      //...

    }
    const before = getHostSibling(finishedWork);//找兄弟节点
    if (isContainer) {
      insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
    } else {
      insertOrAppendPlacementNode(finishedWork, before, parent);
    }
  }
 **commitWork更新节点:**

 在简化后的源码中可以看到

 ​ 如果fiber的tag是SimpleMemoComponent会调用commitHookEffectListUnmount执行对应的hook的销毁函数,可以看到传入的参数是HookLayout | HookHasEffect,也就是说执行useLayoutEffect的销毁函数。

 ​ 如果是HostComponent,那么调用commitUpdate,commitUpdate最后会调用updateDOMProperties处理对应Update的dom操作
function commitWork(current: Fiber | null, finishedWork: Fiber): void {
    if (!supportsMutation) {
      switch (finishedWork.tag) {
          //...
        case SimpleMemoComponent: {
              commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
        }
        //...
      }
    }

    switch (finishedWork.tag) {
      //...
      case HostComponent: {
        //...
        commitUpdate(
              instance,
              updatePayload,
              type,
              oldProps,
              newProps,
              finishedWork,
            );
        }
        return;
      }
  }
function updateDOMProperties(
    domElement: Element,
    updatePayload: Array<any>,
    wasCustomComponentTag: boolean,
    isCustomComponentTag: boolean,
  ): void {
    // TODO: Handle wasCustomComponentTag
    for (let i = 0; i < updatePayload.length; i += 2) {
      const propKey = updatePayload[i];
      const propValue = updatePayload[i + 1];
      if (propKey === STYLE) {
        setValueForStyles(domElement, propValue);
      } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
        setInnerHTML(domElement, propValue);
      } else if (propKey === CHILDREN) {
        setTextContent(domElement, propValue);
      } else {
        setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
      }
    }
  }
 **commitDeletion删除节点:**

 如果是ClassComponent会执行componentWillUnmount,删除fiber,如果是FunctionComponent 会删除ref、并执行useEffect的销毁函数,具体可在源码中查看unmountHostComponents、commitNestedUnmounts、detachFiberMutation这几个函数
function commitDeletion(
    finishedRoot: FiberRoot,
    current: Fiber,
    renderPriorityLevel: ReactPriorityLevel,
  ): void {
    if (supportsMutation) {
      // Recursively delete all host nodes from the parent.
      // Detach refs and call componentWillUnmount() on the whole subtree.
      unmountHostComponents(finishedRoot, current, renderPriorityLevel);
    } else {
      // Detach refs and call componentWillUnmount() on the whole subtree.
      commitNestedUnmounts(finishedRoot, current, renderPriorityLevel);
    }
    const alternate = current.alternate;
    detachFiberMutation(current);
    if (alternate !== null) {
      detachFiberMutation(alternate);
    }
  }
  • commitLayoutEffects 在commitMutationEffects之后所有的dom操作都已经完成,可以访问dom了,commitLayoutEffects主要做了

    1. 调用commitLayoutEffectOnFiber执行相关生命周期函数或者hook相关callback

    2. 执行commitAttachRef为ref赋值

function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
    while (nextEffect !== null) {
      const effectTag = nextEffect.effectTag;

      // 调用commitLayoutEffectOnFiber执行生命周期和hook
      if (effectTag & (Update | Callback)) {
        const current = nextEffect.alternate;
        commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
      }

      // ref赋值
      if (effectTag & Ref) {
        commitAttachRef(nextEffect);
      }

      nextEffect = nextEffect.nextEffect;
    }
  }
 **commitLayoutEffectOnFiber:**

 ​ 在源码中commitLayoutEffectOnFiber函数的别名是commitLifeCycles,在简化后的代码中可以看到,commitLifeCycles会判断fiber的类型,SimpleMemoComponent会执行useLayoutEffect的回调,然后调度useEffect,ClassComponent会执行componentDidMount或者componentDidUpdate,this.setState第二个参数也会执行,HostRoot会执行ReactDOM.render函数的第三个参数,例如
ReactDOM.render(<App />, document.querySelector("#root"), function() {
      console.log("root mount");
    });
 现在可以知道useLayoutEffect是在commit阶段同步执行,useEffect会在commit阶段异步调度
function commitLifeCycles(
      finishedRoot: FiberRoot,
      current: Fiber | null,
      finishedWork: Fiber,
      committedLanes: Lanes,
    ): void {
      switch (finishedWork.tag) {
        case SimpleMemoComponent: {
          // 此函数会调用useLayoutEffect的回调
          commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
          // 向pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount中push effect                        // 并且调度它们
          schedulePassiveEffects(finishedWork);
        }
        case ClassComponent: {
          //条件判断...
          instance.componentDidMount();
          //条件判断...
          instance.componentDidUpdate(//update 在layout期间同步执行
            prevProps,
            prevState,
            instance.__reactInternalSnapshotBeforeUpdate,
          );      
        }

        case HostRoot: {
            commitUpdateQueue(finishedWork, updateQueue, instance);//render第三个参数
          }
        }
      }
 在schedulePassiveEffects中会将useEffect的销毁和回调函数push到pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount中
function schedulePassiveEffects(finishedWork: Fiber) {
        const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
        const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
        if (lastEffect !== null) {
          const firstEffect = lastEffect.next;
          let effect = firstEffect;
          do {
            const {next, tag} = effect;
            if (
              (tag & HookPassive) !== NoHookEffect &&
              (tag & HookHasEffect) !== NoHookEffect
            ) {
              //push useEffect的销毁函数并且加入调度
              enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
              //push useEffect的回调函数并且加入调度
              enqueuePendingPassiveHookEffectMount(finishedWork, effect);
            }
            effect = next;
          } while (effect !== firstEffect);
        }
      }
 **commitAttachRef:**

 ​ commitAttacRef中会判断ref的类型,执行ref或者给ref.current赋值
function commitAttachRef(finishedWork: Fiber) {
        const ref = finishedWork.ref;
        if (ref !== null) {
          const instance = finishedWork.stateNode;

          let instanceToUse;
          switch (finishedWork.tag) {
            case HostComponent:
              instanceToUse = getPublicInstance(instance);
              break;
            default:
              instanceToUse = instance;
          }

          if (typeof ref === "function") {
            // 执行ref回调
            ref(instanceToUse);
          } else {
            // 如果是值的类型则赋值给ref.current
            ref.current = instanceToUse;
          }
        }
      }

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

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

相关文章

Materials - DistanceField Nodes

以前的相关笔记&#xff0c;归档发布&#xff1b;距离场相关节点&#xff1a;DistanceToNearestSurface节点&#xff1a;求出传入的Position位置到最近的面的距离并输出&#xff0c;在没有Position输入的时候&#xff0c;默认值会直接使用World Position&#xff1a;Position的…

部门来了个测试工程师,听说是00后,实在是太卷了.....

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。 这不&#xff0c;前段时间我们部门来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…

喜讯!华秋电子荣获第六届“蓝点奖”十佳分销商奖

2 月 25 日&#xff0c;由深圳市电子商会主办的2023 中国电子信息产业创新发展交流大会暨第六届蓝点奖颁奖盛典在深圳隆重举行。 图&#xff1a;华秋商城渠道总监杨阳&#xff08;右三&#xff09; 深圳市电子商会连续六年举办“蓝点奖”评选活动&#xff0c;旨在表彰对电子信…

公众号运营思路是怎样的?建议收藏

问大家一个直击灵魂的问题&#xff1a; 你真的知道你的公众号该怎么运营吗&#xff1f; 先不要草率回答我&#xff0c;你先说说你的公众号运营思路是怎样的&#xff1f; 如果你连基本的运营思路都讲不出来&#xff0c;可想而知&#xff0c;你的公众号运营也好不到哪里去。 …

3.一一对应—简单工厂模式

什么是简单工厂模式&#xff1f; 简单工厂模式&#xff08;Simple Factory&#xff09;&#xff0c;解决的是在多个基类的继承类中如何选择对应的类实例化。 到底要实例化哪一个继承类&#xff1f;——这就是简单工厂模式整天思考的问题。而之所以会出现这个问题是由于软件架…

java虚拟机中对象创建过程

java虚拟机中对象创建过程 我们平常创建一个对象&#xff0c;仅仅只是使用new关键字new一个对象&#xff0c;这样一个对象就被创建了&#xff0c;但是在我们使用new关键字创建对象的时候&#xff0c;在java虚拟机中一个对象是如何从无到有被创建的呢&#xff0c;我们接下来就来…

强化学习RL 02: Value-based Reinforcement Learning

DQN和TD更新算法。 目录 Review 1. Deep Q-Network(DQN) 1.1 Approximate the Q*(s,a) Function 1.2 Apply DQN to Play Game 1.3 Temporal Difference(TD) Learning 1.4 TD Learning for DQN 1.4.1 TD使用条件 condition 1.4.2 Train DQN using TD learning 1.5 summ…

【项目精选】基于java出租车计价器设计与实现(源码+论文+视频)

关于java出租车计价器设计与实现 点击下载源码 总共4个模块 &#xff08;1&#xff09;出租车计价系统可以实现出租车信息的管理。 1.1出租车信息的查询&#xff1a;通过数据库查询出租车的车型&#xff0c;车号&#xff0c;以及是否可用 1.2出租车信息的增加&#xff1a;向数据…

如何将BI 工具与业务系统进行单点登录对接,实现用户权限通用

首先来看下两套系统的用户体系功能&#xff0c;左边是BI 工具&#xff0c;右边是业务系统&#xff0c;需要实现用户权限对接和打通&#xff1a; 单点登录体系及用户场景 • 场景1. 用户登录Wyn BI页面使用第三方业务系统账号 • 场景2. 用户使用第三方账号登录wyn BI以后需要获…

栈与队列小结

一、理论基础1.队列是先进先出&#xff0c;栈是先进后出2.栈和队列是STL&#xff08;C标准库&#xff09;里面的两个数据结构。栈提供push和pop等等接口&#xff0c;所有元素必须符合先进后出规则&#xff0c;所以栈不提供走访功能&#xff0c;也不提供迭代器。3.栈是以底层容器…

求职陷阱:Lazarus组织以日本瑞穗銀行等招聘信息为诱饵的攻击活动分析

概述 Lazarus组织是疑似具有东北亚背景的APT组织&#xff0c;奇安信威胁情报中心内部追踪编号为APT-Q-1&#xff0c;因2014年攻击索尼影业开始受到广泛关注&#xff0c;其攻击活动最早可追溯到2007年。该组织早期主要针对其他国家政府机构&#xff0c;以窃取敏感情报为目的&am…

Java查漏补缺(15)java.io.File类的使用、IO流原理及流的分类、节点流、处理流、其他流的使用、apache-common包的使用

Java查漏补缺&#xff08;15&#xff09;java.io.File类的使用、IO流原理及流的分类、节点流、处理流、其他流的使用、apache-common包的使用本章专题与脉络1. java.io.File类的使用1.1 概述1.2 构造器1.3 常用方法1、获取文件和目录基本信息2、列出目录的下一级3、File类的重命…

CLion Remote Debug CrossCompile

CLion远程Docker调试ROS(交叉编译)的设置步骤 准备一个好用的docker&#xff0c;运行起来&#xff08;Docker Image一定可以跑cuda和图形界面的&#xff0c;否则启动不了CLion&#xff0c;可以不用浪费时间看本教程了&#xff09; 在docker镜像中配置好ssh和rsync&#xff0c;…

数据可视化第二版-03部分-06章-比较与排序

文章目录数据可视化第二版-03部分-06章-比较与排序总结可视化视角-比较与排序代码实现创建虚拟环境1. python版本管理2.切换到指定版本后安装虚拟环境切换路径到文件当前路径柱形图环形柱状图子弹图哑铃图雷达图词云图教材截图数据可视化第二版-03部分-06章-比较与排序 总结 …

18- TensorFlow模型中Keras进阶 (TensorFlow系列) (深度学习)

知识要点 导入数据: (x_train, y_train), (x_test, y_test) mnist.load_data()标准化处理: x_train_scaled scaler.fit_transform(x_train) # scaler StandardScaler()one-hot编码: y_train tf.keras.utils.to_categorical(y_train, 10) 定义神经网络: model t…

《数据库系统概论》学习笔记——第四章 数据库安全

教材为数据库系统概论第五版&#xff08;王珊&#xff09; 这一章简单记一下那几条sql的用法和两种存取控制和审计&#xff08;今年期末考了&#xff09;吧&#xff0c;不知道有啥好考的 数据库安全性 问题的提出 数据库的一大特点是数据可以共享数据共享必然带来数据库的安全…

算法练习(八)计数质数(素数)

1、问题描述&#xff1a; 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 2、示例如下&#xff1a; 3、代码如下&#xff1a; 第一种&#xff1a;比较暴力的算法 class Solution {public int countPrimes(int n) {int count1;if(n<2) return 0;for(in…

【数据结构必会基础】关于树,你所必须知道的亿些概念

目录 1.什么是树 1.1浅显的理解树 1.2 数据结构中树的概念 2.树的各种结构概念 2.1 节点的度 2.2 根节点/叶节点/分支节点 2.3 父节点/子节点 2.4祖先节点/子孙节点 2.5兄弟节点 2.6树的度 2.7节点的层次 2.8森林 3. 如何用代码表示一棵树 3.1链式结构 3.1.1 树节…

01-mybatis-快速入门、代理、CRUD练习

文章目录MybatisMybatis入门案例1、创建User表&#xff0c;添加数据2、创建模块&#xff0c;搭建框架2.1 创建模块注意&#xff1a;完善项目目录2.2 导入坐标2.3 编写 MyBatis 核心配置文件2.4 编写sql映射文件2.5 编码3、解决SQL映射文件的警告提示Mapper代理开发1、定义同名接…

python下如何安装并使用matplotlib(画图模块)

在搜索命令中输入cmd&#xff0c;以管理员身份运行。 输入以下命令&#xff0c;先对pip安装工具进行升级 pip install --upgrade pip 升级完成 之后使用pip安装matplotlib pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple 也可以使用pycharm来安装matp…