溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

react渲染原理怎么實現

發布時間:2022-02-25 10:14:39 來源:億速云 閱讀:216 作者:iii 欄目:開發技術

本篇內容主要講解“react渲染原理怎么實現”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“react渲染原理怎么實現”吧!

一、JSX

那么首先我們來看一下,簡單的React組件,代碼如下:

import React from 'react';

export default function App() {
  return (
    <div className="App">
      <h2>Hello React</h2>
    </div>
  );
}

在這個代碼中我們用的語法被稱為 JSX,它是React.createElement方法的語法糖,我們通過使用 JSX 可以直觀的展現 UI 及交互可以實現關注點分離,而且每一個react組價的頂部都要導入React,因為JSX實際上依賴的是Babel(@bable/preset-react)從而來對語法進行轉換,最終生成我們需要的React.createElement的嵌套語法。下面我們來看下 JSX 轉換渲染后的結果吧,代碼如下:

function App() {
  return React.createElement(
    'div',
    {
      className: 'App',
    },
    React.createElement('h2', null, 'Hello React')
  );
}

二、createElement

createElement()方法定如下:

React.createElement(type, [props], [...children]);

createElement()接收三個參數,在代碼中我們可以知道它分別是元素類型、屬性值和子元素這三個值,而且它最終會生成Virtual DOM,我們現在將<app/>組件內容打印到我們的控制臺中

我們通過截圖可以看到 Virtual DOM 本質上是 JS 對象,所以我們將節點信息通過鍵值對的方式存儲起來,同時使用嵌套來表示節點間的層級關系。然后再使用 VDOM 能夠避免頻繁的進行 DOM 操作,同時也為后面的 React Diff 算法創造了條件。那么我們現在回到我們的createElement()方法中,來看一下它是如何生產 VDOM 的。


三、createElement()方法精簡版

首先我們通過createElement()方法會先通過遍歷config獲取所有的參數,然后獲取其子節點以及默認的Props的值,然后我們在將值傳遞給ReactElement()調用返回JS對象。

在截圖中值得我們去注意的是,每個react組件都會使用$$typeof來進行標識,它的值使用了Symbol數據結構來確保唯一性。


四、ReactDOM.render

通過上面的步驟,我們得到了VDOM,react通過協調算法(reconciliation)去比較更新前后的VDOM,從而找到需要更新的最小操作,來減少多次操作DOM的成本,由于我們遍歷組件樹,當組件越來越大我們的遞歸遍歷成本就會越高所有我們有了下面這種解決方法。

render()方法:

ReactDOM.render(element, container[, callback])

這邊的話我們還需要了解ReactDOM.render是怎么構建fiber tree,其實呢在ReactDOM.render中實際調用了legacyRenderSubtreeIntoContainer這個方法,下面是有關的調用過程,代碼如下:

ReactDOM = {
  render(element, container, callback) {
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback
    );
  },
};

在代碼中的elementcontainer我想大家都很熟悉,然而在代碼中的callback是用來渲染完成后需要執行的回調函數。

接下來我們再來看看該方法的定義,代碼如下:

function legacyRenderSubtreeIntoContainer(
  parentComponent,
  children,
  container,
  forceHydrate,
  callback
) {
  let root = container._reactRootContainer;
  let fiberRoot;
  // 初次渲染
  if (!root) {
    // 初始化掛載,獲得React根容器對象
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate
    );
    fiberRoot = root._internalRoot;

    // 初始化安裝不需要批量更新,需要盡快完成
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    fiberRoot = root._internalRoot;

    updateContainer(children, fiberRoot, parentComponent, callback);
  }
  return getPublicRootInstance(fiberRoot);
}

我們可以發現到,在代碼中因為掛載是root,所以我們需要將parentComponent的值設置為null

除此之外對于另一個參數forceHydrate代表是否是服務端渲染,因為在這邊調用了render()方法為客戶端渲染,所以默認為false。

因為是首次掛載,所以rootcontainer._reactRootContainer獲取不到值,就會創建FiberRoot對象。而且在FiberRoot對象創建過程中考慮到了服務端渲染的情況,并且函數之間相互調用非常多,所以這里直接展示其最終調用的核心方法,代碼如下所示:

// 創建fiberRoot和rootFiber并相互引用
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
  const root = new FiberRootNode(containerInfo, tag, hydrate);
  if (enableSuspenseCallback) {
    root.hydrationCallbacks = hydrationCallbacks;
  }

  // 創建fiber tree的根節點,即rootFiber
  const uninitializedFiber = createHostRootFiber(tag);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  initializeUpdateQueue(uninitializedFiber);

  return root;
}

我們從代碼中可以知道,在這個方法中containerInfo就是root節點,然而tagFiberRoot節點的標記,這里變為LegacyRoot。而且另外兩個參數和服務端渲染是有關的,在代碼中這里使用FiberRootNode方法創建了FiberRoot對象,并使用createHostRootFiber方法創建RootFiber對象,使FiberRoot中的current指向RootFiber,RootFiberstateNode指向FiberRoot,從而形成相互引用。

下面的兩個構造函數是展現出了fiberRoot以及rootFiber的部分重要的屬性。

FiberRootNode部分屬性,代碼如下:

function FiberRootNode(containerInfo, tag, hydrate) {
  // 用于標記fiberRoot的類型
  this.tag = tag;
  // 指向當前激活的與之對應的rootFiber節點
  this.current = null;
  // 和fiberRoot關聯的DOM容器的相關信息
  this.containerInfo = containerInfo;
  // 當前的fiberRoot是否處于hydrate模式
  this.hydrate = hydrate;
  // 每個fiberRoot實例上都只會維護一個任務,該任務保存在callbackNode屬性中
  this.callbackNode = null;
  // 當前任務的優先級
  this.callbackPriority = NoPriority;
}

Fiber Node構造函數的部分屬性代碼如下:

function FiberNode(tag, pendingProps, key, mode) {
  // rootFiber指向fiberRoot,child fiber指向對應的組件實例
  this.stateNode = null;
  // return屬性始終指向父節點
  this.return = null;
  // child屬性始終指向第一個子節點
  this.child = null;
  // sibling屬性始終指向第一個兄弟節點
  this.sibling = null;
  // 表示更新隊列,例如在常見的setState操作中,會將需要更新的數據存放到updateQueue隊列中用于后續調度
  this.updateQueue = null;
  // 表示當前更新任務的過期時間,即在該時間之后更新任務將會被完成
  this.expirationTime = NoWork;
}

最終生成的fiber tree結構

五、React Diff 算法

對于react來說并不會比原生操作的DOM快,但是在大型的應用中,我們往往是不需要每次都進行重新渲染的,所以這時候可以讓react通過VCOM 以及diff算法能夠值更新必要的DOM。

到此,相信大家對“react渲染原理怎么實現”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女