This is the 116th original article without water. if you want to get more original articles, please follow us by searching our official account
preface
Backflow and redraw are one of the most frequently used terms in front-end development, as you can see in various articles on performance optimization, but many of them are lightly covered. This article takes you through the principles of backflow and redraw in the browser rendering process.
Browser Rendering process
The main function of the browser is to send a request to the server to download the parsed resource and display it on the browser. The process of presenting web content to the browser, which is actually done by the rendering engine. There are many different rendering engines, and webKit is used as an example.
WebKit rendering engine main flow
(Photo from Internet)
From the diagram above, we can see that the browser rendering process is as follows:
- Parse the HTML Source to generate a DOM tree.
- The CSS is parsed to generate the CSSOM tree.
- DOM Tree and CSSOM Tree are combined to remove invisible elements and generate Render Tree.
- Layout: According to the generated rendering tree, carry out the Layout, get the geometric information of the node (width, height, position, etc.).
- Painting: Render each pixel of the Render Tree onto the screen based on the Render Tree and the geometry information obtained by backflow.
Render tree
(Photo from Internet)
- Process for building a render tree:
- Each visible node is traversed starting at the root of the DOM tree.
- For each visible node, find the corresponding rules in the CSSOM tree and apply them.
- The render tree is composed from each visible node and its corresponding style.
- What is an invisible node
- Nodes that do not render output, such as script, meta, link, etc.
- Some nodes are hidden by CSS. Such as display: None. Note that nodes hidden using opacity and opacity will still be displayed in the rendering tree (since they occupy document space), and only nodes with display: None will not be displayed in the rendering tree.
Principle of reflux and redraw
Webkit turns elements in the render tree into render objects, and each render object represents a rectangular area, usually corresponding to a CSS box for the node in question, containing geometric information such as width, height, and position. The box type is influenced by the node-specific “display” style property, which creates different render objects based on different display types
- RenderInline
- RenderBlock
- RenderListItem
The WebKits RenderObject class is the base class for all rendered objects. It is defined as follows:
class RenderObject{
virtual void layout();
virtual void paint(PaintInfo);
virtual void rect repaintRect();
Node* node; // the DOM node
RenderStyle* style; // the computed style
RenderLayer* containgLayer; // the containing z-index layer
}
Copy the code
Each render object has layout and paint methods that correspond to backflow and redraw methods, respectively. Layout is a recursive process. The root render object starts with an HTML element and recursively traverses some or all of the tree structure, each calling the Layout or paint methods of the children that need to be laid out.
What is reflux
When a render object is created and added to the render tree, it simply combines the DOM node with its corresponding style and contains no location or size information. Therefore, the layout process is also needed to calculate their position and size, which is called backflow.
Global layout and incremental layout
A global layout is a layout that triggers the entire render tree range, usually synchronously.
- Global style changes that affect all rendered objects, such as font size changes.
- Screen size adjustment.
Incremental layout: This is the layout of rendered objects marked “dirty”. It is typically executed asynchronously, with the browser enqueuing incremental layout “reflow commands” and the scheduler triggering the batch execution of these commands. But scripts that request style information, such as offsetHeight, trigger incremental layouts synchronously.
To avoid the overall layout of all small changes, browsers use a system of dirty bits. If a render object changes or marks itself and its children as “dirty”, a layout is required.
There are two types of markers: “dirty” and “children are dirty”. “Children are dirty” means that although the render object itself has not changed, at least one of its children needs to be laid out.
Triggering conditions:
The backflow phase computs the location and geometry of nodes, which is required when the page layout and geometry change
- The geometry of a DOM element changes. Common geometry attributes include width, height, padding, margin, left, top, border, and so on.
- Add, subtract, or move DOM nodes.
- When reading and writing offset family, Scroll family and client family attributes, the browser needs to perform backflow operations to obtain these values.
- Call the window.getComputedStyle method.
What is redraw
By constructing the render tree and the backflow phase, knowing which nodes are visible, as well as the style of the visible nodes and specific geometric information (location, size), we can convert each node of the render tree to an actual pixel on the screen, a process called redrawing.
In the redraw phase, the render tree is traversed and the paint method of the render object is called to display the contents of the render object on the screen. Like layout, drawing can be global (drawing the entire rendering tree) or incremental.
Drawing order
The order in which the elements are drawn is the order in which they enter the stack style context. These stacks are drawn from back to front, so this order affects the drawing. The stack order of the block rendering objects is as follows:
- The background color
- The background image
- A border
- Their offspring
- outline
Triggering conditions:
Redraw is a browser behavior triggered by changes in the appearance of an element, such as changing the visibility, outline, background-color, etc. These properties only affect the appearance and style of the element, and do not affect the geometry of the element.
Debugging with the Performance tool
The following is a small example, with the Performance tool debugging more intuitive
const box = document.querySelector('#box')
const btn = document.querySelector('#btn')
btn.addEventListener('click'.() = > {
box.style.width = '200px'
})
Copy the code
JS/CSS > Style > Layout > Draw > Composition
If we just change the background color
box.style.background = '#fof'
Copy the code
By changing the background color as seen in the image above, the render process skips Layout and moves on to drawing and the rest of the process.
Pixel pipe
(Photo from Internet)
This is a classic flow chart, a rendering pipeline of individual frames that the browser runs, called a pixel pipeline
- JavaScript. In general, we will use JavaScript to achieve some visual changes. Like jQuery
animate
Function to animate, sort a data set, or add some DOM elements to a page. Of course, in addition to JavaScript, there are other common ways to achieve visual changes, such as CSS Animations, Transitions, and the Web Animation API. - Style calculation. This process is based on the match selector (e.g
.headline
或.nav > .nav__item
The process of figuring out which elements apply which CSS rules. Once you know the rules from this, you apply the rules and calculate the final style for each element. - layout. Once you know which rules to apply to an element, the browser can start calculating how much space it takes up and where it is on the screen. The layout pattern of a web page means that one element can affect other elements, for example
<body>
The width of an element generally affects the width of its child elements and nodes throughout the tree, so the layout process is a common occurrence for browsers. - To draw. Drawing is the process of filling pixels. It involves drawing text, colors, images, borders, and shadows, basically including every visible part of an element. Drawing is typically done on multiple surfaces (often called layers). Drawing is actually a two-step process: create a list of drawing calls and fill in the pixels.
- Synthesis. Because parts of a page may be drawn to multiple layers, they need to be drawn to the screen in the correct order to render the page correctly. This is especially important for elements that overlap with another element, because an error can cause one element to mistakenly appear on top of another.
Every step of the single-frame rendering pipeline can have an impact on performance, so we want to minimize the number of pipeline steps. Not every frame will always be processed by every part of the pipe. In fact, whether using JavaScript, CSS, or web animation, there are usually three ways that a pipe can run for a given frame when implementing visual changes:
JS/CSS > Style > Layout > Draw > Composition
If you change the layout property of an element, you trigger a backflow. For example, changing the width, height, and so on of an element, the browser triggers a relayout, a series of substages after parsing, called backflow. Backflow is also the most expensive because it requires updating the entire rendering pipeline.
JS/CSS > Styles > Draw > Composition
If you change properties such as the background image, text color, or shadow that do not affect the layout of the page, the browser skips the layout, but the subsequent drawing and subsequent flow is still performed.
JS/CSS > Styles > Composition
Some properties, such as transform and opacity, allow the rendering pipeline to skip layout and rendering and simply combine compositing layers.
Transform and opacity do not trigger paint unless an element is promoted to a composition layer, or if it is not, it will still trigger paint.
According to the order of the rendering pipeline, backflow must trigger repainting, and repainting does not necessarily backflow
If you want to know which of the three versions will be triggered by changing any of the specified CSS properties, look at CSS triggers. For a quick look at high performance animation, read the section changing only synthesizer properties.
How to reduce backflow and redraw
We have introduced the content of pixel pipeline above, and know that the cost of backflow and redrawing is very expensive. If we keep changing the layout of the page, it will cause the browser to spend a lot of overhead in the calculation of the page, which is very unfriendly to the user experience. Reducing backflow and redrawing is an important means of front-end performance optimization.
Reduce forced synchronization of layouts
Avoid frequently reading properties that cause backflow/redraw, and if you do need to use them more than once, cache them in a variable.
For example, when a property or method is clicked, the browser immediately clears the queue
Read and write offset family, Scroll family, and client family attributes, as well as getComputedStyle() and getBoundingClientRect() methods
Modern browsers will optimize the frequent backflow or redraw operation, the browser will maintain a queue, put all cause re-flow and re-paint operation in the queue, if the number of tasks in the queue or interval reached a threshold, the browser will queue to empty, a batch, so that you can make multiple re-flow and re-paint again.
Avoid frequent DOM manipulation
Create a documentFragment on which all DOM operations are performed and then mount it to its parent node.
let container = document.getElementById('container')
let content = document.createDocumentFragment()
for(let i=0; i<10; i++){let li = document.createElement("li")
li.innerHTML = 'li'+i
content.appendChild(li)
}
container.appendChild(content)
Copy the code
Enable GPU acceleration
Styles are similar to layers in Photoshop, where Layout and Paint do not affect each other. GPU acceleration enabled elements are now promoted to a separate layer.
<style>
#box{
width: 100px;
height: 100px;
background: #f00;
transition: 0.5s ease;
}
</style>
<body>
<div id="box"></div>
<button id="btn">Click on the</button>
</body>
</html>
<script>
const box = document.querySelector('#box')
const btn = document.querySelector('#btn')
btn.addEventListener('click'.() = > {
box.style.transform = 'translateX(200px)'
})
</script>
Copy the code
While some articles write that the transform and opacity properties do not cause backflow or redraw, the effect of the above example (which only captures the beginning of the animation) is to have a Paint at the beginning and end of the animation. Only **composite ** takes place during animation. So why is there a redrawing here? Animation or transition properties are required to be applied to transform and opacity during the process. If animation or transition has not started or has ended, raising the composition layer will also fail. So the composition layer created before the animation starts is redrawn, and the independent composition layer is removed after the animation ends, which causes the redraw.
You can see the composition layer in the Layers tool on the console:
In the browser of WebKit kernel, CSS3’s transform, opacity, filter and other properties can achieve the synthetic effect, and the browser will upgrade the rendering layer to the synthetic layer
How do YOU turn on hardware acceleration?
- The will-change property of the CSS developer.mozilla.org/zh-CN/docs/…
- If will-change is not supported, translateZ(0) can be used
Hardware acceleration is not a panacea, and there is a price to be paid for creating separate compositing layers. For each compositing layer you create, you allocate memory to it, depending on the number of compositing layers
The management of the size layer of the composite layer is also more complicated, which is more obvious in some low-end and terminal mobile devices. The abuse of GPU acceleration will cause page lag or even flash back.
Don’t abuse hardware acceleration
Implicit synthesis
There are two elements a and B that are absolutely positioned, and they partially overlap. A is lower than B and b is above it. If a is added with the translateZ(0) attribute or other attributes to make a be promoted to the composition layer, then b will be promoted to the composition layer in order to maintain the relationship of a being lower than B and b being above.
Here’s an example:
<style>
.box1 {
width: 100px;
height: 100px;
background-color: #999;
position: absolute;
left: 100px;
top: 100px;
transform: translateZ(0);
}
.box2 {
width: 100px;
height: 100px;
background-color: #Awesome!;
position: absolute;
z-index: 10;
left: 180px;
top: 180px;
}
</style>
<body>
<div class="box1">a</div>
<div class="box2">b</div>
</body>
</html>
Copy the code
In the figure above, A is promoted to the synthesis layer because of its low level. In order to maintain the original hierarchical relationship, B will also be promoted to the synthesis layer.
I did an extreme example where the console promoted all elements to the composition layer using the example of the Site of Hand Shopping
* {transform: translateZ(0)}Copy the code
Imagine if we put a lot of lower-level elements into a composite layer, which may cause a lot of meaningless promotion of the composite layer. Although the browser has a layer compression mechanism, there are also a lot of cases that cannot be compressed, too many composite layer explosion browser crash, stalling, etc.
If your existing projects are not running smoothly on some low-end or terminal mobile devices, you can check whether some of them are due to implicit composition, using debugging tools
Is there a lot of yellow blocks? If there is a large number of synthetic layers, it is certainly unreasonable. We can check the synthetic reasons.
Using requestAnimationFrame
Tell the browser window. RequestAnimationFrame () – you want to perform an animation, and required the browser until the next redraw calls the specified callback function to update the animation. This method takes as an argument a callback function that is executed before the browser’s next redraw
Use requestAnimationFrame instead of setTimeout or setInterval to perform visual changes such as animations to avoid easy frame loss and stutters.
Note: Do not call properties or methods in callback functions that trigger forced synchronous layouts
Using requestIdleCallback
Window. RequestIdleCallback () method will be called function in browser free time line. This enables developers to perform background and low-priority work on the main event loop without affecting the delay of critical events such as animations and input responses. Functions are typically executed in first-come-first-called order; however, if a callback function specifies a timeout, it is possible to scramble the order of execution in order to execute the function before it times out.
Build the Document Fragment in the callback of the requestIdleCallback, and then make the actual DOM changes in the requestAnimationFrame callback of the next frame.
other
There are many examples, here is a brief list:
- Apply the animation to an element with position as absolute or fixed, making the z-index level higher.
- Top left uses transform instead.
- Avoid using CSS expressions/such as calc.
- Use higher performance selectors, such as class selectors. Optionally, the BEM (Block, element, modifier) specification can also be used.
conclusion
This article describes the principle of backflow and redraw from the perspective of the browser rendering process. Through the performance debugging tool, you can more intuitively feel the five key paths of the pixel pipeline. At the end of the article, there are common examples of optimized backflow and redraw, hoping to help you.
Refer to the connection
How browsers work: Behind the scenes of the new Web browser
Rendering performance
Do you really understand reflux and redraw
Summary of browser rendering process &Composite
Recommended reading
E-commerce minimum inventory – SKU and algorithm implementation
What you need to know about project management
How to build code global retrieval system from 0 to 1
How to build a build deployment platform suitable for your team
Open source works
- Political cloud front-end tabloid
Open source address www.zoo.team/openweekly/ (wechat communication group on the official website of tabloid)
, recruiting
ZooTeam, a young passionate and creative front-end team, belongs to the PRODUCT R&D department of ZooTeam, based in picturesque Hangzhou. The team now has more than 50 front-end partners, with an average age of 27, and nearly 30% of them are full-stack engineers, no problem in the youth storm group. The members consist of “old” soldiers from Alibaba and netease, as well as fresh graduates from Zhejiang University, University of Science and Technology of China, Hangzhou Electric And other universities. In addition to daily business docking, the team also carried out technical exploration and practice in material system, engineering platform, building platform, performance experience, cloud application, data analysis and visualization, promoted and implemented a series of internal technical products, and continued to explore the new boundary of front-end technology system.
If you want to change what’s been bothering you, you want to start bothering you. If you want to change, you’ve been told you need more ideas, but you don’t have a solution. If you want change, you have the power to make it happen, but you don’t need it. If you want to change what you want to accomplish, you need a team to support you, but you don’t have the position to lead people. If you want to change the pace, it will be “5 years and 3 years of experience”; If you want to change the original savvy is good, but there is always a layer of fuzzy window… If you believe in the power of believing, believing that ordinary people can achieve extraordinary things, believing that you can meet a better version of yourself. If you want to be a part of the process of growing a front end team with deep business understanding, sound technology systems, technology value creation, and impact spillover as your business takes off, I think we should talk. Any time, waiting for you to write something and send it to [email protected]