1. What is the browser rendering process?

In chronological order of rendering, the pipeline can be divided into the following sub-stages: DOM tree building, style calculation, layout stage, layering, rasterization, and display.

  1. The renderer transforms HTML content into a DOM tree structure that is readable.
  2. The rendering engine translates CSS styleSheets into styleSheets that browsers can understand, calculating the style of DOM nodes.
  3. Create a layout tree and calculate the layout information for the elements.
  4. Layer the layout tree and generate a hierarchical tree.
  5. Generate a draw list for each layer and submit it to the composition thread. The composition thread divides the layer into blocks and rasterizes the blocks into bitmaps.
  6. The composite thread sends the draw block command to the browser process. The browser process generates the page on command and displays it on the monitor.

After the browser obtains HTML byte data from the network or hard disk, it will go through a process to parse the byte into DOM tree. First, the original HTML byte data is converted into characters specified in the file encoding. Then, the browser will convert the string into various token tags according to THE HTML specification, such as HTML and body. The final resolution is a tree object model, the DOM tree;

When the HTML code encounters a tag, the browser sends a request for the TAG’s CSS. When the rendering engine receives the CSS text, it performs a conversion operation. Convert CSS text to styleSheets that browsers can understand

Create a layout tree, traverse all visible nodes in the DOM tree, and add these nodes to the layout; Invisible nodes are ignored by the layout tree, such as everything under the head tag, or the body.p.pan element, which is not included in the layout tree because its attribute contains dispaly: None. Finally, the DOM element layout information is computed so that it is stored in the layout tree. During the layout completion process, if there are JS operations or other operations, changes to the color and background of elements will cause redrawing, if there are changes to the size and positioning of elements will cause backflow.

Because there are many complex effects on the page, such as complex 3D transformations, scrolling, or z-indexing, the rendering engine will need to generate a tree of layers for each node to make it easier to achieve these effects.

The rendering engine does the drawing of layers, breaks the drawing of a layer into smaller drawing instructions and then puts them into a list of drawing instructions in order. When the drawing list of layers is ready, the main thread submits the drawing list to the compositing thread, which divides the layers into blocks. The bitmap is then generated in preference to the blocks near the viewport (the actual bitmap generation is performed by rasterization. Rasterization refers to the conversion of blocks to bitmaps.)

Once all the blocks have been rasterized, the composition thread generates a command to draw the block, which it then submits to the browser process, which displays it.

2. How to understand reflux and redraw?

Backflow: When we make changes to the DOM that result in a change in the DOM’s geometry (such as changing the width or height of an element, or hiding an element), the browser recalculates the element’s geometry (which also affects the geometry and position of other elements), and then draws the calculated results. This process is called backflow (also known as rearrangement).

Redraw: When we make changes to the DOM that result in a style change without affecting its geometry (such as changing the color or background color), the browser doesn’t have to recalculate the element’s geometry and simply draw a new style for the element (skipping the backflow shown above). This process is called redrawing. From this we can see that redrawing does not necessarily lead to backflow, backflow does lead to redrawing.

Common elements that cause reflux:
  • Common geometry attributes are width, height, padding, margin, left, top, border, and so on.
  • The most overlooked operation: Get attributes that need to be computed in real time when you need attributes like this: OffsetTop, offsetLeft, When offsetWidth, offsetHeight, scrollTop, scrollLeft, scrollWidth, scrollHeight, clientTop, clientLeft, clientWidth, clientHeight, The browser also backflows to retrieve these values.
  • Backflow is also triggered when we call the getComputedStyle method, or currentStyle in IE. The principle is the same, both for a “immediacy” and “accuracy”.
How to avoid:
  1. Avoid changing styles line by line and use class names to merge styles
  2. Take the DOM “offline” and use the DocumentFragment
  3. Upgrade to a composite layer, as usedwill-change
#divId {
  will-change: transform;
}
Copy the code

advantages

  • The bitmap of the composite layer will be synthesized by the GPU faster than the CPU
  • When repaint is required, only repaint itself is required and no other layers are affected
  • Layout and Paint are not triggered for Transform and opacity effects

Note:

Some browsers cache a Flush queue, filling it with the backflow and redraw tasks that we trigger, and then flushing them out when the queue becomes crowded, or when it reaches a certain interval, or when it “has to.” However, when we access some attributes, the browser will flush the task out of the queue in order to get the most accurate attribute value at the moment.

3. When will the rendering engine create a new layer for a particular node?

A cascading context is the three-dimensional concept of HTML elements that extend along an imaginary Z-axis relative to the user facing a window or web page, occupying the space of the cascading context in order of priority according to their own attributes.

  1. Elements with cascading context attributes are promoted to a separate layer.

