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

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

React中常見(jiàn)的TypeScript定義實(shí)戰(zhàn)教程_React

作者:xiaofeng123aa ? 更新時(shí)間: 2022-11-30 編程語(yǔ)言

一 引沿

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

二 什么是調(diào)和

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

三 什么是Filber

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

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

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

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

function FiberNode(){
  this.tag = tag;                  // fiber 標(biāo)簽 證明是什么類(lèi)型fiber。
  this.key = key;                  // key調(diào)和子節(jié)點(diǎn)時(shí)候用到。 
  this.type = null;                // dom元素是對(duì)應(yīng)的元素類(lèi)型,比如div,組件指向組件對(duì)應(yīng)的類(lèi)或者函數(shù)。  
  this.stateNode = null;           // 指向?qū)?yīng)的真實(shí)dom元素,類(lèi)組件指向組件實(shí)例,可以被ref獲取。

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

  this.ref = null;                 // ref指向,ref函數(shù),或者ref對(duì)象。

  this.pendingProps = pendingProps;// 在一次更新中,代表element創(chuàng)建
  this.memoizedProps = null;       // 記錄上一次更新完畢后的props
  this.updateQueue = null;         // 類(lèi)組件存放setState更新隊(duì)列,函數(shù)組件存放
  this.memoizedState = null;       // 類(lèi)組件保存state信息,函數(shù)組件保存hooks信息,dom元素為null
  this.dependencies = null;        // context或是時(shí)間的依賴(lài)項(xiàng)

  this.mode = mode;                //描述fiber樹(shù)的模式,比如 ConcurrentMode 模式

  this.effectTag = NoEffect;       // effect標(biāo)簽,用于收集effectList
  this.nextEffect = null;          // 指向下一個(gè)effect

  this.firstEffect = null;         // 第一個(gè)effect
  this.lastEffect = null;          // 最后一個(gè)effect

  this.expirationTime = NoWork;    // 通過(guò)不同過(guò)期時(shí)間,判斷任務(wù)是否過(guò)期, 在v17版本用lane表示。

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

type 就是react的元素類(lèi)型

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

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

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

相關(guān)React實(shí)戰(zhàn)視頻講解:進(jìn)入學(xué)習(xí)

四 實(shí)現(xiàn)調(diào)和的過(guò)程

我們通過(guò)渲染一段jsx來(lái)說(shuō)明React的調(diào)和過(guò)程,也就是我們要手寫(xiě)實(shí)現(xiàn)ReactDOM.render()

const jsx = (
  <div className="border">
    <h1>hello</h1>
    <a   >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 }

2. render階段

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

ReactFiberWorkloop.js

let wipRoot = null // work in progress
let nextUnitOfwork = null // 下一個(gè)fiber節(jié)點(diǎn)

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

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

function performUnitOfWork() {}

requestIdleCallback(workLoop)

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

performUnitOfWork 包括兩個(gè)階段:

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

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

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

  // 2. 返回下一個(gè)要更新的任務(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類(lèi)型區(qū)分是FunctionComponent/ClassComponent/HostComponent/...
本文中只處理HostComponent類(lèi)型,其他類(lèi)型的處理可以看文末的完整代碼鏈接。

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é)點(diǎn)
  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,   // 標(biāo)記節(jié)點(diǎn)類(lèi)型
    key: vnode.key,     // 標(biāo)記節(jié)點(diǎn)在當(dāng)前層級(jí)下的唯一性
    props: vnode.props, // 屬性
    stateNode: null,    // 如果組件是原生標(biāo)簽則是dom節(jié)點(diǎn),如果是類(lèi)組件則是類(lèi)實(shí)例
    child: null,        // 第一個(gè)子節(jié)點(diǎn)
    return: returnFiber,// 父節(jié)點(diǎn)
    sibling: null,      // 下一個(gè)兄弟節(jié)點(diǎn)
  };

  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é)點(diǎn)
  commitWorker(wip.child);

  // 3. 提交兄弟節(jié)點(diǎn)
  commitWorker(wip.sibling);
}

五 總結(jié)

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

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

欄目分類(lèi)
最近更新