当前位置: 首页 > news >正文

React16源码: React中context-stack的源码实现

context-stack


1 ) 概述

  • 在 context 内部有一个概念是 stack
    • 有一个唯一的stack
    • 里面保存各种各样的东西
  • stack的特性
    • 在更新节点的时候,会把相关的信息入栈
      • 在因为stack就是栈,在里面会存储各种各样的信息
      • 在更新节点的时候,每一个节点的信息都会推入这个stack
    • 完成节点更新的时候,相关的信息需要出栈
      • 因为栈是一个先入后出的这么一个数据结构
      • 这正好对应于 react的更新过程中的 beginWork 是从头到尾
      • 沿着子树的一侧这么下来的一个过程,这个时候我们从头到尾,推入了相关的信息
      • 然后在 completeUnitOfWork 的时候,我们又是从尾到头去进行一个完成节点更新的操作
      • 这个时候刚好是一个相反的顺序
      • 在这个相反的顺序当中,又可以同时出栈,这样的话, 就可以保证我们的栈从更新开始到最终更新结束
      • 因为从更新开始到更新结束,都是要回到 root 节点,它这个栈更新开始的时候是空的
      • 更新结束的时候,也刚好变成一个空的状态
      • 这就是在更新时入栈,在完成节点时出栈
    • 在这个栈的数据结构中可以用不同的cursor来记录不同的信息
      • react的这个stack的实现过程中,它需要存的信息是非常多的
      • 比如说有新的 contextAPI 也有老的 contextAPI
      • 还有跟 HostComponent 相关的一些信息,还有container相关的一些信息
      • 这些信息之间是互不干涉,没有任何关联的,这些信息都要存在同一个stack里面
      • 通过 cursor 去标记这些相关性的信息自己处于哪个位置

2 )源码

定位到 packages/react-reconciler/src/ReactFiberStack.js

