日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

React 源碼中最重要的部分,你知道有哪些嗎?

來源: 責編: 時間:2024-05-16 09:09:36 158觀看
導讀無論是并發模式,還是同步模式,最終要生成新的 Fiber Tree,都是通過遍歷 workInProgress 的方式去執行 performUnitOfWork。// 并發模式function workLoopConcurrent() { // Perform work until Scheduler asks us to yi

32228資訊網——每日最新資訊28at.com

無論是并發模式,還是同步模式,最終要生成新的 Fiber Tree,都是通過遍歷 workInProgress 的方式去執行 performUnitOfWork。32228資訊網——每日最新資訊28at.com

// 并發模式function workLoopConcurrent() {  // Perform work until Scheduler asks us to yield  while (workInProgress !== null && !shouldYield()) {    performUnitOfWork(workInProgress);  }}
// 同步function workLoopSync() {  // Already timed out, so perform work without checking if we need to yield.  while (workInProgress !== null) {    performUnitOfWork(workInProgress);  }}

需要特別注意的是這里的 workInProgress 表示當前正在執行的 Fiber 節點,他會在遞歸的過程中不斷改變指向,這里要結合我們之前章節中分享過的 Fiber Tree 的鏈表結構來理解。32228資訊網——每日最新資訊28at.com

