Author of this article: Bermudarat
foreword
In more and more businesses, in addition to displaying data and providing UI for user operations, front-end pages also need to bring users a richer interactive experience. As a carrier, animation has become a must for daily front-end development, especially C-side development. The improvement of device hardware performance and the upgrade of the browser kernel also make it possible to achieve smooth animation on the page side. At present, the refresh rate of conventional devices is usually 60HZ, that is to say, if the user does not feel obvious lag, the rendering pipeline of the browser needs to output 60 pictures per second (60 FPS).
Next, the article will start from the basic rendering tree, introduce the browser rendering pipeline, and commonly used methods to optimize animation performance.
Rendering Basics
render tree
Although different rendering engines have different rendering processes, they all need to parse HTML and CSS to generate the rendering tree. The rendering engine with the most contact in front-end development is WebKit (and Blink, which is derived from it). Next, this article will introduce the rendering tree based on Webkit.
Image via GPU Accelerated Compositing in Chrome
In the above figure, in addition to the familiar DOM tree, there are also RenderObject
trees, RenderLayer
trees, and GraphicsLayer
trees, which together constitute the "rendering forest".
RenderObject
RenderObject
saves various information needed to draw DOM nodes, corresponding to the DOM tree, RenderObject
also constitutes a tree. But RenderObject
does not have a one-to-one correspondence with DOM nodes. RenderObject
is created if the following conditions are met:
document
node in the DOM tree;- Visible nodes in the DOM tree (webkit does not create
RenderObject
nodes for non-visible nodes); - To handle the need, Webkit creates anonymous
RenderObject
nodes, such asRenderBlock
(RenderObject
) nodes that represent block elements.
To draw DOM nodes on the page, in addition to knowing the information of the rendering nodes, the level of each rendering node is also required. The browser provides RenderLayer
to define the rendering level.
RenderLayer
RenderLayer
is a browser based on RenderObject
. RenderLayer
was originally used to generate stacking contexts to ensure that page elements are displayed in the correct hierarchy. Similarly, RenderObject
and RenderLayer
not one-to-one correspondence, RenderObject
will create the corresponding 061e50cfa9d37a if the following conditions are RenderLayer
( GPU Accelerated Compositing in Chrome ):
- the root node of the document;
- Nodes with explicit CSS positioning information (eg
relative
,absolute
ortransform
) - transparent node;
reflection
attributesoverflow
,mask
or 061e50cfa9d406;- A node with the
filter
Canvas
node with 3D Context or accelerated 2D Context;- The node corresponding to the
Video
We can RenderLayer
as a layer. Rendering is in each RenderLayer
on the layer, the RenderObject
drawn. This process can be drawn using the CPU, which is software drawing. However, software drawing cannot handle 3D drawing context. Each layer of RenderObject
cannot contain nodes that use 3D drawing, such as Canvas
nodes with 3D Contex, and cannot support CSS 3D change properties. In addition, in the page animation, every time the element size or position changes, the RenderLayer
tree must be re-constructed, triggering the Layout and subsequent rendering pipelines. This will cause the frame rate of the page to drop, resulting in visual stuttering. So modern browsers have introduced hardware accelerated drawing done by the GPU.
After obtaining the information of each layer, it needs to be merged into the same image. This process is called Compositing, which is called composite rendering.
In software rendering, compositing is actually not needed, because software rendering completes the drawing of each layer in the same memory space from front to back. In modern browsers, especially mobile devices, hardware-accelerated drawing done using the GPU is more common. Hardware-accelerated drawing done by the GPU requires compositing, and compositing is done using the GPU. This whole process is called hardware-accelerated composite rendering.
In modern browsers, not all drawing needs to be done using the GPU, as pointed out in "Webkit Technology Insider":
For common 2D drawing operations, using the GPU to draw does not necessarily have a performance advantage over using the CPU to draw, such as drawing text, points, lines, etc. The reason is that the CPU's cache mechanism effectively reduces the overhead of repeated drawing without requiring a GPU parallelism.
GraphicsLayer
In order to save the memory resources of the GPU, Webkit does not allocate a corresponding backend storage RenderLayer
Instead, according to certain rules, some RenderLayer
combined together to form a new layer with back-end storage for later synthesis, which is called a synthesis layer. In the composite layer, the storage space is represented GraphicsLayer
For a RenderLayer
object, if not individually promoted to a composite layer, the composite layer of its parent is used. A RenderLayer
has its own compositing layer if it has one of the following characteristics ( GPU Accelerated Compositing in Chrome
- CSS properties with 3D or perspective transforms;
Video
element using hardware-accelerated video encoding;Canvas
element with 3D Context or accelerated 2D Context; (Note: normal 2D Context will not be promoted to composition layer);- There are animations changed by
opacity
,transform
- Using hardware accelerated CSS filter technology;
- The descendant contains a composite layer;
- Overlap: There is a sibling node whose Z coordinate is smaller than itself, and this node is a composite layer.
For the compositing layer promotion caused by Overlap, Compositing in Blink / WebCore: From WebCore::RenderLayer to cc:Layer gives three pictures:
In Figure 1, the green rectangle at the top and the blue rectangle at the bottom are siblings, and the blue rectangle is promoted to a composite layer for some reason. If the green rectangle does not have a compositing layer boost, it will share a compositing layer with the parent node. This results in a rendering error where the green rectangle is at the bottom of the blue rectangle (Figure 2). So if overlap occurs, the green rectangle also needs to be promoted to a composite layer.
The boost conditions for the composite layer are in more detail in Wireless Performance Optimization: Composite 161e50cfa9d6b1. Combining RenderLayer
and GraphicsLayer
, it can be seen that animation (changes in size, position, style, etc.) elements are easier to create RenderLayer
, and then promoted to a composite layer (it should be noted here that not all CSS animation elements will be promoted to a composite layer , which will be introduced in the subsequent rendering pipeline). This design allows the browser to make better use of the GPU's capabilities, giving users a smooth animation experience.
Using Chrome's DevTools makes it easy to view a page's compositing layers:
Select "More tools -> Layers"
In the above picture, you can not only see the composition layers of the cloud music homepage, but also see the reasons for each composition layer in detail. For example, the reason the playbar at the bottom of the page is promoted to a composited layer is "Overlaps other composited content", which corresponds to "Overlap overlap: has a sibling node with a smaller Z coordinate than itself, and this node is a composited layer".
On front-end pages, especially during animations, the compositing layer lift due to Overlap can easily happen. If the overlapping top RenderLayer
promoted to a compositing layer every time, it will consume a lot of CPU and memory (Webkit needs to allocate a backend storage for each compositing layer). In order to avoid the occurrence of "layer explosion", the browser will perform layer compression (Layer Squashing): if multiple RenderLayer
overlap with the same composition layer, these RenderLayer
will be compressed into the same composition layer, that is, in the same composition Floor. However, for some special cases, the browser cannot perform layer compression, which will result in the creation of a large number of composite layers. Wireless performance optimizations: Composite describes several situations that can cause composite layer compression to fail. For reasons of space, it will not be introduced in this paper.
RenderObjectLayer
, RenderLayer
, GraphicsLayer
are the basis of rendering in Webkit, of which RenderLayer
determines the hierarchical order of rendering, RenderObject
stores the information required for each node rendering, and GraphicsLayer
uses GPU capabilities to accelerate page rendering.
rendering pipeline
After the browser creates a rendering tree, how will this information be presented on the page, which refers to the rendering pipeline.
For the code below:
<body>
<div id="button">点击增加</div>
<script>
const btn = document.getElementById('button');
btn.addEventListener('click', () => {
const div = document.createElement("div");
document.body.appendChild(div);
});
</script>
</body>
The Performance tab in DevTools can record and view the rendering process of the page (the width of the picture shown is limited, and there is no interception of the input part of the synthesis thread to obtain the event).
This process is almost identical to the schematic diagram of the rendering pipeline given in Aerotwist - The Anatomy of a Frame
There are two processes in the schematic diagram of the rendering pipeline: the rendering process (Renderer Process) and the GPU process (GPU Process).
Each page Tab has a separate rendering process, which includes the following threads (pools):
- Composite Thread (Compositor Thread) : Responsible for receiving the browser's vertical synchronization signal (Vsync, indicating the end of the previous frame and the beginning of the next frame), and also responsible for receiving user input such as scrolling and clicking. In the case of GPU compositing, generate drawing instructions.
- Main Thread (Main Tread) : The execution thread of the browser, our common Javascript calculations, Layout, and Paint are all executed in the main thread.
- Rasterizer thread pool (Raster/Tile worker) : There may be multiple rasterizer threads for rasterizing tiles. (If the main thread only converts the page content into a drawing instruction list, execute the drawing instruction here to obtain the color value of the pixel).
The GPU process (GPU Process) is not executed in the GPU, but is responsible for uploading the tile bitmap drawn in the rendering process to the GPU as a texture, and finally drawing it to the screen.
The entire rendering process is described in detail below:
1. Frame Start
The browser sends a vertical sync signal (Vsync) to indicate the start of a new frame.
2. Input event handlers
The synthesis thread passes input events to the main thread, which handles the callbacks for each event (including executing some Javascript scripts). Here, all input events (eg touchmove
, scroll
, click
) will only be fired once per frame.
3. requestAnimiationFrame
If the requestAnimiationFrame
(rAF) function is registered, the rAF function will execute here.
4. HTML parsing (Parse HTML)
HTML parsing needs to be performed if the previous operation resulted in a change to the DOM node (eg appendChild
5. Recalc Styles
If the CSS style was modified in the previous step, the browser needs to recalculate the modified DOM node and child node styles.
6. Layout
Calculate the geometric information such as the size and position of each visible element. It is usually necessary to document
, and the modification of some CSS properties will not trigger Layout (refer to CSS triggers ). Avoid large, complex layouts and layout jitter points out that the calculation of browser geometry is called Layout in Chrome, Opera, Safari and Internet Explorer. Called Reflow in Firefox, but actually the process is the same.
7. Update the render tree (Update Layer Tree)
The next step is to update the render tree. Changes to DOM nodes and CSS styles can result in changes to the render tree.
8. Paint
The actual drawing has two steps, which refers to the first step: generating drawing instructions. The drawing commands generated by the browser are very similar to the drawing API provided by Canvas
It can be viewed in DevTools:
These paint commands form a paint list, and the output of the Paint phase is these paint lists ( SkPicture
).
The SkPicture is a serializable data structure that can capture and then later replay commands, similar to a display list.
9. Composite
In DevTools this step is called Composite Layers, the compositing in the main thread is not really compositing. A copy of the render tree is maintained in the main thread ( LayerTreeHost
), and a copy of the render tree is also maintained in the compositing thread ( LayerTreeHostImpl
). With this copy, the composition thread can perform composition operations without interacting with the main thread. So while the main thread is doing Javascript computations, the compositing thread can still work without interruption.
After the rendering tree is changed, two copies need to be synchronized. The main thread sends the changed rendering tree and drawing list to the composition thread, and blocks the main thread to ensure that the synchronization can proceed normally. This is Composite Layers. This is the last step of the main thread in the rendering pipeline, in other words, this step just generates data for compositing, not the real compositing process.
10. Raster Scheduled and Rasterize
When the synthesis thread receives the information submitted by the main thread (rendering tree, drawing instruction list, etc.), it fills the information with bitmaps and converts it into pixel values, that is, rasterization. Webkit provides a thread pool for rasterization. The number of threads in the thread pool is related to platform and device performance. Since the size of each layer of the composite layer is the size of the entire page, before rasterization, the page needs to be divided and the layer is converted into tiles. The size of these tiles is usually 256*256 or 512*512. In DevTools "More tools -> Rendering", select "Layer borders" to view.
The image above shows a page divided into tiles, orange is the border of the composite layer, and cyan is the tile information. Rasterization is performed for each tile, and different tiles have different rasterization priorities. Usually, tiles located near the browser's viewport will be rasterized first (for more details, please refer to [Tile Prioritization] Design
]( https://docs.google.com/document/d/1tkwOlSlXiR320dFufuA_M-RF9L5LxFWmZFg5oW35rZk/edit# ). In modern browsers, rasterization is not performed in the compositing thread. The rendering process maintains a rasterization thread pool, which is the (Compositor Tile Workers) in the figure. The number of threads in the thread pool depends on system and device compatibility. .
Rasterization can be divided into software rasterization (Software Rasterization) and hardware rasterization (Hardware Rasterization), the difference is whether the bitmap is generated in the CPU, and then uploaded to the GPU for synthesis, or directly in the GPU for drawing and image pixels filling. The process of hardware rasterization is shown in the following figure:
Image from Raster threads creating the bitmap of tiles and sending to GPU
We can check whether Chrome's hardware rasterization is turned on chrome://gpu/
11. Frame End
After the tiles are rasterized, the compositor thread collects tile information called draw quads to create a composite frame. The composite frame is sent to the GPU process and the frame ends.
Draw quads: Contains information such as the tile's location in memory and where in the page to draw the tile taking in consideration of the page compositing.
Compositor frame: A collection of draw quads that represents a frame of a page.
12. Image display
The GPU process is responsible for communicating with the GPU and completing the drawing of the final image. The GPU process receives the composite frame, and if hardware rasterization is used, the rasterized texture is already stored in the GPU. The browser provides a 3D API for drawing (such as Webkit's GraphicsContext3D
class) to combine and draw textures into the same bitmap.
As mentioned above, for elements with animations such as transparency, they will be promoted to composite layers separately. These changes are actually set on the composite layer. Before the texture is merged, the browser acts on the composite layer through 3D deformation, that is, a specific effect can be achieved. That's why we say that transfrom
and transparency properties can improve rendering efficiency. Because these animations will not change the layout structure and texture during the execution process, that is, will not trigger the subsequent Layout and Paint.
Animation performance optimization
The browser's rendering pipeline is described above, but not every rendering triggers the entire pipeline. Some of these steps are not necessarily triggered only once. Here are several ways to improve rendering efficiency in the order of the rendering pipeline:
Handle page scrolling reasonably
In the rendering pipeline of the browser, the composition thread is the entry point for user input events. When the user input event occurs, the composition thread needs to determine whether the main thread needs to participate in subsequent rendering. For example, when the user scrolls the page, all layers have been rasterized, and the synthesis thread can directly generate the synthesis frame without the participation of the main thread. If the user binds event handlers on some elements, the compositing thread marks those regions as non-fast scrollable regions. When a user input event occurs in a non-fast scrolling scroll area, the synthesis thread will pass this event to the main thread for Javascript calculation and subsequent processing.
In front-end development, event delegation is often used to delegate the events of some elements to its parent element or outer elements (such as document
), and the binding events of its outer elements are triggered by event bubbling. Execute a function on an element. Event delegation can reduce the memory consumption caused by binding multiple sub-elements to the same event handler, and can also support dynamic binding, which is widely used in front-end development.
If you use event delegation document
, the entire page will be marked as a non-fast scrolling area. This means that the composition thread needs to send each user input event to the main thread, wait for the main thread to execute Javascript to process these events, and then compose and display the page. In this case, smooth page scrolling is difficult to achieve.
In order to optimize the above problem, the third parameter of the addEventListener
{ passive: true }
(default is false
), this option tells the synthesis thread to still pass the user event to the main thread for processing, but the synthesis thread will continue to synthesize new The frame will not be blocked by the execution of the main thread. At this time, the preventDefault
function in the event handler is invalid.
document.body.addEventListener('touchstart', event => {
event.preventDefault(); // 并不会阻止默认的行为
}, { passive: true });
Image via Inside look at modern web browser (part 4)
In addition, in business scenarios such as lazy loading, it is often necessary to monitor page scrolling to determine whether relevant elements are in the viewport. A common method is to use Element.getBoundingClientReact()
get the bounding information of the relevant element, and then calculate whether it is in the viewport. In the main thread, calling Element.getBoundingClientReact()
every frame can cause performance issues (eg forced page reflow due to improper use). Intersection Observer API provides a way to asynchronously detect changes in the intersection of a target element with ancestor elements or viewports. This API supports registering callback functions that are triggered when the intersection of the monitored element and other elements changes. In this way, the judgment of intersection is handed over to the browser to manage and optimize itself, thereby improving the scrolling performance.
Javascript optimization
Reduce the execution time of Javascript in the main thread
For devices with a frame rate of 60FPS, each frame needs to be executed within 16.66 milliseconds. If this requirement cannot be fulfilled, the content will shake on the screen, that is, freeze, affecting the user experience. In the main thread, the user's input needs to be calculated. In order to ensure the user's experience, it is necessary to avoid long-term calculations in the main thread to prevent subsequent processes from being blocked.
Optimizing JavaScript Execution In the , the following points are proposed to optimize Javascript:
- For the implementation of animation effects, avoid using setTimeout or setInterval, use requestAnimationFrame.
- Move long-running JavaScript from the main thread to the Web Worker.
- Use microtasks to perform DOM changes over multiple frames.
- Use Chrome DevTools' Timeline and JavaScript Analyzers to assess the impact of JavaScript.
When using setTimeout
/ setTimeInterval
to perform animations, because it is not sure at which stage in the rendering pipeline the callback will occur, if it is right at the end, it may result in dropped frames. In the rendering pipeline, rAF will be executed after Javascript and before Layout, and the above problems will not occur. Shifting the pure computing work to the Web Worker can reduce the execution time of Javascript in the main thread. For large computing tasks that must be executed in the main thread, consider splitting them into RequestIdleCallback
and processing them in rAF or 061e50cfa9de51 per frame (refer to React Fiber's implementation).
Reduce forced reflow caused by unreasonable Javascript code (Force Layout)
In the rendering pipeline, the operation of Javascript/rAF may change the rendering tree, which in turn triggers the subsequent layout. el.style.backgroundImage
and el.style.offsetWidth
in Javascript/rAF, Force Layout may be triggered, causing subsequent Recalc styles or Layout to be executed before this step. affects rendering efficiency .
requestAnimationFrame(logBoxHeight);
function logBoxHeight() {
box.classList.add('super-big');
// 为了获取到box的offsetHeight值,浏览器需要在先应用super-big的样式改变,然后进行布局(Layout)
console.log(box.offsetHeight);
}
Reasonable course of action is
function logBoxHeight() {
console.log(box.offsetHeight);
box.classList.add('super-big');
}
Reduce Layout and Paint
That is, the cliché of reducing reflows and redraws. For the three stages of Layout, Paint and Synthesis of the rendering pipeline, Layout and Paint are relatively time-consuming. But not all frame changes need to go through the complete rendering pipeline: Layout is triggered when a modification to a DOM node causes its size and position to change; and if the change does not affect its position in the document flow, the browser does not The layout needs to be recalculated, just generate a drawing list and paint. Paint is based on composite layers. Once you change the style of an element that triggers Paint, the composite layer where the element is located will be re-Painted. So, for some animated elements, it can be promoted to a separate compositing layer, reducing the scope of the Paint.
Synthetic layer boost
When introducing the rendering tree, it was mentioned that the RenderObjectLayer
that meets certain conditions will be promoted to the composite layer. The drawing of the composite layer is performed in the GPU, which has better performance than the CPU; if the composite layer needs Paint, it will not affect other compositing layer; some compositing layer animations do not trigger Layout and Paint. The following are some common ways to improve the synthesis layer in development:
Writing animations using transform
and opacity
As mentioned above, if an element is animated with CSS transparency effects or CSS transform animations, it will be promoted to a composite layer. And these animation transforms are actually applied on the compositing layer itself. The execution process of these animations does not require the participation of the main thread. Before texture synthesis, the composite layer can be deformed using the 3D API.
#cube {
transform: translateX(0);
transition: transform 3s linear;
}
#cube.move {
transform: translateX(100px);
}
<body>
<div id="button">点击移动</div>
<div id="cube"></div>
<script>
const btn = document.getElementById('button');
btn.addEventListener('click', () => {
const cube = document.getElementById('cube');
cube.classList = 'move';
});
</script>
</body>
For the above animation, the promotion of the composition layer will only be performed after the animation starts, and the promotion of the composition layer will also disappear after the animation ends. This also avoids the CPU performance penalty caused by the browser creating a large number of compositing layers.
will-change
This property tells the browser to perform some special transformations on certain elements next. When will-change
set opacity
, transform
, top
, left
, bottom
, right
(wherein top
, left
, bottom
, right
other attributes need to set specific targeting, such as relative
etc.), the browser will be synthesized with this element ascend. When writing, avoid the following spellings:
*{ will-change: transform, opacity; }
This way, all elements are promoted to separate compositing layers, resulting in a large memory footprint. So you need to set will-change
only for the animation element, and after the animation is completed, you need to manually remove this attribute.
Canvas
Animations are done Canvas
with accelerated 2D Context or 3D Context. Due to its independent composition layer, Canvas
will not affect the drawing of other composition layers, which is more suitable for large and complex animations (such as HTML5 games). In addition, you can also set multiple Canvas
elements to reduce drawing overhead by layering Canvas with reasonable
CSS container module
The CSS Containment Module has recently released version Level 3 The main goal is to improve page rendering performance by isolating specific DOM elements from the entire document's DOM tree, so that changes to those elements do not affect other parts of the document. The CSS container module mainly provides two properties to support such optimization.
contain
contain
attribute allows developers to specify specific DOM elements independently of the DOM tree. For these DOM elements, the browser can calculate their layout, style, size, etc. individually. Therefore, when contain
attribute changes, it will not cause the overall rendering tree to change, resulting in the Layout and Paint of the entire page.contain
has the following values:
layout
contain
value is layout
will be independent of the overall layout of the page, and the change of the element will not cause the layout of the page.
paint
contain
A DOM node with a value of paint
, indicating that its child elements will not be displayed beyond its boundaries. So if a DOM node is offscreen or invisible, its children can be guaranteed to be invisible. It also does the following:
- For child nodes of
position
valuefixed
orabsolute
contain
value ofpaint
becomes a containing block ( containing block ). contain
A DOM node with a value ofpaint
contain
A DOM node with a value ofpaint
creates a formatting context (BFC).
size
contain
is a DOM node whose value is size
size
will not be affected by its children.
style
contain
A DOM node with a value of style
, indicating that its CSS properties do not affect elements other than its children.
inline-size
inline-size
is the latest addition to Level 3. contain
A DOM node with a value of inline-size
principal box intrinsic-size inline axes are not affected by the content.
strict
Equivalent to contain: size layout paint
content
Equivalent to contain: layout paint
In a complex page with a large number of DOM nodes, modifying DOM elements that are not in a separate composition layer will cause the Layout and Paint of the entire page. In this case, setting contain
attributes (such as contain:strict
) to these elements can significantly improve page performance. .
An Introduction to CSS Containment given a long list of examples , the first in a long list item
of contain
property to strict
, and change the item
content, after previously forced manual triggering heavy pages Row. Compared with no setting of strict
, the execution time of Javascript is reduced from 4.37ms to 0.43ms, and the rendering performance has been greatly improved. contain
is as follows:
content-visibility
contain
attribute requires us to determine whether the DOM element needs to be optimized for rendering during development, and set the appropriate value. content-visibility
provides another way, set it to auto
, the browser can automatically optimize. As mentioned above, the compositing thread will convert each page-sized layer into tiles, and then rasterize the tiles according to a certain priority, and the browser will render all elements that may be viewed by the user. . content-visibility
value set for auto
element in the off-screen, the browser will calculate its size, scroll bars to the right show the structure of a page, but the browser without its child elements generated render tree, which means it's Child elements will not be rendered. When the page scrolls so that it appears in the viewport, the browser starts rendering its child elements.
But it will also lead to a question: content-visibility
value set for auto
elements, from the state under the screen, the browser will not be Layout of its child elements, and therefore can not determine the size of its children, then if not explicitly specified size, its size will be 0, which will cause the display of the entire page height and scrollbar to be wrong. To solve this problem, CSS provides another property contain-intrinsic-size
to set content-visibility
value auto
footprint size of the element of time. This ensures that the element still occupies space when the page is laid out, even if it is not explicitly sized.
.ele {
content-visibility: auto;
contain-intrinsic-size: 100px;
}
content-visibility: the new CSS property that boosts your rendering performance gives an example of a travel blog. By setting content-visibility
reasonably, the first load performance of the page has been improved by 7 times. content-visibility
is as follows:
Summarize
There have been a lot of articles about the browser rendering mechanism. But some articles, especially those involving browser kernels, are rather obscure. This article starts from the bottom rendering of the browser, and introduces the rendering tree and rendering pipeline in detail. Then, according to the order of the rendering pipeline, the ways to improve the animation performance are introduced: reasonable handling of page scrolling, Javascript optimization, reduction of Layout and Paint. I hope it will help you understand the rendering mechanism of the browser and the daily animation development.
Reference article
- Webkit Technology Insider - Zhu Yongsheng
- GPU Accelerated Compositing in Chrome
- Compositing in Blink / WebCore: From WebCore::RenderLayer to cc:Layer
- Wireless performance optimization: Composite
- The Anatomy of a Frame
- Avoid large, complex layouts and layout jitter
- Software vs. GPU Rasterization in Chromium
- optimize JavaScript execution
- Browser rendering pipeline analysis and web animation performance optimization
- Tile Prioritization Design
- CSS Containment Module Level 3
- Let’s Take a Deep Dive Into the CSS Contain Property
- CSS triggers
- and composites are just the tip of the iceberg 161e50cfa9e6f2
- Improve page rendering speed using only CSS
This article is published from NetEase Cloud Music Front-end Team , any form of reprinting of the article is prohibited without authorization. We recruit front-end, iOS, and Android all year round. If you are ready to change jobs and happen to like cloud music, then join us at grp.music-fe (at) corp.netease.com!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。