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

React深入分析更新的創(chuàng)建源碼

 更新時間:2023年01月14日 08:53:23   作者:web老猴子  
React組件分為函數(shù)組件與class組件;函數(shù)組件是無狀態(tài)組件,class稱為類組件;函數(shù)組件只有props,沒有自己的私有數(shù)據(jù)和生命周期函數(shù);class組件有自己私有數(shù)據(jù)(this.state)和生命周期函數(shù)

React 的鮮活生命起源于 ReactDOM.render ,這個過程會為它的一生儲備好很多必需品,我們順著這個線索,一探嬰兒般 React 應用誕生之初的悅?cè)弧?/p>

更新創(chuàng)建的操作我們總結(jié)為以下兩種場景

  • ReactDOM.render
  • setState
  • forceUpdate

ReactDom.render

串聯(lián)該內(nèi)容,一圖以蔽之

首先看到 react-dom/client/ReactDOM 中對于 ReactDOM 的定義,其中包含我們熟知的方法、不穩(wěn)定方法以及即將廢棄方法。

const ReactDOM: Object = {createPortal,// LegacyfindDOMNode,hydrate,render,unstable_renderSubtreeIntoContainer,unmountComponentAtNode,// Temporary alias since we already shipped React 16 RC with it.// TODO: remove in React 17.unstable_createPortal(...args) {// ...return createPortal(...args);},unstable_batchedUpdates: batchedUpdates,flushSync: flushSync,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {// ...},
}; 

此處方法均來自 ./ReactDOMLegacyrender 方法定義很簡單,正如我們常使用的那樣,第一個參數(shù)是組件,第二個參數(shù)為組件所要掛載的DOM節(jié)點,第三個參數(shù)為回調(diào)函數(shù)。

export function render( element: React$Element<any>,container: DOMContainer,callback: ?Function, ) {// ...return legacyRenderSubtreeIntoContainer(null,element,container,false,callback,);
}
function legacyRenderSubtreeIntoContainer( parentComponent: ?React$Component<any, any>,children: ReactNodeList,container: DOMContainer,forceHydrate: boolean,callback: ?Function, ) {// TODO: Without `any` type, Flow says "Property cannot be accessed on any// member of intersection type." Whyyyyyy.let root: RootType = (container._reactRootContainer: any);let fiberRoot;if (!root) {// Initial mountroot = container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate,);fiberRoot = root._internalRoot;if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = getPublicRootInstance(fiberRoot);originalCallback.call(instance);};}// 初次渲染,不會將更新標記為batched.unbatchedUpdates(() => {updateContainer(children, fiberRoot, parentComponent, callback);});} else {fiberRoot = root._internalRoot;if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = getPublicRootInstance(fiberRoot);originalCallback.call(instance);};}// UpdateupdateContainer(children, fiberRoot, parentComponent, callback);}return getPublicRootInstance(fiberRoot);
} 

這段代碼我們不難發(fā)現(xiàn),調(diào)用 ReactDOM.render 時,返回的 parentComponent 是 null,并且初次渲染,不會進行批量策略的更新,而是需要盡快的完成。(batchedUpdates批量更新后續(xù)介紹)

從這部分源碼我們不難看出,rendercreateProtal 的用法的聯(lián)系,通過DOM容器創(chuàng)建Root節(jié)點的形式

function legacyCreateRootFromDOMContainer( container: DOMContainer,forceHydrate: boolean, ): RootType {const shouldHydrate =forceHydrate || shouldHydrateDueToLegacyHeuristic(container);// First clear any existing content.if (!shouldHydrate) {let warned = false;let rootSibling;while ((rootSibling = container.lastChild)) {// ...container.removeChild(rootSibling);}}return createLegacyRoot(container,shouldHydrate? {hydrate: true,}: undefined,);
} 

createLegacyRoot 定義于 ./ReactDOMRoot 中,指定了創(chuàng)建的DOM容器和一些option設置,最終會返回一個 ReactDOMBlockingRoot 。

export function createLegacyRoot( container: DOMContainer,options?: RootOptions, ): RootType {return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}
function ReactDOMBlockingRoot( container: DOMContainer,tag: RootTag,options: void | RootOptions, ) {this._internalRoot = createRootImpl(container, tag, options);
}
function createRootImpl( container: DOMContainer,tag: RootTag,options: void | RootOptions, ) {// Tag is either LegacyRoot or Concurrent Root// ...const root = createContainer(container, tag, hydrate, hydrationCallbacks);// ...return root;
} 

