React源码-知识点

调合 reconciliation

React用来diff一个树与另一个树,以确定哪些部分需要更改,

  • 假设不同的组件类型生成不同的树。React不会尝试改变它们,而是新树完全替换旧树
  • list的diff是用key来实施的,key应该唯一、可预测、安全
    调节器(reconciler)只是计算树的哪一部分变了和渲染者(web,ios,android),Fiber重新实现了reconciler
调度 schedule
  • 它是决定什么时候执行工作的过程。
  • 如果有些工作不需要马上显示在UI界面,我们可以延迟它相关的任何逻辑。
  • 如果数据到达的速度快于帧速率,我们可以合并和批量更新
  • 我们应该优先用户交互的功能,延迟数据加载,为了防止丢帧。

关键点

  • 在ui界面,不是每一次的更新都要马上执行,这样会丢帧和降低用户体验
  • 不同的更新有不同的优先级,比如动画更新需要比加载数据更快完成
  • 在fiber中,使用调度对React的核心算法进行了改革
可中断渲染

eg 一个可过滤的列表,如果每次输入变化更新,就会产生性能问题,如果用防抖和节流,也会影响用户体验。
主要原因就是渲染是无法中断的,阻塞式渲染

Concurrent Mode
  • React可以同时处理多个状态更新
  • blocking rendering 阻塞式渲染,比如当创建新的DOM和执行组件代码,是不能暂停的
  • 在并行模式中,渲染不是阻塞的,可以打断的
  • 并发模式类似于React“在一个分支上”工作
  • Concurrent Mode中,进入下一个状态UI之前,react会现在内存中计算好,会在旧的状态UI多呆一会,这样就有更好的用户体验
  • For CPU-bound updates (such as creating DOM nodes and running component code), concurrency means that a more urgent update can “interrupt” rendering that has already started.
  • For IO-bound updates (such as fetching code or data from the network), concurrency means that React can start rendering in memory even before all the data arrives, and skip showing jarring empty loading states.
Work

是调用更新(setState)产生的结果

为了充分利用调度的优势,我们需要实现

  • 任务的暂停和继续
  • 给不同任务分配优先级
  • 复用已完成的工作
  • 放弃不需要的工作
Fiber (A fiber represents a unit of work)

渐进式渲染,把一个渲染split成多个chunk,并分布在多个帧, 其他关键特性包括能够暂停、中止或在新更新到来时重用工作;
为不同类型的更新分配优先级的能力,和原始新的并发功能。

  • Fiber 是 call stack的重新实现,定制化的call stack
  • 每一个fiber都是一个虚拟 调用栈的帧

requestIdleCallback 分配一个低优先级的工作
requestAnimationFrame 分配一个高优先级的工作
我们需要手动操作 stack frames

Fiber detail
  • fiber 是一个对象,包含了组件,组件的输入、输出
  • fiber对应于 一个stack frame 也对应于一个组件实例
  • type、key两个字段,和正常组件的表现一样,type描述了组件的类型,符合组件type就是自身,host组件,type是字符串"div"
  • 配合type字段,key用来在调合阶段,哪些fiber可以重用
  • child是父组件render方法返回的组件
  • siblings是相邻组件
  • return 是处理完fiber之后返回的fiber ,如果有多个子fiber,返回的是父fiber
  • pendingProps开始时设置组件的参数 memoizedProps是结束后,如果两者相同,代表上次的结果可以复用
  • flush 一个 fiber 就是使结果输入到UI
  • work-in-progress 一个没完成的fiber,是一个没return的stack frame
  • 一个组件有两个fiber,一个完成的flushed fiber 一个work-in-progress的fiber alternate就是两个fiber的互相的值
  • 一个fiber的alternate是懒创建的使用cloneFiber创建的 cloneFiber会复用现有的alternate,有的话
  • 每个fiber最后返回的都是 HostComponent(div这种)
帧数

当每秒绘制的帧数(FPS)达到 60 时,页面是流畅的,小于这个值时,用户会感觉到卡顿。1s 60帧,所以每一帧分到的时间是 1000/60 ≈ 16 ms
一帧内需要完成如下六个步骤的任务 处理用户的交互、JS 解析执行、帧开始。窗口尺寸变更,页面滚去等的处理、requestAnimationFrame(rAF)、布局、绘制
上面六个步骤完成后没超过 16 ms,说明时间有富余,此时就会执行 requestIdleCallback 里注册的任务。但是requestAnimationFrame 每一帧必定会执行。

performance

Performance接口提供对当前页面的性能相关信息的访问,主要包含4个属性部分,

  • timing 包含与延迟相关的性能信息的遗留PerformanceTiming对象
  • navigation 一个遗留的PerformanceNavigation对象,它提供了有关定时列出的times中包含的操作的有用上下文,包括页面是加载还是刷新,发生了多少重定向,等等。
  • memory 一个非标准扩展添加在Chrome中,该属性提供一个对象的基本内存使用信息。您不应该使用这种非标准的API
  • timeOrigin 返回性能度量的开始时间的高精度时间戳(3个精度的时间戳)

performance methods

  • performance.now() 返回一个milliseconds(毫秒)精确度的时间戳 返回的值表示从时间源开始经过的时间。
const t0 = performance.now();
doSomething();
const t1 = performance.now();
console.log(`Call to doSomething took ${
    
    t1 - t0} milliseconds.`);
  • mark 使用给定的名称在浏览器的性能输入缓冲区中创建时间戳
  • measure 开始测量
const markerNameA = "example-marker-a"
const markerNameB = "example-marker-b"

// Run some nested timeouts, and create a PerformanceMark for each.
performance.mark(markerNameA);
setTimeout(function() {
    
    
  performance.mark(markerNameB);
  setTimeout(function() {
    
    

    // Create a variety of measurements.
    performance.measure("measure a to b", markerNameA, markerNameB);
    performance.measure("measure a to now", markerNameA);
    performance.measure("measure from navigation start to b", undefined, markerNameB);
    performance.measure("measure from the start of navigation to now");

    // Pull out all of the measurements.
    console.log(performance.getEntriesByType("measure"));

    // Finally, clean up the entries.
    performance.clearMarks();
    performance.clearMeasures();
  }, 1000);
}, 1000);

猜你喜欢

转载自blog.csdn.net/qq_29334605/article/details/106136826