欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React Fiber與調(diào)和深入分析

 更新時間:2022年11月17日 08:27:53   作者:xiaofeng123aazz  
Fiber可以理解為一個執(zhí)行單元,每次執(zhí)行完一個執(zhí)行單元,React Fiber就會檢查還剩多少時間,如果沒有時間則將控制權(quán)讓出去,然后由瀏覽器執(zhí)行渲染操作,這篇文章主要介紹了React Fiber架構(gòu)原理剖析,需要的朋友可以參考下

一 引沿

Fiber 架構(gòu)是React16中引入的新概念,目的就是解決大型 React 應(yīng)用卡頓,React在遍歷更新每一個節(jié)點的時候都不是用的真實DOM,都是采用虛擬DOM,所以可以理解成fiber就是React的虛擬DOM,更新Fiber的過程叫做調(diào)和,每一個fiber都可以作為一個執(zhí)行單元來處理,所以每一個 fiber 可以根據(jù)自身的過期時間expirationTime,來判斷是否還有空間時間執(zhí)行更新,如果沒有時間更新,就要把主動權(quán)交給瀏覽器去渲染,做一些動畫,重排( reflow ),重繪 repaints 之類的事情,這樣就能給用戶感覺不是很卡。

二 什么是調(diào)和

調(diào)和是一種算法,就是React對比新老虛擬DOM的過程,以決定需要更新哪一部分。

三 什么是Filber

Fiber的目的是為了讓React充分利用調(diào)度,以便做到如下幾點:

  • 暫停工作,稍后再回來
  • 優(yōu)先考慮不同類型的工作
  • 重用以前完成的工作
  • 如果不再需要,則中止工作

為了實現(xiàn)上面的要求,我們需要把任務(wù)拆分成一個個可執(zhí)行的單元,這些可執(zhí)行的單元就叫做一個Fiber,一個Fiber就代表一個可執(zhí)行的單元。

一個Fiber就是一個普通的JS對象,包含一些組件的相關(guān)信息。

function FiberNode(){
  this.tag = tag;                  // fiber 標簽 證明是什么類型fiber。
  this.key = key;                  // key調(diào)和子節(jié)點時候用到。 
  this.type = null;                // dom元素是對應(yīng)的元素類型,比如div,組件指向組件對應(yīng)的類或者函數(shù)。  
  this.stateNode = null;           // 指向?qū)?yīng)的真實dom元素,類組件指向組件實例,可以被ref獲取。
  this.return = null;              // 指向父級fiber
  this.child = null;               // 指向子級fiber
  this.sibling = null;             // 指向兄弟fiber 
  this.index = 0;                  // 索引
  this.ref = null;                 // ref指向,ref函數(shù),或者ref對象。
  this.pendingProps = pendingProps;// 在一次更新中,代表element創(chuàng)建
  this.memoizedProps = null;       // 記錄上一次更新完畢后的props
  this.updateQueue = null;         // 類組件存放setState更新隊列,函數(shù)組件存放
  this.memoizedState = null;       // 類組件保存state信息,函數(shù)組件保存hooks信息,dom元素為null
  this.dependencies = null;        // context或是時間的依賴項
  this.mode = mode;                //描述fiber樹的模式,比如 ConcurrentMode 模式
  this.effectTag = NoEffect;       // effect標簽,用于收集effectList
  this.nextEffect = null;          // 指向下一個effect
  this.firstEffect = null;         // 第一個effect
  this.lastEffect = null;          // 最后一個effect
  this.expirationTime = NoWork;    // 通過不同過期時間,判斷任務(wù)是否過期, 在v17版本用lane表示。
  this.alternate = null;           //雙緩存樹,指向緩存的fiber。更新階段,兩顆樹互相交替。
}

type 就是react的元素類型

export const FunctionComponent = 0;       // 對應(yīng)函數(shù)組件
export const ClassComponent = 1;          // 對應(yīng)的類組件
export const IndeterminateComponent = 2;  // 初始化的時候不知道是函數(shù)組件還是類組件 
export const HostRoot = 3;                // Root Fiber 可以理解為跟元素 , 通過reactDom.render()產(chǎn)生的根元素
export const HostPortal = 4;              // 對應(yīng)  ReactDOM.createPortal 產(chǎn)生的 Portal 
export const HostComponent = 5;           // dom 元素 比如 <div>
export const HostText = 6;                // 文本節(jié)點
export const Fragment = 7;                // 對應(yīng) <React.Fragment> 
export const Mode = 8;                    // 對應(yīng) <React.StrictMode>   
export const ContextConsumer = 9;         // 對應(yīng) <Context.Consumer>
export const ContextProvider = 10;        // 對應(yīng) <Context.Provider>
export const ForwardRef = 11;             // 對應(yīng) React.ForwardRef
export const Profiler = 12;               // 對應(yīng) <Profiler/ >
export const SuspenseComponent = 13;      // 對應(yīng) <Suspense>
export const MemoComponent = 14;          // 對應(yīng) React.memo 返回的組件