Have cascading context properties:

  • The root element (HTML),
  • The absolute/relative positioning element whose z-index is not “auto”,
  • Position is compatible with all mobile browsers, but not with older desktop browsers.
  • Z – the index value is not to “auto” flex items (flex item), i.e., the parent element display: flex | inline – flex,
  • Z-index A grid child whose value is not “auto”, that is, the parent element display: grid
  • Any element with an opacity property value less than 1 (see the Specification for opacity),
  • Elements whose transform property value is not “None”,
  • Elements whose mix-blending-mode attribute is not “normal”,
  • Filter elements that are not “None”,
  • Elements whose perspective value is not “None”,
  • Elements whose clip-path value is not “None”
  • Mask/mask-image/mask-border is not an element of “None”
  • The isolation property is set to elements of “ISOLATE”
  • Arbitrary CSS properties specified in will-change (see this article)
  • -webkit-overflow-scrolling for the elements set to “touch”
  • Contain property: “layout”, “paint”, or any other combination such as “strict”, “Content”
  1. Places that need to be clipped will also be created as layers.

The clipping here means that if we limit the size of a DIV to 200 * 200 pixels, and there is a lot of text in the div, the area displayed by the text will definitely exceed 200 * 200 pixels. The rendering engine will use a portion of the clipped text content to display in the div area. When this clipping happens, the rendering engine creates a separate layer for the text section, and if the scroll bar appears, the scroll bar is promoted to a separate layer.

How does JavaScript support block-level scope?

Block-level scope is implemented through the stack structure of the lexical environment, and variable promotion is implemented through the variable environment. By combining the two, the JavaScript engine supports both variable promotion and block-level scope.

The lexical environment, as well as the functional context, is implemented through the stack structure. Variables declared by var inside a function are stored in the context of the function at compile time, while variables declared by let and const are appended to the lexical context. When the block is finished, anything appended to the lexical scope is destroyed.

Here’s an example:

function foo() {
    var test = 1
    let myname= 'LuckyWinty'
    {
        console.log(myname) 
        let myname= 'winty'
    }
    console.log(test,The '-',myname) 
}
foo()
// What is the output?
Copy the code

The execution context before execution to the first console.log looks like this:

As you can see from the figure, the first console.log should theoretically print undefined. But the syntax provides for a “temporary dead zone” (TDZ, when entering its scope, it cannot be accessed (retrieved or set) until the execution reaches the declaration), which means that the JavaScript engine will throw an error if the variable declared by the let is already in the lexical context without assigning a value to it.

[Uncaught ReferenceError: Cannot access ‘myname’ before initialization] To allow our code to continue parsing, we add a try-catch and continue parsing:

function foo() {
    var test = 1
    let myname= 'LuckyWinty'
    try{{console.log(myname) 
            let myname= 'winty'}}catch(ex){
        console.error(ex)
    }
    console.log(test,The '-',myname) 
}
foo()
// What is the output?
Copy the code

The execution context before execution to the second console.log looks like this:

At this point, the contents of the {} block scope have been executed and destroyed. The second console.log will output 1 “– “”LuckyWinty”.

5. How is data stored in JavaScript in memory?

In JavaScript, an assignment of a primitive type copies the value of a variable, while an assignment of a reference type copies the reference address.

During the execution of JavaScript, there are three main types of memory space: code space, stack space, and heap space. The code space is mainly used to store executable code. Data values of primitive types (Number, String, Null, Undefined, Boolean, Symbol, BigInt) are directly stored in the “stack”, and values of reference types (Object) are stored in the “heap”. So in the stack space (execution context), primitive types store the value of a variable, and reference types store its address in the “heap space”. When JavaScript needs to access that data, it accesses it through the reference address in the stack, which is equivalent to one more hand-passing process.

During compilation, if the JavaScript engine detects a closure, it also creates a “closure(fn)” object in the heap space (this is an internal object that JavaScript cannot access) to hold variables in the closure. So variables in closures are stored in “heap space.”

JavaScript engine needs to use the stack to maintain the state of the context during program execution. If the stack space is too large, all the data will be stored in the stack space, which will affect the efficiency of context switch, and then affect the efficiency of the entire program execution. In general, the stack space is not set too large and is mainly used to store small data of primitive types. Reference data takes up a lot of space, so this kind of data will be stored in the heap, the heap space is large, can store a lot of large data, but the disadvantage is that allocating memory and reclaim memory will take a certain amount of time. Hence the need for both “stack” and “heap” Spaces.

The resources

  • Geek Time “How Browsers Work and Practice”

The last

  • It is not easy to arrange, please give me a thumbs up ~ ~
  • To learn more, please pay attention to my blog and give me a star ~ ~
  • Welcome to add my wechat (Winty230), pull you into the technology group, long-term exchange learning ~~
  • Feel the content is helpful can pay attention to my public number “front-end Q”, learn and grow together ~ ~