關鍵點在于,方法最終調(diào)用了 createContainer 來創(chuàng)建root,而該方法中會創(chuàng)建我們上一節(jié)所介紹的 FiberRoot ,該對象在后續(xù)的更新調(diào)度過程中起著非常重要的作用,到更新調(diào)度內(nèi)容我們詳細介紹。

在這部分我們看到了兩個方法,分別是:createContainer、 updateContainer,均出自 react-reconciler/inline.dom , 最終定義在 ``react-reconciler/src/ReactFiberReconciler` 。創(chuàng)建方法很簡單,如下

export function createContainer(containerInfo: Container,tag: RootTag,hydrate: boolean,hydrationCallbacks: null | SuspenseHydrationCallbacks,): OpaqueRoot {return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
} 

我們繼續(xù)往下看,緊跟著能看到 updateContainer 方法,該方法定義了更新相關的操作,其中最重要的一個點就是 expirationTime ,直接譯為中文是過期時間,我們想想,此處為何要過期時間,這個過期的含義是什么呢?這個過期時間是如何計算的呢?繼續(xù)往下我們可以看到,computeExpirationForFiber 方法用于過期時間的計算,我們先將源碼片段放在此處。

export function updateContainer( element: ReactNodeList,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,callback: ?Function, ): ExpirationTime {const current = container.current;const currentTime = requestCurrentTimeForUpdate();// ...const suspenseConfig = requestCurrentSuspenseConfig();const expirationTime = computeExpirationForFiber(currentTime,current,suspenseConfig,);// ...const context = getContextForSubtree(parentComponent);if (container.context === null) {container.context = context;} else {container.pendingContext = context;}// ...const update = createUpdate(expirationTime, suspenseConfig);// Caution: React DevTools currently depends on this property// being called "element".update.payload = {element};callback = callback === undefined ? null : callback;if (callback !== null) {warningWithoutStack(typeof callback === 'function','render(...): Expected the last optional `callback` argument to be a ' +'function. Instead received: %s.',callback,);update.callback = callback;}enqueueUpdate(current, update);scheduleWork(current, expirationTime);return expirationTime;
} 

計算完更新超時時間,而后創(chuàng)建更新對象 createUpdate ,進而將element綁定到update對象上,如果存在回調(diào)函數(shù),則將回調(diào)函數(shù)也綁定到update對象上。update對象創(chuàng)建完成,將update添加到UpdateQueue中,關于update和UpdateQueue數(shù)據(jù)結(jié)構見上一節(jié)講解。至此,開始任務調(diào)度。

setState 與 forceUpdate

這兩個方法綁定在我們當初定義React的文件中,具體定義在 react/src/ReactBaseClasses 中,如下

Component.prototype.setState = function(partialState, callback) {// ...this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
}; 

這就是為何React基礎上拓展React-Native能輕松自如,因為React只是做了一些規(guī)范和結(jié)構設定,具體實現(xiàn)是在React-Dom或React-Native中,如此達到了平臺適配性。

Class組件的更新使用 this.setState ,這個api我們早已爛熟于心,對于對象組件的更新創(chuàng)建,定義在 react-reconciler/src/ReactFiberClassComponent.js ,classComponentUpdater對象定義了 enqueueSetStateenqueueReplaceState 以及 enqueueForceUpdate 對象方法,觀察這兩個方法會發(fā)現(xiàn),不同在于enqueueReplaceStateenqueueForceUpdate 會在創(chuàng)建的update對象綁定一個tag,用于標志更新的類型是 ReplaceState 還是 ForceUpdate ,具體實現(xiàn)我們一起來看代碼片段。

const classComponentUpdater = {isMounted,enqueueSetState(inst, payload, callback) {const fiber = getInstance(inst);const currentTime = requestCurrentTimeForUpdate();const suspenseConfig = requestCurrentSuspenseConfig();const expirationTime = computeExpirationForFiber(currentTime,fiber,suspenseConfig,);const update = createUpdate(expirationTime, suspenseConfig);update.payload = payload;if (callback !== undefined && callback !== null) {// ...update.callback = callback;}enqueueUpdate(fiber, update);scheduleWork(fiber, expirationTime);},enqueueReplaceState(inst, payload, callback) {const fiber = getInstance(inst);const currentTime = requestCurrentTimeForUpdate();const suspenseConfig = requestCurrentSuspenseConfig();const expirationTime = computeExpirationForFiber(currentTime,fiber,suspenseConfig,);const update = createUpdate(expirationTime, suspenseConfig);update.tag = ReplaceState;update.payload = payload;if (callback !== undefined && callback !== null) {// ...update.callback = callback;}enqueueUpdate(fiber, update);scheduleWork(fiber, expirationTime);},enqueueForceUpdate(inst, callback) {const fiber = getInstance(inst);const currentTime = requestCurrentTimeForUpdate();const suspenseConfig = requestCurrentSuspenseConfig();const expirationTime = computeExpirationForFiber(currentTime,fiber,suspenseConfig,);const update = createUpdate(expirationTime, suspenseConfig);update.tag = ForceUpdate;if (callback !== undefined && callback !== null) {// ...update.callback = callback;}enqueueUpdate(fiber, update);scheduleWork(fiber, expirationTime);},
}; 

我們也能發(fā)現(xiàn),其實通過setState更新的操作實現(xiàn)和ReactDOM.render基本一致。

1.更新過期時間

2.創(chuàng)建Update對象

3.為update對象綁定一些屬性,比如 tag 、callback

4.創(chuàng)建的update對象入隊 (enqueueUpdate)

5.進入調(diào)度過程

expirationTime的作用

expirationTime用于React在調(diào)度和渲染過程,優(yōu)先級判斷,針對不同的操作,有不同響應優(yōu)先級,這時我們通過 currentTime: ExpirationTime 變量與預定義的優(yōu)先級EXPIRATION常量計算得出expirationTime。難道currentTime如我們平時糟糕代碼中的 Date.now() ?錯!如此操作會產(chǎn)生頻繁計算導致性能降低,因此我們定義currentTime的計算規(guī)則。

獲取currentTime

export function requestCurrentTimeForUpdate() {if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {// We're inside React, so it's fine to read the actual time.return msToExpirationTime(now());}// We're not inside React, so we may be in the middle of a browser event.if (currentEventTime !== NoWork) {// Use the same start time for all updates until we enter React again.return currentEventTime;}// This is the first update since React yielded. Compute a new start time.currentEventTime = msToExpirationTime(now());return currentEventTime;
} 

該方法定義了如何去獲得當前時間,now 方法由 ./SchedulerWithReactIntegration 提供,對于now方法的定義似乎不太好找,我們通過斷點調(diào)試 Scheduler_now ,最終能夠發(fā)現(xiàn)時間的獲取是通過 window.performance.now(), 緊接著找尋到 msToExpirationTime 定義在 ReactFiberExpirationTime.js ,定義了expirationTime相關處理邏輯。

不同的expirationTime

閱讀到 react-reconciler/src/ReactFilberExpirationTime ,對于expirationTime的計算有三個不同方法,分別為:computeAsyncExpirationcomputeSuspenseExpiration 、computeInteractiveExpiration 。這三個方法均接收三個參數(shù),第一個參數(shù)均為以上獲取的 currentTime ,第二個參數(shù)為約定的超時時間,第三個參數(shù)與批量更新的粒度有關。

export const Sync = MAX_SIGNED_31_BIT_INT;
export const Batched = Sync - 1;
const UNIT_SIZE = 10;
const MAGIC_NUMBER_OFFSET = Batched - 1;
// 1 unit of expiration time represents 10ms.
export function msToExpirationTime(ms: number): ExpirationTime {// Always add an offset so that we don't clash with the magic number for NoWork.return MAGIC_NUMBER_OFFSET - ((ms / UNIT_SIZE) | 0);
}
export function expirationTimeToMs(expirationTime: ExpirationTime): number {return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
}
function ceiling(num: number, precision: number): number {return (((num / precision) | 0) + 1) * precision;
}
function computeExpirationBucket( currentTime,expirationInMs,bucketSizeMs, ): ExpirationTime {return (MAGIC_NUMBER_OFFSET -ceiling(MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE,bucketSizeMs / UNIT_SIZE,));
}
// TODO: This corresponds to Scheduler's NormalPriority, not LowPriority. Update
// the names to reflect.
export const LOW_PRIORITY_EXPIRATION = 5000;
export const LOW_PRIORITY_BATCH_SIZE = 250;
export function computeAsyncExpiration( currentTime: ExpirationTime, ): ExpirationTime {return computeExpirationBucket(currentTime,LOW_PRIORITY_EXPIRATION,LOW_PRIORITY_BATCH_SIZE,);
}
export function computeSuspenseExpiration( currentTime: ExpirationTime,timeoutMs: number, ): ExpirationTime {// TODO: Should we warn if timeoutMs is lower than the normal pri expiration time?return computeExpirationBucket(currentTime,timeoutMs,LOW_PRIORITY_BATCH_SIZE,);
}
export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150;
export const HIGH_PRIORITY_BATCH_SIZE = 100;
export function computeInteractiveExpiration(currentTime: ExpirationTime) {return computeExpirationBucket(currentTime,HIGH_PRIORITY_EXPIRATION,HIGH_PRIORITY_BATCH_SIZE,);
} 

重點在于 ceil 方法的定義,方法傳遞兩個參數(shù),需要求值的number和期望精度precision,不妨隨意帶入兩個值觀察該函數(shù)的作用,number = 100,precision = 10,那么函數(shù)返回值為 (((100 / 10) | 0) + 1) * 10,我們保持precision值不變,更改number會發(fā)現(xiàn),當我們的值在100-110之間時,該函數(shù)返回的值相同。哦!此時恍然大悟,原來這個方法就是保證在同一個bucket中的更新獲取到相同的過期時間 expirationTime ,就能夠?qū)崿F(xiàn)在較短時間間隔內(nèi)的更新創(chuàng)建能夠__合并處理__。

關于超時時間的處理是很復雜的,除了我們看到的 expirationTime ,還有 childExpirationTime 、root.firstPendingTime 、root.lastExpiredTime 、root.firstSuspendedTimeroot.lastSuspendedTime ,root下的相關屬性標記了其下子節(jié)點fiber的expirationTime的次序,構成處理優(yōu)先級的次序,childExpirationTime 則是在遍歷子樹時,更新其 childExpirationTime 值為子節(jié)點 expirationTime 。

以上是React創(chuàng)建更新的核心流程,任務調(diào)度我們下次再見。

到此這篇關于React深入分析更新的創(chuàng)建源碼的文章就介紹到這了,更多相關React更新的創(chuàng)建內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 關于React項目中的PDF展示解決方案

    關于React項目中的PDF展示解決方案

    這篇文章主要介紹了關于React項目中的PDF展示解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • React-Router v6實現(xiàn)頁面級按鈕權限示例詳解

    React-Router v6實現(xiàn)頁面級按鈕權限示例詳解

    這篇文章主要介紹了使用 reac+reactRouter來實現(xiàn)頁面級的按鈕權限功能,這篇文章分三部分,實現(xiàn)思路、代碼實現(xiàn)、踩坑記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2023-10-10
  • react中setState的執(zhí)行機制詳解

    react中setState的執(zhí)行機制詳解

    setState() 的執(zhí)行機制包括狀態(tài)合并、批量更新、異步更新、虛擬 DOM 比較和渲染組件等步驟,這樣可以提高性能并優(yōu)化渲染過程,這篇文章主要介紹了react中的setState的執(zhí)行機制,需要的朋友可以參考下
    2023-10-10
  • react高階組件經(jīng)典應用之權限控制詳解

    react高階組件經(jīng)典應用之權限控制詳解

    在React中,高階組件是重用組件邏輯的一項高級技術。下面這篇文章主要給大家介紹了關于react高階組件經(jīng)典應用之權限控制的相關資料,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2017-09-09
  • React如何使用axios請求數(shù)據(jù)并把數(shù)據(jù)渲染到組件

    React如何使用axios請求數(shù)據(jù)并把數(shù)據(jù)渲染到組件

    這篇文章主要介紹了React如何使用axios請求數(shù)據(jù)并把數(shù)據(jù)渲染到組件,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 使用react-native-doc-viewer實現(xiàn)文檔預覽

    使用react-native-doc-viewer實現(xiàn)文檔預覽

    這篇文章主要介紹了使用react-native-doc-viewer實現(xiàn)文檔預覽,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • webpack打包react項目的實現(xiàn)方法

    webpack打包react項目的實現(xiàn)方法

    這篇文章主要介紹了webpack打包react項目的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • React改變元素樣式的操作代碼

    React改變元素樣式的操作代碼

    這篇文章主要介紹了React技巧之改變元素樣式,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • react實現(xiàn)antd線上主題動態(tài)切換功能

    react實現(xiàn)antd線上主題動態(tài)切換功能

    這篇文章主要介紹了react實現(xiàn)antd線上主題動態(tài)切換功能,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-08-08
  • 記錄React使用connect后,ref.current為null問題及解決

    記錄React使用connect后,ref.current為null問題及解決

    記錄React使用connect后,ref.current為null問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05

最新評論