比如元素結(jié)構(gòu)如下:

export default class Parent extends React.Component{
   render(){
     return <div>
       <h1>hello,world</h1>
       <Child />
     </div>
   }
}
function Child() {
  return <p>child</p>
}

對應(yīng)的Filber結(jié)構(gòu)如下:

有了上面的概念后我們就自己實現(xiàn)一個Fiber的更新機制

四 實現(xiàn)調(diào)和的過程

我們通過渲染一段jsx來說明React的調(diào)和過程,也就是我們要手寫實現(xiàn)ReactDOM.render()

const jsx = (
  <div className="border">
    <h1>hello</h1>
    <a  rel="external nofollow" >React</a>
  </div>
)
ReactDOM.render(
  jsx,
  document.getElementById('root')
);

1. 創(chuàng)建FiberRoot

react-dom.js

function createFiberRoot(element, container){
    return {
    type: container.nodeName.toLocaleLowerCase(),
    props: { children: element },
    stateNode: container
  }
}
function render(element, container) {
  const FibreRoot = createFiberRoot(element, container)
  scheduleUpdateOnFiber(FibreRoot)
}
export default { render }

參考React實戰(zhàn)視頻講解:進入學(xué)習(xí)

2. render階段

調(diào)和的核心是render和commit,本文不講調(diào)度過程,我們會簡單的用requestIdleCallback代替React的調(diào)度過程。

ReactFiberWorkloop.js

let wipRoot = null // work in progress
let nextUnitOfwork = null // 下一個fiber節(jié)點
export function scheduleUpdateOnFiber(fiber) {
  wipRoot = fiber
  nextUnitOfwork = fiber
}
function workLoop(IdleDeadline) {
  while(nextUnitOfwork && IdleDeadline.timeRemaining() > 0) {
    nextUnitOfwork = performUnitOfWork(nextUnitOfwork)
  }
}
function performUnitOfWork() {}
requestIdleCallback(workLoop)

每一個 fiber 可以看作一個執(zhí)行的單元,在調(diào)和過程中,每一個發(fā)生更新的 fiber 都會作為一次 workInProgress 。那么 workLoop 就是執(zhí)行每一個單元的調(diào)度器,如果渲染沒有被中斷,那么 workLoop 會遍歷一遍 fiber 樹

performUnitOfWork 包括兩個階段:

  • 是向下調(diào)和的過程,就是由 fiberRoot 按照 child 指針逐層向下調(diào)和,期間會執(zhí)行函數(shù)組件,實例類組件,diff 調(diào)和子節(jié)點
  • 是向上歸并的過程,如果有兄弟節(jié)點,會返回 sibling兄弟,沒有返回 return 父級,一直返回到 fiebrRoot

這么一上一下,構(gòu)成了整個 fiber 樹的調(diào)和。

import { updateHostComponent } from './ReactFiberReconciler'
function performUnitOfWork(wip) {
  // 1. 更新wip
  const { type } = wip
  if (isStr(type)) {
    // type是string,更新普通元素節(jié)點
    updateHostComponent(wip)
  } else if (isFn(type)) {
    // ...
  }
  // 2. 返回下一個要更新的任務(wù) 深度優(yōu)先遍歷
  if (wip.child) {
    return wip.child
  }
  let next = wip
  while(next) {
    if (next.sibling) {
      return next.sibling
    }
    next = next.return
  }
  return null
}

根據(jù)type類型區(qū)分是FunctionComponent/ClassComponent/HostComponent/…

本文中只處理HostComponent類型,其他類型的處理可以看文末的完整代碼鏈接。

ReactFiberReconciler.js

import { createFiber } from './createFiber'
export function updateHostComponent(wip) {
  if (!wip.stateNode) {
    wip.stateNode = document.createElement(wip.type);
    updateNode(wip.stateNode, wip.props);
  }
  // 調(diào)和子節(jié)點
  reconcileChildren(wip, wip.props.children);
}
function reconcileChildren(returnFiber, children) {
  if (isStr(children)) {
    return
  }
  const newChildren = isArray(children) ? children : [children];
  let previousNewFiber = null
  for(let i = 0; i < newChildren.length; i++) {
    const newChild = newChildren[i];
    const newFiber = createFiber(newChild, returnFiber)
    if (previousNewFiber === null) {
      returnFiber.child = newFiber
    } else {
      previousNewFiber.sibling = newFiber
    }
    previousNewFiber = newFiber
  }
}
function updateNode(node, nextVal) {
  Object.keys(nextVal).forEach((k) => {
    if (k === "children") {
      if (isStringOrNumber(nextVal[k])) {
        node.textContent = nextVal[k];
      }
    } else {
      node[k] = nextVal[k];
    }
  });
}

