感謝 yck: 剖析 React 源碼解析,本篇文章是在讀完他的文章的基礎上,將他的文章進行拆解和加工,加入我本身的一下理解和例子,便於你們理解。以爲yck寫的真的很棒 。React 版本爲 16.8.6,關於源碼的閱讀,能夠移步到yck react源碼解析html
本文永久有效連接: react解析: render的中的update(四)
上一章節說到,不存在root數據節點,即經過createFiberRoot 函數建立FiberRoot,FiberRoot
對象是整個React應用的起點,同時也記錄了整個React應用更新過程當中的各類信息。react
下面將要聊到的就是,當root唄建立後,還會發生什麼👇👇git
下面銜接上一部份內容,不懂得能夠查看上一章節。github
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: DOMContainer,
forceHydrate: boolean,
callback: ?Function,
) {
// 初始化時,container 確定沒有 _reactRootContainer屬性
let root: Root = (container._reactRootContainer: any);
if (!root) {
// 省略建立root部分
unbatchedUpdates(() => {
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
});
}
}複製代碼
在root剛剛被建立時,parentComponent
通常都爲null;數組
unbatchedUpdates
函數在這裏做用是:告知React內部不進行批量更新,即不用將多個setState合併爲一個;
(setState在後面的章節咱們將會說到)bash
那麼這裏實際調用的就是root.render函數,root是ReactRoot實例對象,即調用 root.render函數 == ReactRoot.prototype.render函數
。dom
ReactRoot.prototype.render = function(
children: ReactNodeList,
callback: ?() => mixed,
): Work {
// 這裏指 FiberRoot
const root = this._internalRoot;
const work = new ReactWork();
callback = callback === undefined ? null : callback;
// 若是有 callback,就 push 進 work 中的數組
if (callback !== null) {
work.then(callback);
}
// work._onCommit 就是用於執行全部回調函數的
updateContainer(children, root, null, work._onCommit);
return work;
};複製代碼
函數中的參數children
便是ReactElement節點對象,callback
爲回調函數。ReactWork
實例對象的主要做用就是維護一個回調數組,可查看yck: ReactWork 源碼 327行,若是傳入參數中存在callback,就將其掛載ReactWork
實例對象中;函數
下面來看看updateContainer函數會作什麼。
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): ExpirationTime {
const current = container.current;
// 計算時間
const currentTime = requestCurrentTime();
// expirationTime 表明優先級,數字越大優先級越高
// sync 的數字是最大的,因此優先級也是最高的
const expirationTime = computeExpirationForFiber(currentTime, current);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
callback,
);
}複製代碼
container.current
便是從FiberRoot中取出RootFiber
對象,currentTime
就是當前距離React應用初始化的時間。 **expirationTime
字面意思就是過時時間,後面我會專門花一章的時間來介紹這兩個時間,這兩個時間也是React應用任務調度的重點。
updateContainerAtExpirationTime函數實際調用的就是scheduleRootUpdate
函數,下面來講一下scheduleRootUpdate
函數的做用。
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// 建立一個 update,就是內部有幾個屬性的對象
const update = createUpdate(expirationTime);
update.payload = {element};
// render中的回調函數
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
flushPassiveEffects();
// 把 update 入隊,內部就是一些建立或者獲取 queue(鏈表結構),而後給鏈表添加一個節點的操做
enqueueUpdate(current, update);
scheduleWork(current, expirationTime);
return expirationTime;
}複製代碼
下面就是update對象其中的屬性:
// update對象屬性
export type Update<State> = {
// 更新的過時時間
expirationTime: ExpirationTime,
// export const UpdateState = 0;
// export const ReplaceState = 1;
// export const ForceUpdate = 2;
// export const CaptureUpdate = 3;
// 指定更新的類型,值爲以上幾種
tag: 0 | 1 | 2 | 3,
// 更新內容,好比`setState`接收的第一個參數
payload: any,
// 對應的回調,`setState`,`render`都有
callback: (() => mixed) | null,
// 指向下一個更新
next: Update<State> | null,
// 指向下一個`side effect`
nextEffect: Update<State> | null,
};複製代碼
udate對象會被插入到React應用維護的任務隊列中,無論你是setState仍是ReactDOM.render形成的 React應用 更新都是如此。這個函數核心做用就是建立或者獲取一個隊列,而後把 update 對象插入隊列進行更新。scheduleWork
函數就是任務調度的東西了。
更多內容:
react解析: React.createElement(一)
參考:
Jokcy 的 《React 源碼解析》: react.jokcy.me/
ps: 順便推一下本身的我的公衆號:Yopai,有興趣的能夠關注,每週不按期更新,分享能夠增長世界的快樂