if (next === null) {  // If this doesn't spawn new work, complete the current work.  completeUnitOfWork(unitOfWork);} else {  workInProgress = next;}

一、performUnitOfWork

該方法主要用于創建 Fiber Tree,是否理解 Fiber Tree 的構建過程,跟我們是否能做好性能優化有非常直接的關系,因此對我而言,這是 React 源碼中最重要的一個部分。32228資訊網——每日最新資訊28at.com

從他的第一行代碼我們就能知道,Fiber Tree 的創建是依賴于雙緩存策略。上一輪構建完成的 Fiber tree,在代碼中用 current 來表示。32228資訊網——每日最新資訊28at.com

正在構建中的 Fiber tree,在代碼中用 workInProgress 來表示,并且他們之間同層節點都用 alternate 相互指向。32228資訊網——每日最新資訊28at.com

current.alternate = workInProgress;workInProgress.alternate = current;

workInProgress 會基于 current 構建。32228資訊網——每日最新資訊28at.com

function performUnitOfWork(unitOfWork: Fiber): void {  const current = unitOfWork.alternate;  ...

整體的思路是從 current[rootFiber] 樹往下執行深度遍歷,在遍歷的過程中,會根據 key、props、context、state 等條件進行判斷,判斷結果如果發現節點沒有發生變化,那么就復用 current 的節點,如果發生了變化,則重新創建 Fiber 節點,并標記需要修改的類型,用于傳遞給 commitRoot。32228資訊網——每日最新資訊28at.com

二、beginWork

每一個被遍歷到的 Fiber 節點,會執行 beginWork 方法。32228資訊網——每日最新資訊28at.com

let next;if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {  startProfilerTimer(unitOfWork);  next = beginWork(current, unitOfWork, subtreeRenderLanes);  stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);} else {  next = beginWork(current, unitOfWork, subtreeRenderLanes);}

該方法根據傳入的 Fiber 節點創建子節點,并將這兩個節點連接起來。32228資訊網——每日最新資訊28at.com

function beginWork(  current: Fiber | null,  workInProgress: Fiber,  renderLanes: Lanes,): Fiber | null {...}

React 在 ReactFiberBeginWork.new.js 模塊中維護了一個全局的 didReceiveUpdate 變量,來表示當前節點是否需要更新。32228資訊網——每日最新資訊28at.com

let didReceiveUpdate: boolean = false;

在 beginWork 的執行過程中,會經歷一些判斷來確認 didReceiveUpdate 的值,從而判斷該 Fiber 節點是否需要重新執行。32228資訊網——每日最新資訊28at.com

if (current !== null) {    const oldProps = current.memoizedProps;    const newProps = workInProgress.pendingProps;    if (      oldProps !== newProps ||      hasLegacyContextChanged() ||      // Force a re-render if the implementation changed due to hot reload:      (__DEV__ ? workInProgress.type !== current.type : false)    ) {      // If props or context changed, mark the fiber as having performed work.      // This may be unset if the props are determined to be equal later (memo).      didReceiveUpdate = true;    } else {

這里比較的是 props 和 context 是否發生了變化。當他們其中一個變化時,則將 didReceiveUpdate 設置為 true。32228資訊網——每日最新資訊28at.com

這里的 hasLegacyContextChanged()  兼容的是舊版本 的 context,新版本的 context 是否發生變化會反應到 pending update 中,也就是使用下面的 checkScheduledUpdateOrContext 來查看是否有更新的調度任務。32228資訊網——每日最新資訊28at.com

當 props 和 context 都沒有發生變化,并且也不存在對應的調度任務時,將其設置為 false。32228資訊網——每日最新資訊28at.com

如果有 state/context 發生變化,則會存在調度任務。32228資訊網——每日最新資訊28at.com

} else {  // Neither props nor legacy context changes. Check if there's a pending  // update or context change.  const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(    current,    renderLanes,  );  if (    !hasScheduledUpdateOrContext &&    // If this is the second pass of an error or suspense boundary, there    // may not be work scheduled on `current`, so we check for this flag.    (workInProgress.flags & DidCapture) === NoFlags  ) {    // No pending updates or context. Bail out now.    didReceiveUpdate = false;    return attemptEarlyBailoutIfNoScheduledUpdate(      current,      workInProgress,      renderLanes,    );  }

這里有一個很關鍵的點,就在于當方法進入到 attemptEarlyBailoutIfNoScheduledUpdate 去判斷子節點是否可以 bailout 時,他并沒有比較子節點的 props。32228資訊網——每日最新資訊28at.com

核心的邏輯在 bailoutOnAlreadyFinishedWork 中。32228資訊網——每日最新資訊28at.com

{  ...  // 判斷子節點是否有 pending 任務要做  if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {    // The children don't have any work either. We can skip them.    return null  }  // This fiber doesn't have work, but its subtree does. Clone the child  // fibers and continue.  cloneChildFibers(current, workInProgress);  return workInProgress.child;}

所以這里有一個很重要的思考就是為什么判斷子節點是否發生變化時,并沒有去比較 props,這是性能優化策略的關鍵一步,結合我們之前講的性能優化策略去理解,你就能知道答案。32228資訊網——每日最新資訊28at.com

回到 beginWork, 后續的邏輯會根據不同的 tag,創建不同類型的 Fiber 節點。32228資訊網——每日最新資訊28at.com

switch (workInProgress.tag) {  case IndeterminateComponent: {    return mountIndeterminateComponent(      current,      workInProgress,      workInProgress.type,      renderLanes,    );  }  case LazyComponent: {    const elementType = workInProgress.elementType;    return mountLazyComponent(      current,      workInProgress,      elementType,      renderLanes,    );  }  case FunctionComponent: {    const Component = workInProgress.type;    const unresolvedProps = workInProgress.pendingProps;    const resolvedProps =      workInProgress.elementType === Component        ? unresolvedProps        : resolveDefaultProps(Component, unresolvedProps);    return updateFunctionComponent(      current,      workInProgress,      Component,      resolvedProps,      renderLanes,    );  }  case ClassComponent: {    const Component = workInProgress.type;    const unresolvedProps = workInProgress.pendingProps;    const resolvedProps =      workInProgress.elementType === Component        ? unresolvedProps        : resolveDefaultProps(Component, unresolvedProps);    return updateClassComponent(      current,      workInProgress,      Component,      resolvedProps,      renderLanes,    );  }  case HostRoot:    return updateHostRoot(current, workInProgress, renderLanes);  case HostComponent:    return updateHostComponent(current, workInProgress, renderLanes);  case HostText:    return updateHostText(current, workInProgress);  case SuspenseComponent:    return updateSuspenseComponent(current, workInProgress, renderLanes);  case HostPortal:    return updatePortalComponent(current, workInProgress, renderLanes);  case ForwardRef: {    const type = workInProgress.type;    const unresolvedProps = workInProgress.pendingProps;    const resolvedProps =      workInProgress.elementType === type        ? unresolvedProps        : resolveDefaultProps(type, unresolvedProps);    return updateForwardRef(      current,      workInProgress,      type,      resolvedProps,      renderLanes,    );  }    // ...    case MemoComponent: {    const type = workInProgress.type;    const unresolvedProps = workInProgress.pendingProps;    // Resolve outer props first, then resolve inner props.    let resolvedProps = resolveDefaultProps(type, unresolvedProps);    if (__DEV__) {      if (workInProgress.type !== workInProgress.elementType) {        const outerPropTypes = type.propTypes;        if (outerPropTypes) {          checkPropTypes(            outerPropTypes,            resolvedProps, // Resolved for outer only            'prop',            getComponentNameFromType(type),          );        }      }    }    resolvedProps = resolveDefaultProps(type.type, resolvedProps);    return updateMemoComponent(      current,      workInProgress,      type,      resolvedProps,      renderLanes,    );  }}// ... 其他類型

我們重點關注 updateFunctionComponent 的執行邏輯,可以發現,當 didReceiveUpdate 為 false 時,會執行 bailout 跳過創建過程。32228資訊網——每日最新資訊28at.com

if (current !== null && !didReceiveUpdate) {  bailoutHooks(current, workInProgress, renderLanes);  return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);}

如果無法 bailout,則最后執行 reconcileChildren 創建新的子節點。32228資訊網——每日最新資訊28at.com

reconcileChildren(current, workInProgress, nextChildren, renderLanes);  return workInProgress.child;

另外我們還應該關注 updateMemoComponent 中的邏輯。該邏輯通過淺比較函數 shallowEqual 來比較更新前后兩個 props 的差異。當比較結果為 true 時,也是調用 bailout 跳過創建。32228資訊網——每日最新資訊28at.com

而不是沿用 didReceiveUpdate 的結果。32228資訊網——每日最新資訊28at.com

if (!hasScheduledUpdateOrContext) {  // This will be the props with resolved defaultProps,  // unlike current.memoizedProps which will be the unresolved ones.  const prevProps = currentChild.memoizedProps;  // Default to shallow comparison  let compare = Component.compare;  compare = compare !== null ? compare : shallowEqual;  if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);  }}

二、completeWork

在 performUnitOfWork 執行過程中,當發現當前節點已經沒有子節點了,就會調用 completeUnitOfWork 方法。32228資訊網——每日最新資訊28at.com

if (next === null) {  // If this doesn't spawn new work, complete the current work.  completeUnitOfWork(unitOfWork);

該方法主要用于執行 completeWork。completeWork 主要的作用是用于創建與 Fiber 節點對應的 DOM 節點。32228資訊網——每日最新資訊28at.com

這里創建的 DOM 節點并沒有插入到 HTML 中,還存在于內存里。32228資訊網——每日最新資訊28at.com

const instance = createInstance(  type,  newProps,  rootContainerInstance,  currentHostContext,  workInProgress,);appendAllChildren(instance, workInProgress, false, false);

由于 completeWork 的執行是從葉子節點,往根節點執行,因此,每次我們將新創建的節點 append 到父節點,執行到最后 rootFiber 時,一個完整的 DOM 樹就已經構建完成了。32228資訊網——每日最新資訊28at.com

completeWork 的執行順序是一個回溯的過程。32228資訊網——每日最新資訊28at.com

當然,Fiber 節點與 DOM 節點之間,也會保持一一對應的引用關系,因此在更新階段,我們能夠輕易的判斷和復用已經存在的 DOM 節點從而避免重復創建。32228資訊網——每日最新資訊28at.com

四、遍歷順序

beginWork 和 completeWork 的執行順序理解起來比較困難,為了便于理解,我們這里用一個圖示來表達。32228資訊網——每日最新資訊28at.com

例如有這樣一個結構的節點。32228資訊網——每日最新資訊28at.com

<div id="root">  <div className="1">    <div className="1-1">1-1</div>    <div className="1-2">1-2</div>    <div className="1-3">      <div className="1-3-1">1-3-1</div>    </div>  </div>  <div className="2">2</div>  <div className="3">3</div></div>

beginWork 的執行是按照 Fiber 節點的鏈表深度遍歷執行。32228資訊網——每日最新資訊28at.com

completeWork 則是當 fiber.next === null 時開始執行,他一個從葉子節點往根節點執行的回溯過程。當葉子節點被執行過后,則對葉子節點的父節點執行 completeWork。32228資訊網——每日最新資訊28at.com

下圖就是上面 demo 的執行順序。32228資訊網——每日最新資訊28at.com

其中藍色圓代表對應節點的 beginWork 執行。黃色圓代表對應節點的 completeWork 執行。32228資訊網——每日最新資訊28at.com

32228資訊網——每日最新資訊28at.com

五、總結

beginWork 與 completeWork 的執行是 React 源碼中最重要的部分,理解他們的核心邏輯能有效幫助我們做好項目的性能優化。因此在學習他們的過程中,應該結合實踐去思考優化策略。32228資訊網——每日最新資訊28at.com

不過性能優化的方式在我們之前的章節中已經詳細介紹過,因此這里帶大家閱讀源碼更多的是做一個驗證,去揭開源碼的神秘面紗。32228資訊網——每日最新資訊28at.com

到這篇文章這里,React 原理的大多數重要邏輯我們在知命境的文章都已經給大家分享過了,其中包括同步更新邏輯,異步更新邏輯,任務優先級隊列,任務調度,Fiber 中的各種鏈表結構,各種比較方式的成本,包括本文介紹的 Fiber tree 的構建過程,大家可以把這些零散的文章串起來總結一下,有能力的可以自己在閱讀源碼時結合我分享的內容進一步擴展和完善。32228資訊網——每日最新資訊28at.com

閱讀源碼是一個高投入,低回報的過程,希望我的這些文章能有效幫助大家以更低的時間成本獲得更高的知識回報。32228資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-88379-0.htmlReact 源碼中最重要的部分,你知道有哪些嗎?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 常見,但是總回答不好的面試題:JS 模塊化以及模塊打包器

下一篇: SpringBoot3.x 和 WebSocket 在物聯網設備管理中的應用

標簽:
  • 熱門焦點
  • 小米官宣:2023年上半年出貨量中國第一!

    今日早間,小米電視官方微博帶來消息,稱2023年小米電視上半年出貨量達到了中國第一,同時還表示小米電視的巨屏風暴即將開始。“公布一個好消息2023年#小米電視上半年出貨量中國
  • K8S | Service服務發現

    一、背景在微服務架構中,這里以開發環境「Dev」為基礎來描述,在K8S集群中通常會開放:路由網關、注冊中心、配置中心等相關服務,可以被集群外部訪問;圖片對于測試「Tes」環境或者
  • 三言兩語說透設計模式的藝術-單例模式

    寫在前面單例模式是一種常用的軟件設計模式,它所創建的對象只有一個實例,且該實例易于被外界訪問。單例對象由于只有一個實例,所以它可以方便地被系統中的其他對象共享,從而減少
  • 慕巖炮轟抖音,百合網今何在?

    來源:價值研究所 作者:Hernanderz&ldquo;難道就因為自己的一個產品牛逼了,從客服到總裁,都不愿意正視自己產品和運營上的問題,選擇逃避了嗎?&rdquo;這一番話,出自百合網聯合創
  • 當家的盒馬,加速謀生

    來源 | 價值星球Planet作者 | 歸去來自己&ldquo;當家&rdquo;的盒馬,開始加速謀生了。據盒馬官微消息,盒馬計劃今年開放生鮮供應鏈,將其生鮮商品送往食堂。目前,盒馬在上海已經與
  • 三星電子Q2營收60萬億韓元 存儲業務營收同比仍下滑超過50%

    7月27日消息,據外媒報道,從三星電子所發布的財報來看,他們主要利潤來源的存儲芯片業務在今年二季度仍不樂觀,營收同比仍在大幅下滑,所在的設備解決方案
  • 半導體需求下滑 三星電子DS業務部門今年營業虧損預計超10萬億韓元

    7月17日消息,據外媒報道,去年下半年開始的半導體需求下滑,影響到了三星電子、SK海力士、英特爾等諸多廠商,營收明顯下滑,部分廠商甚至出現了虧損。作為
  • OPPO K11評測:旗艦級IMX890加持 2000元檔最強影像手機

    【Techweb評測】中端機型用戶群體巨大,占了中國目前手機市場的大頭,一直以來都是各手機品牌的“必爭之地”,其中OPPO K系列機型一直以來都以高品質、
  • 機構稱Q2全球智能手機出貨量同比下滑11% 蘋果份額依舊第2

    7月20日消息,據外媒報道,研究機構的報告顯示,由于需求下滑,今年二季度全球智能手機的出貨量,同比下滑了11%,三星、蘋果等主要廠商的銷量,較去年同期均有下
Top 主站蜘蛛池模板: 简阳市| 额济纳旗| 抚州市| 凤山市| 甘德县| 福安市| 临沭县| 交口县| 镇赉县| 尼木县| 海盐县| 甘泉县| 平谷区| 延吉市| 镇雄县| 阿图什市| 永清县| 黔江区| 佛冈县| 甘肃省| 营山县| 达日县| 新兴县| 庐江县| 长垣县| 花垣县| 丹阳市| 阳西县| 陈巴尔虎旗| 丽水市| 开原市| 饶阳县| 应城市| 信丰县| 赫章县| 铜鼓县| 龙胜| 北宁市| 贡觉县| 苏州市| 达孜县|