摘要:概述協調,調解本身不存在公共的。安裝的確切結果有時在源代碼中稱為取決于渲染器,可以是節點,字符串或表示原生視圖。關鍵的缺失部分是對更新的支持。為避免混淆,我們將和的實例叫做內部實例。但是,內部實例樹包含復合和主機內部實例。
本節是 stack reconciler程序的實現說明的集合。
本文有一定的技術含量,要對React公共API以及它如何分為核心,渲染器和協調(和解,reconciler)程序有很深的理解。如果你對React代碼庫不是很熟悉,請首先閱讀代碼庫概述。
它還假設你了解React組件的實例和元素之間的差異。
stack reconciler用于15版本和早期. 它的代碼在 src/renderers/shared/stack/reconciler.
視頻:從頭開始構建ReactPaul O’Shannessy談到了從頭開始構建react,這在很大程度上啟發了這個文檔。
本文檔和他的演講都是對實際代碼庫的簡化,因此你可以通過熟悉它們來獲得更好的理解。
概述reconciler(協調,調解)本身不存在公共的API。像React DOM和React Native這樣的渲染器使用它根據用戶編寫的React組件有效地更新用戶界面。
掛載(mounting)作為遞歸過程讓我們考慮第一次掛載組件:
ReactDOM.render(, rootEl);
React DOM會將
console.log(); // { type: App, props: {} }
調解器會檢查這個App是類還是函數(對于這個得實現可以查看如何知道是函數還是類這篇文章)。
如果App是一個函數,則調解器將調用App(props)來獲取渲染元素。
如果App是一個類,那么調解器會通過new App(props)去實例化App,調用componentWillMount生命周期方法,然后調用render方法來獲取渲染的元素。
無論哪種方式,調解器都將得知App“渲染到”的元素。
這個過程是遞歸的。App可能會渲染
可以將此過程想象為偽代碼:
function isClass(type) { // React.Component下面的類有這個標簽 return ( Boolean(type.prototype) && Boolean(type.prototype.isReactComponent) ); } // 這個函數接受一個React元素 (例如) // 并且返回一個已經掛載了樹的DOM或原生節點 function mount(element) { var type = element.type; var props = element.props; // 我們將確定渲染元素的類型 // 函數就直接調用 // 類就實例化后調用render(). var renderedElement; if (isClass(type)) { // 類組件 var publicInstance = new type(props); // 設置props publicInstance.props = props; // 必要時調用生命周期方法 if (publicInstance.componentWillMount) { publicInstance.componentWillMount(); } // 通過調用render()獲得渲染的元素 renderedElement = publicInstance.render(); } else { // 函數組件 renderedElement = type(props); } // 這個過程是遞歸的 因為一個組件可能返回的元素的類型是另外一個組件 return mount(renderedElement); // 注意:這個實現是不完整的,并且會無限的重復下去 // 它只處理 或等元素。 // 它還沒有處理像或這樣的元素。 } var rootEl = document.getElementById("root"); var node = mount( ); rootEl.appendChild(node);
注意: 這真的僅僅只是一個偽代碼,它與真實的實現并不相似。它還會導致堆棧溢出,因為我們還沒有討論何時停止遞歸。
讓我們回顧一下上面例子中的一些關鍵想法:
React的elements只是一個純對象,用來描述組件的類型(如:App)和他的props.
用戶定義的組件(如:App)可以是函數或者類,但是他們都會渲染這些元素。
“Mounting”是一個遞歸過程,它在給定頂級React元素(例如
如果我們沒有在屏幕上呈現某些內容,則此過程將毫無用處。
除了用戶定義的(“復合”)組件之外,React元素還可以表示特定于平臺的(“計算機”)組件。例如,Button可能會從其render方法返回。
如果element的type屬性是一個字符串,我們認為正在處理一個計算機元素:
console.log(); // { type: "div", props: {} }
沒有與計算機元素關聯的用戶定義代碼。
當協調程序(調解器)遇到這些計算機元素時,它會讓渲染器(renderer)負責mounting它。例如,React DOM將創建一個DOM節點。
如果計算機元素具有子節點,則協調器以與上述相同的算法遞歸地mounts它們。子節點是否是計算機元素(
由子組件生成的DOM節點將附加到父DOM節點,并且將遞歸地組裝完整的DOM結構。
注意: 調解器本身與DOM無關。mounting(安裝)的確切結果(有時在源代碼中稱為“mount image”)取決于渲染器,可以是DOM節點(React DOM),字符串(React DOM Server)或表示原生視圖(React Native)。
如果我們要擴展代碼來處理計算機元素,它將如下所示:
function isClass(type) { // 繼承自 React.Component 類有一個標簽 isReactComponent return ( Boolean(type.prototype) && Boolean(type.prototype.isReactComponent) ); } // 這個函數只處理復合的元素 // 比如像是, 這些,但不是這些 function mountComposite(element) { var type = element.type; var props = element.props; var renderedElement; if (isClass(type)) { // 組件是類的情況,就去實例化他 var publicInstance = new type(props); // 設置props publicInstance.props = props; // 必要的時候調用生命周期方法 if (publicInstance.componentWillMount) { publicInstance.componentWillMount(); } renderedElement = publicInstance.render(); } else if (typeof type === "function") { // 組件是個函數 renderedElement = type(props); } // 這是遞歸的 // 但當元素是宿主(例如)而不是復合(例如 )時,我們將最終完成遞歸: return mount(renderedElement); } // 這個函數僅僅處理計算機元素 // 例如它處理和這些,但不處理 function mountHost(element) { var type = element.type; var props = element.props; var children = props.children || []; if (!Array.isArray(children)) { children = [children]; } children = children.filter(Boolean); // 這段代碼不應該在協調器中。 // 不同的渲染器可能對節點進行不同的初始化。 // 例如,React Native將創建iOS或Android視圖。 var node = document.createElement(type); Object.keys(props).forEach(propName => { if (propName !== "children") { node.setAttribute(propName, props[propName]); } }); // 安裝子元素 children.forEach(childElement => { // 子元素可能是計算機元素(比如),也有可能是一個合成組件(比如) // 我們都會遞歸掛載安裝 var childNode = mount(childElement); // 下面這個也是一個特定于平臺的 // 它會根據不同的渲染器來處理,這里只是一個假設他是一個dom渲染器 node.appendChild(childNode); }); // 返回作為安裝結果的DOM節點 // 這也是遞歸的結束的地方 return node; } function mount(element) { var type = element.type; if (typeof type === "function") { // 用戶定義的組件(合成的組件) return mountComposite(element); } else if (typeof type === "string") { // 計算機組件(例如: ) return mountHost(element); } } var rootEl = document.getElementById("root"); var node = mount( ); rootEl.appendChild(node);
這是有效的,但仍遠未達到協調者的實際運行方式。關鍵的缺失部分是對更新的支持。
介紹內部實例react的關鍵特點是你可以重新渲染所有東西,它不會重新創建DOM或重置狀態。
ReactDOM.render(, rootEl); // 應該重用現有的DOM: ReactDOM.render( , rootEl);
但是,我們上面的實現只知道如何掛載初始樹。它無法對其執行更新,因為它不存儲所有必需的信息,例如所有publicInstances,或哪些DOM節點對應于哪些組件。
堆棧協調器代碼庫通過使mount函數成為一個類上面的方法來解決這個問題。但是這種方法存在一些缺點,我們在正在進行的協調重寫任務中正朝著相反的方向去發展(筆者:目前fiber已經出來了)。不過 這就是它現在的運作方式。
我們將創建兩個類:DOMComponent和CompositeComponent,而不是多帶帶的mountHost和mountComposite函數。
兩個類都有一個接受元素的構造函數,以及一個返回已安裝節點的mount()方法。我們將用實例化類的工廠替換頂級mount()函數:
function instantiateComponent(element) { var type = element.type; if (typeof type === "function") { // 用戶定義的組件 return new CompositeComponent(element); } else if (typeof type === "string") { // 特定于平臺的組件,如計算機組件() return new DOMComponent(element); } }
首先,讓我們考慮下CompositeComponent的實現:
class CompositeComponent { constructor(element) { this.currentElement = element; this.renderedComponent = null; this.publicInstance = null; } getPublicInstance() { // 對于復合的組件,暴露類的實例 return this.publicInstance; } mount() { var element = this.currentElement; var type = element.type; var props = element.props; var publicInstance; var renderedElement; if (isClass(type)) { // Component class publicInstance = new type(props); // Set the props publicInstance.props = props; // Call the lifecycle if necessary if (publicInstance.componentWillMount) { publicInstance.componentWillMount(); } renderedElement = publicInstance.render(); } else if (typeof type === "function") { // Component function publicInstance = null; renderedElement = type(props); } // Save the public instance this.publicInstance = publicInstance; // 根據元素實例化子內部實例 // 他將是DOMComponent,例如, // 或者是CompositeComponent,例如, var renderedComponent = instantiateComponent(renderedElement); this.renderedComponent = renderedComponent; // Mount the rendered output return renderedComponent.mount(); } }
這與我們之前的mountComposite()實現沒什么不同,但現在我們可以存儲一些信息,例如this.currentElement,this.renderedComponent和this.publicInstance,在更新期間使用。
請注意,CompositeComponent的實例與用戶提供的element.type的實例不同。CompositeComponent是我們的協調程序的實現細節,永遠不會向用戶公開。用戶定義的類是我們從element.type讀取的,CompositeComponent會創建這個類的實例。
為避免混淆,我們將CompositeComponent和DOMComponent的實例叫做“內部實例”。 它們存在,因此我們可以將一些長期存在的數據與它們相關聯。只有渲染器和調解器知道它們存在。
相反,我們將用戶定義類的實例稱為“公共實例(public instance)”。 公共實例是你在render()和組件其他的方法中看到的this.
至于mountHost()方法,重構成了在DOMComponent類上的mount()方法,看起來像這樣:
class DOMComponent { constructor(element) { this.currentElement = element; this.renderedChildren = []; this.node = null; } getPublicInstance() { // For DOM components, only expose the DOM node. return this.node; } mount() { var element = this.currentElement; var type = element.type; var props = element.props; var children = props.children || []; if (!Array.isArray(children)) { children = [children]; } // Create and save the node var node = document.createElement(type); this.node = node; // Set the attributes Object.keys(props).forEach(propName => { if (propName !== "children") { node.setAttribute(propName, props[propName]); } }); // 創建并保存包含的子元素 // 這些子元素,每個都可以是DOMComponent或CompositeComponent // 這些匹配是依賴于元素類型的返回值(string或function) var renderedChildren = children.map(instantiateComponent); this.renderedChildren = renderedChildren; // Collect DOM nodes they return on mount var childNodes = renderedChildren.map(child => child.mount()); childNodes.forEach(childNode => node.appendChild(childNode)); // DOM節點作為mount的節點返回 return node; } }
與上面的相比,mountHost()重構之后的主要區別是現在將this.node和this.renderedChildren與內部DOM組件實例相關聯。我們會用他來用于在后面做非破壞性的更新。
因此,每個內部實例(復合或主機)現在都指向其子級內部實例。為了幫助可視化,如果函數 在DOM中,你只能看到 復合內部實例需要存儲: 當前元素 公共實例,如果當前元素類型是個類 單個呈現的內部實例。它可以是DOMComponent或CompositeComponent。 計算機內部實例需要存儲: 當前元素 DOM節點 所有子級的內部實例,這些子級中的每一個都可以是DOMComponent或CompositeComponent。 如果你正在努力想象如何在更復雜的應用程序中構建內部實例樹,React DevTools可以給你一個近似的結果,因為它突顯灰色的計算機實例,以及帶紫色的復合實例: 為了完成這個重構,我們將引入一個將完整樹安裝到容器節點的函數,就像ReactDOM.render()一樣。他返回一個公共實例,也像ReactDOM.render(): 既然我們有內部實例來保存它們的子節點和DOM節點,那么我們就可以實現卸載。對于復合組件,卸載會調用生命周期方法并進行遞歸。 對于DOMComponent,卸載會告訴每個子節點進行卸載: 實際上,卸載DOM組件也會刪除事件偵聽器并清除一些緩存,但我們將跳過這些細節。 我們現在可以添加一個名為unmountTree(containerNode)的新頂級函數,它類似于ReactDOM.unmountComponentAtNode(): 為了讓他工作,我們需要從DOM節點讀取內部根實例。我們將修改mountTree()以將_internalInstance屬性添加到DOM根節點。我們還將讓mountTree()去銷毀任何現有樹,以便可以多次調用它: 現在,重復運行unmountTree()或運行mountTree(),刪除舊樹并在組件上運行componentWillUnmount()生命周期方法。 在上一節中,我們實現了卸載。但是,如果每個prop更改導致卸載并安裝整個樹,則React就會顯得不是很好用了。協調程序的目標是盡可能重用現有實例來保留DOM和狀態: 我們將使用另一種方法擴展我們的內部實例。除了mount()和unmount()之外,DOMComponent和CompositeComponent都將實現一個名為receive(nextElement)的新方法: 它的任務是盡一切可能使組件(及其任何子組件)與nextElement提供的描述保持同步。 這是經常被描述為“虛擬DOM區別”的部分,盡管真正發生的是我們遞歸地遍歷內部樹并讓每個內部實例接收更新。 當復合組件接收新元素時,我們運行componentWillUpdate()生命周期方法。 然后我們使用新的props重新渲染組件,并獲取下一個渲染元素: 接下來,我們可以查看渲染元素的type。如果自上次渲染后type未更改,則下面的組件也可以在之前的基礎上更新。 例如,如果第一次返回,第二次返回,我們可以告訴相應的內部實例receive()下一個元素: 但是,如果下一個渲染元素的類型與先前渲染的元素不同,我們無法更新內部實例。不可能變成。 相反,我們必須卸載現有的內部實例并掛載與呈現的元素類型相對應的新實例。例如,當先前呈現的組件呈現時,會發生這種情況: 總而言之,當復合組件接收到新元素時,它可以將更新委托給其呈現的內部實例,或者卸載它并在其位置安裝新的實例。 在另一個條件下,組件將重新安裝而不是接收元素,即元素的key已更改。我們不討論本文檔中的key處理,因為它為原本就很復雜的教程增加了更多的復雜性。 請注意,我們需要將一個名為getHostNode()的方法添加到內部實例協定中,以便可以在更新期間找到特定于平臺的節點并替換它。它的實現對于兩個類都很簡單: 計算機組件實現,例如DOMComponent, 以不同方式更新。當他們收到元素時,他們需要更新底層特定于平臺的視圖。在React DOM的情況下,這意味著更新DOM屬性: 然后,計算機組件需要更新他們的子組件。與復合組件不同,它們可能包含多個子組件。 在這個簡化的示例中,我們使用內部實例數組并對其進行迭代,根據接收的類型是否與之前的類型匹配來更新或替換內部實例。除了插入和刪除之外,真正的協調程序還會使用元素的鍵跟蹤移動,但我們將省略此邏輯。 我們在列表中收集子級的DOM操作,以便批量執行它們: 作為最后一步,我們執行DOM操作。同樣,真正的協調代碼更復雜,因為它也處理移動: 這就是更新計算機組件(DOMComponent) 現在CompositeComponent和DOMComponent都實現了receive(nextElement)方法,我們可以更改頂級mountTree()函數,以便在元素類型與上次相同時使用它: 現在以相同的類型調用mountTree()兩次,不會有破壞性的更新了: 這些是React內部工作原理的基礎知識。 與真實代碼庫相比,本文檔得到了簡化。我們沒有解決幾個重要方面: 組件可以呈現null,并且協調程序可以處理數組中的“空”并呈現輸出。 協調程序還從元素中讀取key,并使用它來確定哪個內部實例對應于數組中的哪個元素。實際React實現中的大部分復雜性與此相關。 除了復合和計算機內部實例類之外,還有“text”和“empty”組件的類。它們代表文本節點和通過呈現null獲得的“空槽”。 渲染器使用注入將計算機內部類傳遞給協調程序。例如,React DOM告訴協調程序使用ReactDOMComponent作為計算機內部實例實現。 更新子項列表的邏輯被提取到名為ReactMultiChild的mixin中,它由React DOM和React Native中的計算機內部實例類實現使用。 協調程序還在復合組件中實現對setState()的支持。事件處理程序內的多個更新將被批處理為單個更新。 協調器還負責將引用附加和分離到復合組件和計算機節點。 在DOM準備好之后調用的生命周期方法(例如componentDidMount()和componentDidUpdate())將被收集到“回調隊列”中并在單個批處理中執行。 React將有關當前更新的信息放入名為“transaction”的內部對象中。transaction對于跟蹤待處理生命周期方法的隊列、警告當前DOM的嵌套以及特定更新的“全局”其他任何內容都很有用。事務還確保React在更新后“清理所有內容”。例如,React DOM提供的事務類在任何更新后恢復輸入選擇。
ReactMount是本教程中的mountTree()和unmountTree()之類的代碼。他負責安裝和卸載頂層的組件。ReactNativeMount是React Native的模擬。
ReactDOMComponent等同于本教程中的DOMComponent。它實現了React DOM渲染器的計算機組件類。ReactNativeBaseComponent是對React Native的模擬。
ReactCompositeComponent是等同于本教程中的CompositeComponent。他處理用戶自定義的組件并維護狀態。
instantiateReactComponent用于選擇要為元素構造的內部實例類。它等同于本教程中的instantiateComponent()。
ReactReconciler里是mountComponent(),receiveComponent(), unmountComponent()方法。它調用內部實例上的底層實現,但也包括一些由所有內部實例實現共享的代碼。
ReactChildReconciler實現獨立于渲染器處理子級的插入,刪除和移動的操作隊列。 由于遺留原因,mount(),receive()和unmount()在React代碼庫中實際上稱為mountComponent(),receiveComponent()和unmountComponent(),但它們接收元素。 內部實例上的屬性以下劃線開頭,例如_currentElement。它們被認為是整個代碼庫中的只讀公共字段。 堆棧協調器(stack reconciler)具有固有的局限性,例如同步并且無法中斷工作或將其拆分為塊。新的 Fiber reconciler正在進行中(筆:當然,大家都知道,目前已經完成了),他們有完全不同的架構。在未來,我們打算用它替換堆棧協調程序,但目前它遠非功能校驗。 閱讀下一節,了解我們用于React開發的指導原則。 原譯文: react的實現記錄 文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。 轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101075.html 摘要:異步實戰狀態管理與組件通信組件通信其他狀態管理當需要改變應用的狀態或有需要更新時,你需要觸發一個把和載荷封裝成一個。的行為是同步的。所有的狀態變化必須通過通道。前端路由實現與源碼分析設計思想應用是一個狀態機,視圖與狀態是一一對應的。
React實戰與原理筆記
概念與工具集
jsx語法糖;cli;state管理;jest單元測試;
webpack-bundle-analyzer
Sto...[object CompositeComponent] {
currentElement:
function mountTree(element, containerNode) {
// 創建頂層的內部實例
var rootComponent = instantiateComponent(element);
// 掛載頂層的組件到容器
var node = rootComponent.mount();
containerNode.appendChild(node);
// 返回他提供的公共實例
var publicInstance = rootComponent.getPublicInstance();
return publicInstance;
}
var rootEl = document.getElementById("root");
mountTree(
卸載
class CompositeComponent {
// ...
unmount() {
// 必要的時候調用生命周期方法
var publicInstance = this.publicInstance;
if (publicInstance) {
if (publicInstance.componentWillUnmount) {
publicInstance.componentWillUnmount();
}
}
// Unmount the single rendered component
var renderedComponent = this.renderedComponent;
renderedComponent.unmount();
}
}
class DOMComponent {
// ...
unmount() {
// 卸載所有的子級
var renderedChildren = this.renderedChildren;
renderedChildren.forEach(child => child.unmount());
}
}
function unmountTree(containerNode) {
// 從DOM節點讀取內部實例
// (目前這個不會正常工作, 我們將需要改變mountTree()方法去存儲)
var node = containerNode.firstChild;
var rootComponent = node._internalInstance;
// 清除容器并且卸載樹
rootComponent.unmount();
containerNode.innerHTML = "";
}
function mountTree(element, containerNode) {
// 銷毀存在的樹
if (containerNode.firstChild) {
unmountTree(containerNode);
}
// 創建頂層的內部實例
var rootComponent = instantiateComponent(element);
// 掛載頂層的組件到容器
var node = rootComponent.mount();
containerNode.appendChild(node);
// 保存內部實例的引用
node._internalInstance = rootComponent;
// 返回他提供的公共實例
var publicInstance = rootComponent.getPublicInstance();
return publicInstance;
}
var rootEl = document.getElementById("root");
mountTree(
class CompositeComponent {
// ...
receive(nextElement) {
// ...
}
}
class DOMComponent {
// ...
receive(nextElement) {
// ...
}
}
class CompositeComponent {
// ...
receive(nextElement) {
var prevProps = this.currentElement.props;
var publicInstance = this.publicInstance;
var prevRenderedComponent = this.renderedComponent;
var prevRenderedElement = prevRenderedComponent.currentElement;
// 更新自有的元素
this.currentElement = nextElement;
var type = nextElement.type;
var nextProps = nextElement.props;
// 弄清楚下一個render()的輸出是什么
var nextRenderedElement;
if (isClass(type)) {
// 類組件
// 必要的時候調用生命周期
if (publicInstance.componentWillUpdate) {
publicInstance.componentWillUpdate(nextProps);
}
// 更新props
publicInstance.props = nextProps;
// Re-render
nextRenderedElement = publicInstance.render();
} else if (typeof type === "function") {
// 函數式組件
nextRenderedElement = type(nextProps);
}
// ...
// ...
// 如果渲染的元素類型沒有改變,
// 重用現有的組件實例
if (prevRenderedElement.type === nextRenderedElement.type) {
prevRenderedComponent.receive(nextRenderedElement);
return;
}
// ...
// ...
// 如果我們到達了這一點,那么我們就需要卸載之前掛載的組件
// 掛載新的一個,并且交換他們的節點
// 找到舊的節點,因為我們需要去替換他
var prevNode = prevRenderedComponent.getHostNode();
// 卸載舊的子級并且掛載新的子級
prevRenderedComponent.unmount();
var nextRenderedComponent = instantiateComponent(nextRenderedElement);
var nextNode = nextRenderedComponent.mount();
// 替換對子級的引用
this.renderedComponent = nextRenderedComponent;
// 新的節點替換舊的
// 記?。合旅娴拇a是特定于平臺的,理想情況下是在CompositeComponent之外的
prevNode.parentNode.replaceChild(nextNode, prevNode);
}
}
class CompositeComponent {
// ...
getHostNode() {
// 請求渲染的組件提供他
// 這將向下遞歸復合組件
return this.renderedComponent.getHostNode();
}
}
class DOMComponent {
// ...
getHostNode() {
return this.node;
}
}
更換計算機組件
class DOMComponent {
// ...
receive(nextElement) {
var node = this.node;
var prevElement = this.currentElement;
var prevProps = prevElement.props;
var nextProps = nextElement.props;
this.currentElement = nextElement;
// 移除舊的屬性
Object.keys(prevProps).forEach(propName => {
if (propName !== "children" && !nextProps.hasOwnProperty(propName)) {
node.removeAttribute(propName);
}
});
// 設置接下來的屬性
Object.keys(nextProps).forEach(propName => {
if (propName !== "children") {
node.setAttribute(propName, nextProps[propName]);
}
});
// ...
// ...
// 這個是React elements數組
var prevChildren = prevProps.children || [];
if (!Array.isArray(prevChildren)) {
prevChildren = [prevChildren];
}
var nextChildren = nextProps.children || [];
if (!Array.isArray(nextChildren)) {
nextChildren = [nextChildren];
}
// 這是內部實例的數組:
var prevRenderedChildren = this.renderedChildren;
var nextRenderedChildren = [];
// 當我們迭代子級的時候,我們將會添加操作到數組
var operationQueue = [];
//注意:下面的部分非常簡單!
//它的存在只是為了說明整個流程,而不是細節。
for (var i = 0; i < nextChildren.length; i++) {
// 嘗試獲取此子級的現有內部實例
var prevChild = prevRenderedChildren[i];
// 如果這個索引下不存在內部實例,那就把子級被追加到后面。
// 創建一個新的內部實例,掛載他并使用他的節點
if (!prevChild) {
var nextChild = instantiateComponent(nextChildren[i]);
var node = nextChild.mount();
// 記錄我們需要追加的節點
operationQueue.push({type: "ADD", node});
nextRenderedChildren.push(nextChild);
continue;
}
// 我們可以只更新元素類型匹配的實例(下面是元素類型相同)
// 例如 可以被更新成
// 但是不可以更新成
// ...
// Process the operation queue.
while (operationQueue.length > 0) {
var operation = operationQueue.shift();
switch (operation.type) {
case "ADD":
this.node.appendChild(operation.node);
break;
case "REPLACE":
this.node.replaceChild(operation.nextNode, operation.prevNode);
break;
case "REMOVE":
this.node.removeChild(operation.node);
break;
}
}
}
}
function mountTree(element, containerNode) {
// 檢查存在的樹
if (containerNode.firstChild) {
var prevNode = containerNode.firstChild;
var prevRootComponent = prevNode._internalInstance;
var prevElement = prevRootComponent.currentElement;
// 如果我們可以,復用存在的根組件
if (prevElement.type === element.type) {
prevRootComponent.receive(element);
return;
}
// 其他的情況卸載存在的樹
unmountTree(containerNode);
}
// ...
}
var rootEl = document.getElementById("root");
mountTree(
原文: Implementation Notes
相關文章
FE.SRC-React實戰與原理筆記
發表評論
0條評論
閱讀 2947·2023-04-25 19:20
閱讀 794·2021-11-24 09:38
閱讀 2052·2021-09-26 09:55
閱讀 2439·2021-09-02 15:11
閱讀 2053·2019-08-30 15:55
閱讀 3615·2019-08-30 15:54
閱讀 3154·2019-08-30 14:03
閱讀 2967·2019-08-29 17:11