/*** Copyright (c) Facebook, Inc. and its affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/import type {Fiber} from './ReactFiber';import warningWithoutStack from 'shared/warningWithoutStack';export type StackCursor<T> = {current: T,
};const valueStack: Array<any> = [];let fiberStack: Array<Fiber | null>;if (__DEV__) {fiberStack = [];
}let index = -1;function createCursor<T>(defaultValue: T): StackCursor<T> {return {current: defaultValue,};
}function isEmpty(): boolean {return index === -1;
}function pop<T>(cursor: StackCursor<T>, fiber: Fiber): void {if (index < 0) {if (__DEV__) {warningWithoutStack(false, 'Unexpected pop.');}return;}if (__DEV__) {if (fiber !== fiberStack[index]) {warningWithoutStack(false, 'Unexpected Fiber popped.');}}cursor.current = valueStack[index];valueStack[index] = null;if (__DEV__) {fiberStack[index] = null;}index--;
}function push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void {index++;valueStack[index] = cursor.current;if (__DEV__) {fiberStack[index] = fiber;}cursor.current = value;
}function checkThatStackIsEmpty() {if (__DEV__) {if (index !== -1) {warningWithoutStack(false,'Expected an empty stack. Something was not reset properly.',);}}
}function resetStackAfterFatalErrorInDev() {if (__DEV__) {index = -1;valueStack.length = 0;fiberStack.length = 0;}
}export {createCursor,isEmpty,pop,push,// DEV only:checkThatStackIsEmpty,resetStackAfterFatalErrorInDev,
};
  • 首先定义一个非常重要的变量叫做 valueStack,它是一个数组
  • 接下去这边有一个 fiberStack,它只是用于开发的过程当中, 可以先忽略它
  • 定义一个全局 index
    • 注意这个 index 就是对应 valueStack 目前存数据存在的哪个位置
  • 接下去, 声明了一个方法叫做 createCursor
    • 直接 return 了一个新的对象
    • 这个对象里面有一个 current 属性
    • 在创建的时候传入的一个 defaultValue 给 current 赋值
    • 它是一个 pure 的对象,没有任何其他的一些特性
  • 注意
    • 这整个文件,它对应的是一个模块
    • valueStack 定义一些什么东西
    • 它里面的所有的数据都是有关联性的
    • 主要的数据也就是 valuestack 跟 index 是在这个模块里面
    • 它其他的数据都是在外部,使用的时候才会创建的
    • 这个 index 它本来是标记 valueStack 对应数据存在哪个位置
    • 可以通过它是否等于 -1 来判断我们目前的 valueStack 它是否是一个空的数组
  • 接下去的 pop 方法
    • 如果 index < 0
      • 说明 valueStack 里面没有任何的数据,直接return
      • 因为我们没有任何东西是可以推出来的
    • 读取 index 在 valueStack 对应的 value 存入 cursor.current
      • 因为在推入的时候,就是把老的值存到 valueStack上面,然后新的值它等于新设置的值
      • 如果要 pop 的时候,对应的肯定要把老的那个值再赋值回来
    • 将 index 对应在 valueStack 中的 value 置空
    • 将 index –
  • 接下去的 push 方法
    • 它接收的参数 cursor, value, fiber
    • cursor 是通过 createCursor 这个方法来创建的一个游标对象
    • 将 index ++
    • 将老的 value (cursor.current) 推入到 栈 当中
    • 将当前 cursor.current 指向传进来的 value
    • 注意,在一次 push 中新的 value 不会入栈的
  • checkThatStackIsEmpty
    • 这个只有在开发时会用到,其实就跟 isEmpty 是一样的
    • 它判断一下index是否等于 -1,然后进行一个warning
  • resetStackAfterFatalErrorInDev
    • dev 相关变量,无需过多关注
    • 就是重置了一些值
  • 注意
    • 如果在对 valueStack 进行操作的过程中,顺序是不一样的
    • 比如说,我有三种不同的数据类型,就有三个不同的cursor
    • 3个不同的 cursor 它们推入的这些数据,保存的不一样
    • 比如说,我目前这个数组先推入了一个a,然后再推入了一个b,然后再推入了一个c
    • 它们分别对应的是 cursor1(c1), cursor2(c2), cursor3(c3),它们想要读取的数据应该是这样的
    • 即 [a, b, c] => c1, c2, c3
    • 在 pop 的时候,可能先 pop 了 c1,但是我拿到的是却是 c3 对应的数据 c
    • 这个就是我们不想要出现的一个情况,实际上这个文件内没有解决这个问题
    • 因为react在更新的过程当中,先从 beginWork 里面推数据
    • 然后在 completeUnitOfWork 里面去提出数据
    • 这个过程中,它始终保持着 cursor 的一一对应关系
    • 也就是说我推进来的是c1, c2, c3的顺序
    • 推出的时候,要先推出 c3,再推出 c2,再推出 c1
    • 它们拿到的值都是一个一一对应的关系
    • 这是react在使用这个stack模块的时候去控制的
    • 而不是在我们这个模块内部去解的这个问题
    • 比如说 context 它一个入栈,出栈的一个情况的时候就会有这个调用的过程
    • 这个目前先跳过

相关文章:

  • C#调用SqlSugar操作达梦数据库报错“无效的表或视图名”
  • 消息中间件之八股面试回答篇:三、RabbitMQ如何解决消息堆积问题(100万条消息堆积)+RabbitMQ高可用性和强一致性机制+回答模板
  • Datawhale 组队学习Task8大模型的有害性(上/下)
  • 毕业找工作只会C语言是不是完蛋了?
  • 当代码遇上玄学……
  • SRE-Redis基本概念篇
  • 初识elasticsearch
  • 内网安全:NTLM-Relay
  • 幻兽帕鲁服务器出租,腾讯云PK阿里云怎么收费?
  • 计算机网络——网络层(2)
  • c#之构值类型和引用类型
  • 4核16G幻兽帕鲁服务器优惠价格表,阿里云和腾讯云报价
  • ES 分词器
  • Android源码设计模式解析与实战第2版笔记(一)
  • RabbitMQ-如何保证消息不丢失
  • [译] 怎样写一个基础的编译器
  • canvas绘制圆角头像
  • js
  • STAR法则
  • 每天10道Java面试题,跟我走,offer有!
  • 让你的分享飞起来——极光推出社会化分享组件
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 学习使用ExpressJS 4.0中的新Router
  • 栈实现走出迷宫(C++)
  • 自制字幕遮挡器
  • ​中南建设2022年半年报“韧”字当头,经营性现金流持续为正​
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • ###STL(标准模板库)
  • (06)金属布线——为半导体注入生命的连接
  • (arch)linux 转换文件编码格式
  • (定时器/计数器)中断系统(详解与使用)
  • (二)构建dubbo分布式平台-平台功能导图
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (四)模仿学习-完成后台管理页面查询
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .net framework4与其client profile版本的区别
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .net 设置默认首页
  • .net操作Excel出错解决
  • ::什么意思
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • [Android]使用Android打包Unity工程
  • [BZOJ]4817: [Sdoi2017]树点涂色
  • [BZOJ3223]文艺平衡树
  • [CakePHP] 在Controller中使用Helper
  • [codeforces]Levko and Permutation
  • [CUDA手搓]从零开始用C++ CUDA搭建一个卷积神经网络(LeNet),了解神经网络各个层背后算法原理
  • [HJ73 计算日期到天数转换]
  • [linux学习]apt-get参数解析
  • [Perl] Find Shell on your Wordpress site
  • [PHP] 面向对象
  • [RN] React Native 常用命令行