react 中渲染html
by Nayeem Reza
通過Nayeem Reza
如何在React中識別和解決浪費的渲染 (How to identify and resolve wasted renders in React)
So, recently I was thinking about performance profiling of a react app that I was working on, and suddenly thought to set a few performance metrics. And I did come across that the first thing I need to tackle is wasted renders I’m doing in each of the webpages. You might be thinking about what are wasted renders by the way? Let’s dive down in.
因此,最近,我正在考慮對正在使用的React應用進行性能分析,并突然想到設置一些性能指標。 我確實發現,我需要解決的第一件事是在每個網頁中執行的渲染浪費 。 您可能正在考慮哪些浪費的渲染? 讓我們深入。
From the beginning, React has changed the whole philosophy of building web apps and subsequently the way front-end developers think. With its introduction of Virtual DOM, React makes UI updates as efficient as they can ever be. This makes the web app experience neat. Have you ever wondered how to make your React applications faster? Why do moderately sized React web apps still tend to perform poorly? The problems lie in how we are actually using React!
從一開始,React就改變了構建Web應用程序的整個哲學,并隨后改變了前端開發人員的思維方式。 通過引入Virtual DOM ,React使UI更新盡可能高效。 這使Web應用程序體驗更簡潔。 您是否曾經想過如何使您的React應用程序更快? 為什么中等大小的React Web應用仍然表現不佳? 問題在于我們如何實際使用React!
React如何工作 (How React works)
A modern front-end library like React doesn’t make our app faster wondrously. First, we developers should understand how React works. How do the components live through the component lifecycles in the applications lifetime? So, before we dive into any optimization technique, we need to have a better understanding of how React actually works under the hood.
像React這樣的現代前端庫并不能使我們的應用程序更快地運轉。 首先,我們的開發人員應該了解React的工作方式。 組件在應用程序生命周期中如何在組件生命周期中生存? 因此,在我們深入研究任何優化技術之前,我們需要更好地了解React在幕后的實際工作方式。
At the core of React, we have the JSX syntax and React’s powerful ability to build and compare virtual DOMs. Since its release, React has influenced many other front-end libraries. For example, Vue.js also relies on the idea of virtual DOMs.
作為React的核心,我們擁有JSX語法和React強大的能力來構建和比較虛擬DOM 。 自發布以來,React已經影響了許多其他前端庫。 例如,Vue.js也依賴于虛擬DOM的想法。
Each React application begins with a root component. We can think of the whole application as a tree formation where every node is a component. Components in React are ‘functions’ that render the UI based on the data. That means props and state it receives; say that is CF
每個React應用程序都以一個根組件開始。 我們可以將整個應用程序視為樹狀結構,其中每個節點都是一個組件。 React中的組件是“功能”,它們基于數據呈現UI。 這意味著道具和它收到的狀態 。 說那是CF
UI = CF(data)
Users interact with the UI and cause the change in data. The interactions are anything a user can do in our application. For example, clicking a button, sliding images, dragging list items around, and AJAX requests invoking APIs. All those interactions only change the data. They never cause any change in the UI.
用戶與UI交互并導致數據更改。 用戶可以在我們的應用程序中進行任何交互。 例如,單擊按鈕,滑動圖像,拖動列表項,以及AJAX請求調用API。 所有這些交互只會更改數據。 它們永遠不會導致UI發生任何變化。
Here, data is everything that defines the state of an application. Not just what we have stored in our database. Even different front-end states like which tab is currently selected or whether a checkbox is currently checked or not are part of this data. Whenever there is a change in data, React uses the component functions to re-render the UI, but only virtually:
在這里,數據就是定義應用程序狀態的所有內容。 不只是我們存儲在數據庫中的內容。 甚至不同的前端狀態(例如當前選中哪個選項卡或當前是否選中復選框)也屬于此數據。 每當數據發生變化時,React都會使用組件函數來重新渲染UI,但實際上只是:
UI1 = CF(data1)UI2 = CF(data2)
React computes the differences between the current UI and the new UI by applying a comparison algorithm on the two versions of its virtual DOM.
React通過在其虛擬DOM的兩個版本上應用比較算法來計算當前UI和新UI之間的差異。
Changes = Difference(UI1, UI2)
React then proceeds to apply only the UI changes to the real UI on the browser. When the data associated with a component changes, React determines if an actual DOM update is required. This allows React to avoid potentially expensive DOM manipulation operations in the browser. Examples such as creating DOM nodes and accessing existing ones beyond necessity.
然后,React繼續將僅UI更改應用于瀏覽器上的實際UI。 當與組件關聯的數據更改時,React會確定是否需要實際的DOM更新。 這使得React可以避免瀏覽器中潛在的昂貴DOM操作操作。 諸如創建DOM節點和訪問現有節點等不必要的示例。
This repeated differentiating and rendering of components can be one of the primary sources of React performance issues in any React app. Building a React app where the differentiating algorithm fails to reconcile effectively, causing the entire app to be rendered repeatedly which is actually causing wasted renders and that can result in a frustratingly slow experience.
組件的這種重復區分和渲染可能是任何React應用程序中React性能問題的主要來源之一。 在差異化算法無法有效協調的情況下構建React應用程序,導致整個應用程序被重復渲染,這實際上導致浪費的渲染,并可能導致令人沮喪的緩慢體驗。
During the initial render process, React builds a DOM tree like this —
在初始渲染過程中,React會像這樣構建DOM樹-
Suppose that a part of the data changes. What we want React to do is re-render only the components that are directly affected by that specific change. Possibly skip even the differentiating process for the rest of the components. Let’s say some data changes in Component 2
in the above picture, and that data has been passed from R
to B
and then 2
. If R re-renders then it’ll re-render each of its children that means A, B, C, D and by this process what actually React does is this:
假設一部分數據發生了變化。 我們希望React要做的是僅重新渲染受該特定更改直接影響的組件。 甚至可以跳過其余組件的區分過程。 假設上圖中的組件2
中有一些數據更改,并且該數據已從R
傳遞到B
,然后傳遞到2
。 如果R重新渲染,那么它將重新渲染它的每個子元素,這意味著A,B,C,D,并且通過此過程,React實際執行的操作是:
In the image above, all the yellow nodes are rendered and differentiated. This results in wasted time/computation resources. This is where we will primarily put our optimization efforts. Configuring each component to only render and differentiate when it is necessary. This will allow us to reclaim those wasted CPU cycles. First, we’ll take a look into the way that we can identify wasted renders of our application.
在上圖中,所有黃色節點都被渲染和區分。 這導致浪費時間/計算資源。 這是我們主要進行優化工作的地方。 配置每個組件以僅在必要時進行渲染和區分。 這將使我們能夠回收那些浪費的CPU周期。 首先,我們將研究確定應用程序浪費的渲染的方式。
識別浪費的渲染 (Identify wasted renders)
There are a few different ways to do this. The simplest method is to toggle on the highlight updates option in the React dev tools preference.
有幾種不同的方法可以做到這一點。 最簡單的方法是在React開發工具首選項中打開突出顯示更新選項。
While interacting with the app, updates are highlighted on the screen with colored borders. By this process, you should see components that have re-rendered. This lets us spot re-renders that were not necessary.
與應用程序交互時,更新會在屏幕上以彩色邊框突出顯示。 通過此過程,您應該看到已重新渲染的組件。 這使我們可以發現不必要的重新渲染。
Let’s follow this example.
讓我們跟隨這個例子。
Note that when we’re entering a second todo, the first ‘todo’ also flashes on the screen on every keystroke. This means it is being re-rendered by React together with the input. This is what we are calling a “wasted” render. We know it is unnecessary because the first todo content has not changed, but React doesn’t know this.
請注意,當我們輸入第二個待辦事項時,每次按鍵時,第一個“待辦事項”也會在屏幕上閃爍。 這意味著它將由React與輸入一起重新渲染。 這就是我們所謂的“浪費”渲染。 我們知道這是沒有必要的,因為第一個待辦事項內容沒有更改,但是React不知道這一點。
Even though React only updates the changed DOM nodes, re-rendering still takes some time. In many cases, it’s not a problem, but if the slowdown is noticeable, we should consider a few things to stop those redundant renders.
即使React僅更新已更改的DOM節點,但重新渲染仍需要一些時間。 在很多情況下,這不是問題,但是如果速度下降明顯,我們應該考慮一些事情來停止那些多余的渲染。
使用shouldComponentUpdate方法 (Using shouldComponentUpdate method)
By default, React will render the virtual DOM and compare the difference for every component in the tree for any change in its props or state. But that is obviously not reasonable. As our app grows, attempting to re-render and compare the entire virtual DOM at every action will eventually slow the whole thing down.
默認情況下,React將渲染虛擬DOM并比較樹中每個組件的差異,以了解其屬性或狀態的任何變化。 但這顯然是不合理的。 隨著我們的應用程序的增長,在每次操作時嘗試重新渲染和比較整個虛擬DOM最終都會使整個過程變慢。
React provides a simple lifecycle method to indicate if a component needs re-rendering and that is, shouldComponentUpdate
which is triggered before the re-rendering process starts. The default implementation of this function returns true
.
React提供了一個簡單的生命周期方法來指示組件是否需要重新渲染,即應該在重新渲染過程開始之前觸發的shouldComponentUpdate
。 該函數的默認實現返回true
。
When this function returns true for any component, it allows the render differentiating process to be triggered. This gives us the power of controlling the render differentiating process. Suppose we need to prevent a component from being re-rendered, we need simply to return false
from that function. As we can see from the implementation of the method, we can compare the current and next props and state to determine whether a re-render is necessary:
當此函數對任何組件返回true時,它將允許渲染區分過程被觸發。 這使我們能夠控制渲染差異化過程。 假設我們需要防止組件被重新渲染,我們只需要從該函數返回false
即可。 從該方法的實現中可以看出,我們可以比較當前和下一個道具和狀態,以確定是否需要重新渲染:
使用純組件 (Using pure components)
As you work on React you definitely know about React.Component
but what’s the deal with React.PureComponent
? We’ve already discussed shouldComponentUpdate lifecycle method, in pure components, there is already a default implementation of, shouldComponentUpdate()
with a shallow prop and state comparison. So, a pure component is a component that only re-renders if props/state
is different from the previous props and state.
在工作過程中上React你肯定知道React.Component
但什么是該交易的React.PureComponent
? 我們已經討論了shouldComponentUpdate生命周期方法,在純組件中,已經有默認的shouldComponentUpdate()
實現,帶有淺層shouldComponentUpdate()
和狀態比較。 因此,純組件是僅在props/state
與先前的props和state不同的情況下才重新渲染的組件。
In shallow comparison, primitive data-types like string, boolean, number are being compared by value and complex data-types like array, object, function are compared by reference
在淺層比較中,原始數據類型(例如字符串,布爾值,數字)通過值進行比較,而復雜數據類型(例如數組,對象,函數)通過??引用進行比較
But, what if we have a functional stateless component in which we need to implement that comparison method before each re-rendering happens? React has a Higher Order Component React.memo
. It is like React.PureComponent
but for functional components instead of classes.
但是,如果我們有一個功能性的無狀態組件,該組件需要在每次重新渲染發生之前實現該比較方法,該怎么辦? React有一個更高階的組件React.memo
。 它類似于React.PureComponent
但用于功能組件而不是類。
By default, it does the same as shouldComponentUpdate() which only shallowly compares the props object. But, if we want to have control over that comparison? We can also provide a custom comparison function as the second argument.
默認情況下,它的作用與shouldComponentUpdate()相同,后者僅淺層比較props對象。 但是,如果我們想控制該比較? 我們還可以提供自定義比較功能作為第二個參數。
使數據不變 (Making Data Immutable)
What if we could use a React.PureComponent
but still have an efficient way of telling when any complex props or states like an array, object, etc. have changed automatically? This is where the immutable data structure makes life easier.
如果我們可以使用React.PureComponent
但仍然有一種有效的方式來告知任何復雜的道具或狀態(例如數組,對象等)何時自動更改怎么辦? 這是不可變數據結構使生活更輕松的地方。
The idea behind using immutable data structures is simple. As we’ve discussed earlier, for complex data-types the comparison performs over their reference. Whenever an object containing complex data changes, instead of making the changes in that object, we can create a copy of that object with the changes which will create a new reference.
使用不可變數據結構的想法很簡單。 如前所述,對于復雜的數據類型,比較將在其引用上進行。 每當包含復雜數據的對象發生更改時,我們無需在該對象中進行更改,而可以使用更改創建該對象的副本,這將創建一個新引用。
ES6 has object spreading operator to make this happen.
ES6具有對象散布算子以實現此目的。
We can do the same for arrays too:
我們也可以對數組做同樣的事情:
避免為相同的舊數據傳遞新的引用 (Avoid passing a new reference for the same old data)
We know that whenever the props
for a component changes, a re-render happens. But sometimes the props
didn’t change. We write code in a way that React thinks it did change, and that’ll also cause a re-render but this time it’s a wasted render. So, basically, we need to make sure that we’re passing a different reference as props for different data. Also, we need to avoid passing a new reference for the same data. Now, we’ll look into some cases where we’re creating this problem. Let’s look at this code.
我們知道,每當props
的成分變化,重新繪制發生。 但是有時候props
沒有變。 我們以React認為確實有所改變的方式編寫代碼,這也會導致重新渲染,但是這次是浪費的渲染。 因此,基本上,我們需要確保傳遞不同的參考作為支持不同數據的道具。 同樣,我們需要避免傳遞相同數據的新引用。 現在,我們將研究造成這種問題的某些情況。 讓我們看一下這段代碼。
Here’s the content for the BookInfo
component where we’re rendering two components, BookDescription
and BookReview
. This is the correct code, and it works fine but there is a problem. BookDescription
will re-render whenever we get new reviews data as props. Why? As soon as the BookInfo
component receives new props, the render
function is called to create its element tree. The render function creates a new book
constant that means a new reference is created. So, BookDescription
will get this book
as a news reference, that’ll cause the re-render of BookDescription
. So, we can refactor this piece of code to this:
這是BookInfo
組件的內容,我們在其中渲染兩個組件BookDescription
和BookReview
。 這是正確的代碼,可以正常工作,但是存在問題。 每當我們獲得新的評論數據作為道具時, BookDescription
將重新呈現。 為什么? 一旦BookInfo
組件收到新的道具,就會調用render
函數來創建其元素樹。 render函數創建一個新的book
常量,這意味著將創建一個新的引用。 因此, BookDescription
將把這book
作為新聞參考,這將導致BookDescription
的重新呈現。 因此,我們可以將這段代碼重構為:
Now, the reference is always the same, this.book
and a new object isn’t created at render time. This re-rendering philosophy applies to every prop
including event handlers, like:
現在,引用始終是相同的, this.book
和渲染時不會創建新對象。 這種重新渲染的理念適用于包括事件處理程序在內的每個prop
,例如:
Here, we’ve used two different ways (binding methods and using arrow function in render) to invoke the event-handler methods but both will create a new function every time the component re-renders. So, to fix these issues, we can bind the method in the constructor
and using class properties which is still in experimental and not standardized yet but so many devs are already using this method of passing functions to other components in production ready applications:
在這里,我們使用了兩種不同的方式(綁定方法和在render中使用arrow函數)來調用事件處理程序方法,但是每次組件重新渲染時,這兩種方法都會創建一個新函數。 因此,要解決這些問題,我們可以將方法綁定到constructor
并使用仍在實驗中且尚未標準化的類屬性,但是許多開發人員已經在使用此方法將功能傳遞給生產就緒應用程序中的其他組件:
結語 (Wrapping up)
Internally, React uses several clever techniques to minimize the number of costly DOM operations required to update the UI. For many applications, using React will lead to a fast user interface without doing much work to optimize for performance specifically. Nevertheless, if we can follow the techniques I’ve mentioned above to resolve wasted renders then for large applications we’ll also get a very smooth experience in terms of performance.
在內部,React使用幾種巧妙的技術來最小化更新UI所需的昂貴的DOM操作的數量。 對于許多應用程序而言,使用React將導致快速的用戶界面,而無需進行大量工作來專門針對性能進行優化。 但是,如果我們可以遵循上面提到的技術來解決浪費的渲染,那么對于大型應用程序,我們還將在性能方面獲得非常流暢的體驗。
翻譯自: https://www.freecodecamp.org/news/how-to-identify-and-resolve-wasted-renders-in-react-cc4b1e910d10/
react 中渲染html