In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article focuses on "Virtual DOM sample Analysis in React". Interested friends may wish to have a look at it. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "Virtual DOM sample Analysis in React".
This is a front-end page of Choerodon
In a complex front-end project, a page may contain hundreds of states, and a finer understanding of the React framework is important for front-end optimization. Once upon a time, clicking on a record to show the details on this page would stutter for a few seconds, but this was only caused by front-end rendering.
In order to solve these problems, developers need to understand the whole process of React components from definition to rendering (and then updating) on the page.
React uses a syntax (called JSX) that mixes HTML and JavaScript when writing components. However, browsers know nothing about JSX and its syntax, and browsers can only understand pure JavaScript, so you must convert JSX to HTML. This is a JSX code for div, which has a class and some content:
Text
After turning this jsx into a normal js in React, it is a function call with many parameters:
React.createElement ('div', {className:' cn'}, 'text'); React.createElement ('div', {className:' cn'}, ['Content 1 steps, React.createElement (' br'), 'Content 2']) Its first parameter is a string, which corresponds to the tag signature in html, and the second parameter is the object composed of all its attributes. Of course, it may also be an empty object, and the remaining parameters are all child elements under this element, and the text here will also be treated as a child element, so the third parameter is `"text "`. At this point you should be able to imagine what will happen when there are more `children` under this element. ```html text 1 text 2React.createElement ('div', {className:' cn'}, 'text 1', / / 1st child React.createElement ('br'), / / 2nd child' text 1'/ / 3rd child)
The current function has five parameters: the type of the element, the object of all attributes, and three child elements. Because a child is also a HTML tag known to React, it will also be interpreted as a function call.
So far, this article has introduced two types of child parameters, one is string plain text, and the other is to call other React.createElement functions. In fact, other values can also be used as parameters, such as:-basic type false,null,undefined and true-array-React component
Arrays are used because subcomponents can be grouped and passed as a parameter:
Of course, the powerful features of React come not from the tags described in the `HTML` specification, but from user-created components, such as:
````jsfunction Table ({rows}) {return ({rows.map (row = > ({row.title}))});}
Components allow developers to decompose templates into reusable blocks. In the "pure function" component example above, the component accepts an array of objects containing table row data and returns a single call by React.createElement to the table element and its rows as child elements.
Whenever a developer puts a component into a JSX layout, it looks like this:
But from a browser point of view, it sees something like this:
React.createElement (Table, {rows: rows})
Note that the first parameter this time is not a HTML element described in string, but a reference to the component (that is, the function name). The second parameter is the props object passed in to the component.
Put the component on the page
Now that the browser has converted all the JSX components to pure JavaScript, the browser has got a bunch of function calls with arguments to other function calls, as well as other function calls. How do you convert them into the DOM elements that make up a web page?
To do this, developers need to use the ReactDOM library and its render methods:
Function Table ({rows}) {/ *... * / component definition / / render a component ReactDOM.render (React.createElement (Table, {rows: rows}), / / "create" a component document.getElementById ('# root') / / put it into DOM)
When ReactDOM.render is called, React.createElement is eventually called, which returns the following objects:
/ / there are many other fields in this object, but these are important to the developer now. {type: Table, props: {rows: rows}, / /...}
These objects constitute Virtual DOM in the sense of React.
They will be compared to each other in all further renderings and eventually converted to a real DOM (compared to Virtual DOM).
This is another example: this time there is a div with the class attribute and several child nodes:
React.createElement ('div', {className:' cn'}, 'Content 1', 'Content 2',)
Become:
{type: 'div', props: {className:' cn', children: ['Content 1', 'Content 2']}}
All the passed expansion functions, that is, the parameters of React.createElement, except for the first and second arguments, will be in the children property of the props object. No matter what function is passed in, they will eventually be passed into props as children.
Moreover, the developer can add the children attribute directly to the JSX code and put the subitems directly in the children, and the result is still the same:
After the Virtual DOM object is created, ReactDOM.render will try to translate it into a DOM node that the browser can understand according to the following rules:-if the type attribute in the Virtual DOM object is a tag name of type string, create a tag that contains all the attributes in the props. -if the type property in the Virtual DOM object is a function or class, call it, it may return a Virtual DOM, and then continue to call this procedure recursively. -if there is a children attribute in props, do the above procedure for each element in children and put the returned result in the parent DOM node.
Finally, the browser gets the following HTML (for the example of table above):
Title... Rebuild DOM
Next, the browser wants to "rebuild" a DOM node, and if the browser wants to update a page, obviously, the developer does not want to replace all the elements in the page, which is the real magic of React. How can it be realized? Start with the simplest method and re-call the node's ReactDOM.render method.
/ / call ReactDOM.render for the second time (React.createElement (Table, {rows: rows}), document.getElementById ('# root'))
This time, the above code execution logic will be different from the code you see. Instead of creating all the DOM nodes from scratch and putting them on the page, React uses the "diff" algorithm to determine which parts of the node tree must be updated and which parts can remain the same.
So how does it work? There are only a few simple cases, and understanding them will be of great help to the optimization of React programs. Keep in mind that the objects you see next are objects that are used to represent nodes in React Virtual DOM.
The ▌ Case 1:type is a string, the type remains the same between calls, and the props remains unchanged.
/ / before update {type: 'div', props: {className:' cn'}} / / after update {type: 'div', props: {className:' cn'}}
This is the simplest case: the DOM remains the same.
▌ Case 2:type is still the same string, props is different.
/ / before update: {type: 'div', props: {className:' cn'}} / / after update: {type: 'div', props: {className:' cnn'}}
Because type still represents a HTML element, React knows how to change its attributes through standard DOM API calls without removing nodes from the DOM tree.
▌ Case 3:type has been changed to a different component String or from a String component to a component.
/ / before update: {type: 'div', props: {className:' cn'}} / / after update: {type: 'span', props: {className:' cn'}}
Because React now sees different types, it won't even try to update the DOM node: the old element will be unmount along with all its child nodes. Therefore, it can be very expensive to replace completely different elements on the DOM tree. Fortunately, this rarely happens in practice.
It is important to remember that React uses = (third class) to compare type values, so they must be the same instance of the same class or function.
The next scenario is more interesting because it is the way developers use React most often.
▌ Case 4:type is a component.
/ / before update: {type: Table, props: {rows: rows}} / / after update: {type: Table, props: {rows: rows}}
You might say, "this doesn't seem to have changed anything," but it's not right.
If type is a reference to a function or class (that is, a regular React component) and the tree diff comparison process is started, React will always try to view all child within the component to ensure that the return value of the render has not changed. Compare each component under the tree-yes, complex rendering can also become expensive!
Children in component
In addition to the four common scenarios described above, developers need to consider the behavior of React when an element has multiple child elements. Suppose there is such an element:
/ /... props: {children: [{type: 'div'}, {type:' span'}, {type: 'br'}]}, / /.
Developers want to re-render it like this (span and div switch places):
/ /... props: {children: [{type: 'span'}, {type:' div'}, {type: 'br'}]}, / /. So what happens?
When React sees any array type of props.children in it, it starts to compare the elements in it with the elements in the array seen before: index 0 will be compared with index 0 score index 1 and index 1, and for each pair of child elements, React will apply the above rule set to compare and update. In the above example, it sees that div becomes a span, which is the case in scenario 3. But there is a problem: suppose the developer wants to delete the first row from the 1000-row table. React must "update" the remaining 999 children, because their content will now be unequal compared to previous index-by-index representations.
Fortunately, React has a built-in way to solve this problem. If the element has a key attribute, the element is compared by key rather than by index. As long as the key is unique, React moves the elements without removing them from the DOM tree, and then puts them back (a process called mount / unmount in React).
/ /... props: {children: [/ / now react compares {type: 'div', key:' div'}, {type: 'span', key:' span'}, {type: 'br', key:' bt'}]}, / / based on key rather than index.
When the state changes
So far, this article only touches on a part of props,React philosophy, but ignores state. This is a simple stateful component:
Class App extends Component {state = {counter: 0} increment = () = > this.setState ({counter: this.state.counter + 1,}) render = () = > ({'Counter:' + this.state.counter})}
Now, the state object in the above example has a counter property. Clicking the button increases its value and changes the button text. But what happens to DOM when the user clicks? Which part of it will be recalculated and updated?
Calling this.setState also causes re-rendering, but not the entire page, but only the component itself and its children. Parents and siblings can be spared.
Fix the problem
This article prepares a DEMO, which is what it looked like before the problem was fixed. You can view the source code here. But before you do that, you need to install React Developer Tools.
The first thing to see when opening demo is which elements and when to cause Virtual DOM updates. Navigate to the React panel in the browser's Dev Tools, click Settings and select the "Highlight Updates" check box:
Now try to add a row to the table. As you can see, a border appears around each element on the page. This means that each time a row is added, React calculates and compares the entire Virtual DOM tree. Now try pressing the counter button within a line. You will see how Virtual DOM is updated (state updates only related elements and their child elements).
React DevTools hinted at where the problem might occur, but did not tell the developer any details: in particular, whether the problematic update refers to a difference after the element "diff" or whether the component has been unmount/mount. To learn more, developers need to use React's built-in parser (note that it doesn't work in production mode).
Go to the "Performance" tag in Chrome DevTools. Click the record button, and then click the table. Add some lines, change some counters, and click the "Stop" button. After a while, the developer will see:
In the result output, developers need to pay attention to "Timing". Zoom the Timeline until you see the "React Tree Reconciliation" group and its children. These are the names of the components, with [update] or [mount] next to them. You can see that one TableRow is mount, and all the other TableRow is in update, which is not what the developer wants.
Most performance problems are caused by [update] or [mount]
A component (and everything under it) is remounted every time it is updated for some reason, and developers don't want it to happen (remount is slow), or perform costly redrawings on large branches, even if the component doesn't seem to have changed.
Repair mount/unmount
Now, when developers understand how React decided to update Virtual DOM and what's going on behind the scenes, they're finally ready to solve the problem! The first thing to fix performance problems is to solve mount/unmount.
If the developer represents multiple child elements of any element / component as an array internally, then the program can get a very significant speed increase.
Consider this:
In a virtual DOM, it is represented as:
/ /... props: {children: [{type: Message}, {type: Table}, {type: Footer}]} / /...
A simple Message component (a div with some text, such as a notice at the top of the pig tooth fish) and a long Table, say more than 1000 lines. They are all child of the div element, so they are placed under the props.children of the parent node, and they have no key. React does not even remind developers to allocate key through console warnings, because the child node React.createElement is passed to the parent node as a parameter list rather than an array.
Now that the user has turned off the top notification, Message is removed from the tree. Table and Footer are the remaining child.
/ /... props: {children: [{type: Table}, {type: Footer}]} / /...
What does React think of it? It sees it as a series of type that changed the child:children [0] of type, which was originally Message, but now it is Table. Because they are references to functions (and different functions), it uninstalls the entire Table and installs it again, rendering all its descendants: more than 1000 lines!
So you can add a unique key (but key is not the best choice in this particular case) or use a smarter trick: Boolean short-circuiting using & &, a feature of JavaScript and many other modern languages. Like this:
{isShowMessage & &}
Even if Message is turned off (no longer displayed), the props.children parent div will still have three elements, and children [0] will have a value of false (Boolean type). Remember that true/false,null and even undefined are allowed values for the type property of a Virtual DOM object? Browsers end up with something like this:
/ /... props: {children: [false, / / isShowMessage & & false {type: Table}, {type: Footer}]} / /.
So, the index does not change whether Message is displayed or not, and Table is still compared to Table, but just comparing Virtual DOM is usually much faster than deleting DOM nodes and creating them from them.
Now let's look at something more advanced. Developers love HOC. A high-order component is a function that takes a component as a parameter, adds some behavior, and returns a different component (function):
Function withName (SomeComponent) {return function (props) {return;}}
The developer creates a HOC in the parent render method. When React needs to re-render the tree, the Virtual DOM for React will look like this:
/ / On first render: {type: ComponentWithName, props: {},} / / On second render: {type: ComponentWithName, / / Same name, but different instance props: {},}
Now, React will only run one diff algorithm on ComponentWithName, but this time a different instance is referenced by the same name. Three equals a failure and must be completely remounted. Note that it can also cause state loss, but fortunately, it is easy to fix: as long as the returned instances are all the same:
/ / singleton const ComponentWithName = withName (Component); class App extends React.Component () {render () {return;}} repair update
Now the browser has ensured that it will not reload anything unless necessary. However, any change to a component located near the root of the DOM tree will result in a comparative redrawing of all its children. The structure is complex, expensive and often avoidable.
It would be nice if there was a way to tell React not to look at a branch because it hasn't changed at all.
This approach exists and involves a component lifecycle function called shouldComponentUpdate. React calls this method before each call to the component and receives the new values for props and state. The developer is then free to compare the differences between the new and old values and decide whether the component should be updated (return true or false). If the function returns false,React, the problematic component will not be re-rendered and its child components will not be viewed.
A simple shallow comparison of two sets of props and state is usually sufficient: if the values of the top-level properties are the same, the browser does not have to be updated. Shallow comparison is not a feature of JavaScript, but there are many ways for developers to implement it on their own, in order not to repeat the wheels, they can also use methods written by others.
After introducing the shallow comparison npm package, developers can write the following code:
Class TableRow extends React.Component {shouldComponentUpdate (nextProps, nextState) {const {props, state} = this; return! shallowequal (props, nextProps) & &! shallowequal (state, nextState);} render () {/ *. * /}}
But you don't even have to write your own code, because React has built this feature into a class called React.PureComponent, which is similar to React.Component, except that shouldComponentUpdate has implemented a shallow props/state comparison for you.
Maybe you have the idea that you can replace Component with PureComponent. However, developers who mistakenly use PureComponent will also have the problem of re-rendering. You need to consider the following three situations:
{/ *. * /}} / >
The above code snippet demonstrates the three most common anti-patterns, so try to avoid them!
Using PureComponent correctly, you can see here that all TableRow is "purified" and rendered.
However, if you can't wait to use all pure function components, this is wrong. Comparing the two sets of props and state is not free and is not even worth it for most basic components: it takes more time to run the shallowCompare algorithm than the diff algorithm.
You can use this rule of thumb: pure components are suitable for complex forms and tables, but they usually slow down simple elements such as buttons or icons.
At this point, I believe you have a deeper understanding of "Virtual DOM sample Analysis in React". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.