createFiber.js

export function createFiber(vnode, returnFiber) {
  const newFiber = {
    type: vnode.type,   // 標記節(jié)點類型
    key: vnode.key,     // 標記節(jié)點在當(dāng)前層級下的唯一性
    props: vnode.props, // 屬性
    stateNode: null,    // 如果組件是原生標簽則是dom節(jié)點,如果是類組件則是類實例
    child: null,        // 第一個子節(jié)點
    return: returnFiber,// 父節(jié)點
    sibling: null,      // 下一個兄弟節(jié)點
  };
  return newFiber;
}

至此已經(jīng)完成了render階段,下面是commit階段,commit階段就是依據(jù)Fiber結(jié)構(gòu)操作DOM

function workLoop(IdleDeadline) {
  while(nextUnitOfwork && IdleDeadline.timeRemaining() > 0) {
    nextUnitOfwork = performUnitOfWork(nextUnitOfwork)
  }
  // commit
  if (!nextUnitOfwork && wipRoot) {
    commitRoot();
  }
}
function commitRoot() {
  commitWorker(wipRoot.child)
  wipRoot = null;
}
function commitWorker(wip) {
  if (!wip) {
    return
  }
  // 1. 提交自己
  const { stateNode } = wip
  let parentNode = wip.return.stateNode
  if (stateNode) {
    parentNode.appendChild(stateNode);
  }
  // 2. 提交子節(jié)點
  commitWorker(wip.child);
  // 3. 提交兄弟節(jié)點
  commitWorker(wip.sibling);
}

五 總結(jié)

  • Fiber結(jié)構(gòu),F(xiàn)iber的生成過程。
  • 調(diào)和過程,以及 render 和 commit 兩大階段。

到此這篇關(guān)于React Fiber與調(diào)和深入分析的文章就介紹到這了,更多相關(guān)React Fiber與調(diào)和內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在?React?中使用?Context?API?實現(xiàn)跨組件通信的方法

    在?React?中使用?Context?API?實現(xiàn)跨組件通信的方法

    在React中,ContextAPI是一個很有用的特性,可用于組件間的狀態(tài)共享,它允許跨組件傳遞數(shù)據(jù)而無需通過每個組件手動傳遞props,本文給大家介紹在?React?中如何使用?Context?API?來實現(xiàn)跨組件的通信,感興趣的朋友一起看看吧
    2024-09-09
  • 淺談react性能優(yōu)化的方法

    淺談react性能優(yōu)化的方法

    這篇文章主要介紹了淺談react性能優(yōu)化的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • React中useState原理的代碼簡單實現(xiàn)

    React中useState原理的代碼簡單實現(xiàn)

    要實現(xiàn)useState的背后原理,則需要深入了解狀態(tài)是如何在函數(shù)組件的渲染周期中保持和更新的,本文將通過一段代碼簡單闡述useState鉤子函數(shù)的實現(xiàn)思路,希望對大家有所幫助
    2023-12-12
  • React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實現(xiàn)

    React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實現(xiàn)

    本文主要介紹了React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • React事件處理的機制及原理

    React事件處理的機制及原理

    這篇文章主要介紹了React事件處理的機制及原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • Create?react?app修改webapck配置導(dǎo)入文件alias

    Create?react?app修改webapck配置導(dǎo)入文件alias

    這篇文章主要為大家介紹了Create?react?app修改webapck配置導(dǎo)入文件alias,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • React Native 中實現(xiàn)確認碼組件示例詳解

    React Native 中實現(xiàn)確認碼組件示例詳解

    這篇文章主要為大家介紹了React Native中實現(xiàn)確認碼組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • React學(xué)習(xí)之事件綁定的幾種方法對比

    React學(xué)習(xí)之事件綁定的幾種方法對比

    這篇文章主要給大家介紹了關(guān)于React學(xué)習(xí)之事件綁定的幾種方法對比,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • 無廢話快速上手React路由開發(fā)

    無廢話快速上手React路由開發(fā)

    本文以簡潔為目標,幫助快速上手react-router-dom默認你接觸過路由相關(guān)的開發(fā),通過實例代碼講解的很詳細,對React路由相關(guān)知識感興趣的朋友一起看看吧
    2021-05-05
  • react-native-video實現(xiàn)視頻全屏播放的方法

    react-native-video實現(xiàn)視頻全屏播放的方法

    這篇文章主要介紹了react-native-video實現(xiàn)視頻全屏播放的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03

最新評論