日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React中常見的TypeScript定義實戰教程_React

作者:xiaofeng123aa ? 更新時間: 2022-11-30 編程語言

一 引沿

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

二 什么是調和

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

三 什么是Filber

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

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

為了實現上面的要求,我們需要把任務拆分成一個個可執行的單元,這些可執行的單元就叫做一個Fiber,一個Fiber就代表一個可執行的單元。

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

function FiberNode(){
  this.tag = tag;                  // fiber 標簽 證明是什么類型fiber。
  this.key = key;                  // key調和子節點時候用到。 
  this.type = null;                // dom元素是對應的元素類型,比如div,組件指向組件對應的類或者函數。  
  this.stateNode = null;           // 指向對應的真實dom元素,類組件指向組件實例,可以被ref獲取。

  this.return = null;              // 指向父級fiber
  this.child = null;               // 指向子級fiber
  this.sibling = null;             // 指向兄弟fiber 
  this.index = 0;                  // 索引

  this.ref = null;                 // ref指向,ref函數,或者ref對象。

  this.pendingProps = pendingProps;// 在一次更新中,代表element創建
  this.memoizedProps = null;       // 記錄上一次更新完畢后的props
  this.updateQueue = null;         // 類組件存放setState更新隊列,函數組件存放
  this.memoizedState = null;       // 類組件保存state信息,函數組件保存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;    // 通過不同過期時間,判斷任務是否過期, 在v17版本用lane表示。

  this.alternate = null;           //雙緩存樹,指向緩存的fiber。更新階段,兩顆樹互相交替。
}

type 就是react的元素類型

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

比如元素結構如下:

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

function Child() {
  return <p>child</p>
}

對應的Filber結構如下:

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

相關React實戰視頻講解:進入學習

四 實現調和的過程

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

const jsx = (
  <div className="border">
    <h1>hello</h1>
    <a   >React</a>
  </div>
)

ReactDOM.render(
  jsx,
  document.getElementById('root')
);

1. 創建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 }

2. render階段

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

ReactFiberWorkloop.js

let wipRoot = null // work in progress
let nextUnitOfwork = null // 下一個fiber節點

export function scheduleUpdateOnFiber(fiber) {
  wipRoot = fiber
  nextUnitOfwork = fiber
}

function workLoop(IdleDeadline) {
  while(nextUnitOfwork && IdleDeadline.timeRemaining() > 0) {
    nextUnitOfwork = performUnitOfWork(nextUnitOfwork)
  }
}

function performUnitOfWork() {}

requestIdleCallback(workLoop)

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

performUnitOfWork 包括兩個階段:

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

這么一上一下,構成了整個 fiber 樹的調和。

import { updateHostComponent } from './ReactFiberReconciler'
function performUnitOfWork(wip) {
  // 1. 更新wip
  const { type } = wip
  if (isStr(type)) {
    // type是string,更新普通元素節點
    updateHostComponent(wip)
  } else if (isFn(type)) {
    // ...
  }

  // 2. 返回下一個要更新的任務 深度優先遍歷
  if (wip.child) {
    return wip.child
  }
  let next = wip
  while(next) {
    if (next.sibling) {
      return next.sibling
    }
    next = next.return
  }
  return null
}

根據type類型區分是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);
  }
  // 調和子節點
  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,   // 標記節點類型
    key: vnode.key,     // 標記節點在當前層級下的唯一性
    props: vnode.props, // 屬性
    stateNode: null,    // 如果組件是原生標簽則是dom節點,如果是類組件則是類實例
    child: null,        // 第一個子節點
    return: returnFiber,// 父節點
    sibling: null,      // 下一個兄弟節點
  };

  return newFiber;
}

至此已經完成了render階段,下面是commit階段,commit階段就是依據Fiber結構操作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. 提交子節點
  commitWorker(wip.child);

  // 3. 提交兄弟節點
  commitWorker(wip.sibling);
}

五 總結

  • Fiber結構,Fiber的生成過程。
  • 調和過程,以及 render 和 commit 兩大階段。

原文鏈接:https://www.cnblogs.com/xiaofeng123aa/p/16821323.html

欄目分類
最近更新