Introduction to the virtual DOM, PWA and WebComponent.

15. Message queues and event loops

Thread model evolution

The first version is executed sequentially

All tasks are written into the main thread in sequence. These tasks are executed in sequence when the thread is executing. When the execution is complete, the thread automatically exits.

Problem: Unable to process new tasks.

The second version introduces event loops

A for loop is used to listen for new tasks.

Problem: Cannot receive tasks from other threads.

Third edition queue + loop

  1. Add a message queue to hold tasks to be executed.
  2. New tasks generated in the IO thread are added to the end of the message queue. (IO threads, which are used by renderers to receive messages from other processes.)
  3. The render main thread circularly reads and executes tasks from the message queue header.

Tasks in message queues are called macro tasks, and each macro task contains a queue of micro tasks.

16. WebAPI: setTimeout

Realize the principle of

In Chrome, in addition to the normal message queue, there is a message queue for deferred tasks, which stores timers and internal tasks that need to be delayed.

When setTimeout is called to set the callback function, the renderer creates a callback task that contains the callback function, the current launch time, and the deferred execution time, and then adds the task to the deferred execution queue.

When a task in the message queue is processed, it calculates whether there are any expired tasks and executes them once. << ❓❓ is it not time to put the delayed task in the queue, waiting to be executed?

The deferred execution queue is actually a HashMap structure. (It doesn’t fit the fifO feature.)

Precautions for use

  1. If the current task is executed for a long time, the execution of the expired task will be delayed.
  2. SetTimeout if nested calls exist, the system sets the minimum interval of 4ms.
  3. For inactive pages, setTimeout executes at a minimum interval of 1000ms.

17. WebAPI: XMLHttpRequest

Why is it that the XHR request callback is put in the message queue, and the request that we make with the AXIos HTTP library is put in the microtask? Even though axios uses promise, the promise callback is a microtask, But axios is still a wrapper around native XHR ❓❓

Create a process

  1. Create an XMLHttpRequest object that performs the actual network request operation.
  2. Register callback functions for XHR objects, onTimeout, onError, onReadyStatechange.
  3. Configure basic request information. (With xhr.responseType configuration, it is really possible to automatically convert the data returned by the server to the specified format ❓❓)
  4. Send the request.
let xhr = new XMLHttpRequest(); xhr.open('GET', URL, true); xhr.timeout = 2000; xhr.responseType = 'json'; // text, json, document, blob, arrayBuffer xhr.setrequestheader ('key', 'val'); // text, json, document, blob, arrayBuffer xhr.setrequestheader ('key', 'val'); xhr.send();Copy the code

The problem

  1. Across domains.
  2. HTTPS mixed content. A page contains mixed content that does not meet HTTPS security requirements, such as HTTP resources, images, videos, style sheets, and scripts.

18. Macro and micro tasks

Macro task

  • Render events (such as PARSING DOM, calculating layout, drawing);
  • User interaction events (such as mouse click, page scrolling, zooming in and out, etc.);
  • JS script execution events;
  • Network request completed, file read/write completed event. ❓ ❓

Asynchronous callback implementation 1: Encapsulate the asynchronous function as a macro task, add it to the end of the message queue, and execute the callback function when the loop executes the task.

Asynchronous callback implementation 2: The callback function is executed after the execution of the main function but before the completion of the current macro task, in the form of a micro task.

Micro tasks

When JS executes a script, V8 creates a global execution context for it and internally creates a queue of microtasks.

Produce way

  • The first way is to use MutationObserver to monitor a DOM node, and then modify the node through JavaScript, or add or remove partial nodes to the node. When the DOM node changes, it produces a microtask to record the DOM changes.
  • The second approach: use promises, which also produce microtasks when promise.resolve () or promise.reject () is called.

Execution time

When the macro task is completed and the JS engine is ready to push out the global execution context and clear the call stack, the JS engine will check the microtask queue in the global execution context and execute it in order.

MutationObserver

The performance problem of synchronous operation is solved by asynchronous operation and the real-time problem is solved by micro-task.

// Select a node to observe
var targetNode = document.getElementById("some-id");
// Set the observer configuration options
var config = { attributes: true.childList: true.subtree: true };
// The function that needs to be executed when the node changes
var callback = function (mutationsList, observer) {
  for (var mutation of mutationsList) {
    if (mutation.type == "childList") {
      console.log("A child node has been added or removed.");
    } else if (mutation.type == "attributes") {
      console.log("The " + mutation.attributeName + " attribute was modified."); }}};// Create an observer example associated with the callback function
var observer = new MutationObserver(callback);
// Use the configuration file to observe the target node
observer.observe(targetNode, config);
// Stop observing
observer.disconnect();
Copy the code

19. Promise

Eliminate nested calls and frequent error handling.

1. Why microtasks were introduced in Promises?

Promise’s resolve and Reject callbacks use a delayed binding mechanism for the callback function, so when resolve is executed, the callback function is not bound yet and needs to be delayed. Compared to setTimeout, the Promise uses microtasks, which can delay calls and improve the efficiency of code execution.

2. How does Promise implement callback function return value penetration?

Return a new Promise in then.

3. When a Promise fails, how does it “bubble” to the last function that catches the exception?

Reject returns the error wrapped as promise.reject, and continues to return promise.reject if no exception is handled by a second argument in then.

20. async – await

The underlying implementation mechanism of Generator — Coroutine. Async-await uses both Generator and Promise technologies.

Generators, coroutines

A generator function is an asterisk function that pauses execution and resumes execution.

Coroutines are much lighter than threads. A thread can have more than one coroutine, but only one coroutine can be executed on the thread at a time.

function* genDemo() {
    console.log("Proceed with paragraph one.")   / / 3
    yield 'generator 1'  			// 4. Yield control of the main thread with yield
    console.log("Proceed to paragraph 2.")   / / 7
    yield 'generator 2'			    / / 8
    console.log("End of execution")		 / / 11
    return 'generator 2'			// 12. Close the current coroutine with return
}
console.log('main 0')
let gen = genDemo()   				// 1. Create gen coroutine
console.log(gen.next().value)	 	 // 2. Restore the gen coroutine with gen.next()
console.log('main 1')				/ / 5
console.log(gen.next().value)		 / / 6
console.log('main 2')				/ / 9
console.log(gen.next().value)		 / / 10
console.log('main 3')			     / / 13
/* Gen coroutines and parent coroutines are executed interactively on the main thread, using yield and gen.next. When the Gen coroutine calls yield, the JS engine saves the current stack information of the Gen coroutine and restores the stack information of the parent coroutine. The parent coroutine calls the Gen.next procedure in a similar way. /Copy the code

async

Async is a function that executes asynchronously and implicitly returns a Promise as a result.

await

async function foo() {
  console.log(1); / / 3
  let a = await 100; // 4. Suspend foo coroutine and return primise_ to parent coroutine, promise_. Then >> go to 5
  // await internal operation
  // let promise_ = new Promise((resolve, reject) => {
  // resolve(100) // 6. Execute microtask, promise_. Then set callback function is activated
  //}) // 7. Pass the value of resolve to foo coroutine (assign the value to a), temporarily execute the parent coroutine, and give control to Foo coroutine
  console.log(a); / / 8
  console.log(2); / / 9
}
console.log(0); / / 1
foo(); // 2. The parent coroutine starts the foo coroutine
console.log(3); / / 5
Copy the code

Disclaimer: Geek Time - How browsers work and practice

21. Chrome Developer Tools

DOMContentLoaded, which means that the page has already built the DOM, meaning that the HTML, JavaScript, and CSS files needed to build the DOM have all been downloaded. Load indicates that the browser has loaded all the resources (images, style sheets, and so on).

Timeline panel

  1. Resource Scheduling
    1. Queueing: New HTTP requests need to be queued due to resource priorities, a maximum of six TCP connections in a domain name, and network processes allocating disk space for data.
  2. Connection Start
    1. 例 句 : There are also reasons why a connection may be delayed before it is launched In addition, the Proxy server adds a Proxy Negotiation stage.
    2. Initial Connection: The event that takes to establish a TCP connection.
    3. SSL: If HTTPS is used, an additional SSL handshake is required to negotiate the encryption time.
  3. Request/Response
    1. Request sent: Prepares the Request data and sends it to the network without determining whether the server has received it.
    2. Waiting (TTFB) : Waiting to receive the first byte of data from the server. Indicates the server response speed.
    3. Content Download: The time from the first byte event to receiving the full response data.

Optimize time items on the timeline

  1. Queueing time is too long: domain name sharding (resources of a site are grouped under multiple domain names), upgraded to HTTP2 (multiplexing without 6 TCP connections limit).
  2. TTFB time too long:
    1. The server processes data too slowly, and various caching techniques can be added.
    2. CDN can be used to cache static files for network reasons (low bandwidth or cross network).
    3. Extra information in the request header leads to longer server processing time and reduces unnecessary cookie information.
  3. Content Download time too long: Too many bytes of data, need to compress, remove unnecessary comments to reduce the file size.

22. The DOM tree

DOM is the internal data structure that expresses HTML, connects Web pages to JavaScript scripts, and filters out unsafe content.

DOM tree generation

Inside the rendering engine, the HTMLParser module is responsible for converting HTML byte streams into DOM structures.

Web process >> HTML parser

  1. After the web process receives a response header, such as a content-Type value of text/ HTML, the browser selects or creates a renderer process for the request.
  2. Between the network process and the renderer process, the data transmission pipeline is established, and the HTML parser dynamically receives the byte stream and parses it into DOM.

Byte stream >> DOM

  1. The tokenizer converts the byte stream to a Token (StartTag Token, EndTag Token, and Text Token).
  2. The Token is resolved into a DOM node and the DOM node is added to the DOM tree. (When the HTML parser starts working, it silently creates an empty DOM structure rooted in Document, and the DOM nodes generated by parsing are mounted under document.)

The HTML parser maintains a Token stack structure that computes parent-child relationships between nodes.

  • The StartTag Token is pushed one by one, and a DOM node is created for the Token to be added to the DOM tree. The parent node is the node generated by the neighboring element in the stack.
  • Text Token, a Text node is generated and added to the DOM tree. Its parent node is the DOM node corresponding to the top of the stack Token.
  • If the StartTag Token matches the top Token, the StartTag Token is removed from the stack. If the StartTag Token does not match, an error is thrown.

JS blocks DOM parsing

  1. When a JS script is inserted into the HTML, the HTML parser pauses DOM parsing because the script in the script tag may modify the DOM that is currently generated.
  2. Downloading JS files also blocks DOM parsing. However, Chrome and other browsers have been optimized for pre-parsing operations. When the rendering engine receives the byte stream, it starts a pre-parsing thread to analyze THE JS and CSS files contained in the HTML, parse the relevant files and download them in advance.
  3. JS may manipulate CSSOM, so the CSS file is downloaded and parsed before executing the JS script.

22. Rendering assembly line

From submitting data to the first rendering stage, it includes parsing HTML, downloading CSS, downloading Javascript, generating CSSOM, executing JS, generating layout trees, drawing and other column operations.

Bottlenecks: download CSS, download Javascript, execute JS.

24. Stratification and synthesis mechanisms

Monitor & video card

  1. The graphics card’s job is to synthesize new images and save them to the back buffer.
  2. The system swaps back and front buffers.
  3. The monitor reads the latest composite image from the front buffer and displays it on the monitor. Updates (reads) 60 times per second.

The graphics card is updated at the same rate as the monitor. In complex cases, processing speed may slow down and cause stutter. Chrome addresses the lag problem by introducing compositing and layering mechanisms,

Stratification & synthesis

Layering: Breaking up a material into layers. In Chrome, after layering generates a layout tree, the rendering engine converts it to a layer tree based on the characteristics of the layout tree, with each node in the layer tree corresponding to a layer.

Composition: The operation of merging these layers together. After the drawing list is generated, the raster generates images, which are combined into a “single” image. The compose operation is completed on the compose thread without affecting the main thread execution. Therefore, CSS animations are more efficient than JS animations.

In the composition thread, we implement a set transformation of the entire layer. Changes involving content in layers are the process of rearranging or redrawing.

The CSS attribute, will-change, tells the rendering engine in advance that certain elements will be transformed. At this time, the rendering engine will implement a single layer of the element, and after the transformation is completed, the composite thread will perform the transformation. Of course, preparing a separate layer for an element also increases the memory footprint.

.box {
  will-change: opacity, transform;
}
Copy the code

block

The compositing thread will divide each layer into blocks of fixed size, giving priority to drawing blocks close to the viewport. Another Chrome strategy is to use a low-resolution image when first compositing a block.

25. Page performance

Performance optimization during loading

  • Reduce the number of critical resources.
  • Reduce the size of critical resources.
  • Reduce the number of RTT counts for critical resources.

RTT, Round Trip Time, Round Trip Time. An HTTP packet is about 14KB. If the data is large, it needs to be divided into multiple packets for transmission. The larger the number of packets is, the longer the round-trip time is.

Performance optimization in the interaction phase

Minimize the generation time of a frame by doing the following.

  • Less JS script execution time, avoid long time occupation of the main thread.
  • JS completes the query before modifying the DOM, avoiding forced synchronization of the layout. Normal layout JS execution and recalculation of the style layout are two tasks. Forcing a synchronous layout brings the computed style and layout action forward to the current task.
  • Do not read and modify the DOM in loop statements to avoid layout jitter (repeated layout operations).
  • Make good use of CSS compositing animation.
  • Create temporary objects less frequently in functions to avoid frequent garbage collection.

26. The virtual DOM

The defect of the DOM

For every DOM operation, the rendering engine needs to rearrange, redraw, or compose. When the DOM structure is complex, performing a rearrangement or redraw operation can be time-consuming.

Virtual DOM

A JS object that reflects the actual DOM structure. Solve the problem of slow page response caused by frequent DOM manipulation.

The react virtual DOM executes the flow

Create a stage

  1. Create a virtual DOM for JSX and base data.
  2. Create a real DOM tree from a virtual DOM tree.
  3. After the actual DOM tree is generated, the rendering pipeline is triggered to output the page to the screen.

Update the stage

  1. The data changes and a new virtual DOM tree is created based on the new data.
  2. React compares the two trees using the Stack Reconciler /Fiber Reconciler algorithm to find out where the changes are.
  3. The changes are updated to the real DOM tree at a time, and the rendering engine updates the rendering pipeline to generate new pages.

The comparative virtual DOM is a recursive function. While the old Stack Reconciler algorithm executes on the main thread, the latest Fiber Reconciler algorithm uses coroutines to give away the main thread, which solves the problem of the function taking too long.

Double cache

Graphical manipulation, such as developing games, is complex and requires a lot of computation, and a complete image takes multiple computations to complete. If part of the calculation is written into the buffer, it will cause problems such as incomplete image display. Double cache, is to put the intermediate calculation results into a buffer, all the calculation is finished and then the complete image is copied to the display buffer. The virtual DOM is like an intermediate buffer.

27. Progressive Web application PWA

Disadvantages of Web applications and solutions

  • Lack of offline usage. << Service Worker
  • Lack of notification push capability. << Service Worker
  • The Web application cannot be installed to the desktop due to the lack of a level 1 entry. << mainfest.json

Service Worker

Add an interceptor between the page and the network to cache and intercept requests.

  • A Service Worker runs in a browser process, outside the main thread, similar to a Web Worker, serving multiple pages.
  • The Service Worker accepts messages pushed by the server even if the browser page is not started. ❓ ❓
  • For security reasons, the Service Worder uses HTTPS.

28. WebComponent

The global nature of CSS and DOM impedes componentization.

WebComponent = Custom Element + Shadow DOM + HTML Templates.

Concrete implementation steps

  1. Use the template tag to define templates. Includes style, structure, and behavior.
  2. Create a class (inherited from HTMLElement), get the component template, create a shadow DOM node and add the template.
  3. Use customElements. Define to customize the element, and then you can use it just as you would an HTML element.
<template id="geekbang-t">
  <style>
    p {
      background-color: brown;
      color: cornsilk;
    }
    div {
      width: 200px;
      background-color: bisque;
      border: 3px solid chocolate;
      border-radius: 10px;
    }
  </style>
  <div>
    <p>time.geekbang.org</p>
    <p>time1.geekbang.org</p>
  </div>
  <script>
    function foo() {}
  </script>
</template>

<script>
  class GeekBang extends HTMLElement {
    constructor() {
      super(a);const content = document.querySelector("#geekbang-t").content;
      const shadowDOM = this.attachShadow({ mode: "open" });
      shadowDOM.appendChild(content.cloneNode(true));
    }
  }
  customElements.define("geek-bang", GeekBang);
</script>

<geek-bang></geek-bang>
Copy the code

The shadow of the DOM

Shadow DOM, which isolates the content of templates from the global DOM and CSS, privatizing elements and styles. DOM internal styles do not affect global CSSOM, and elements inside the shadow DOM cannot be queried directly using the DOM interface.

Browsers implement shadow DOM features by adding a lot of conditional judgments to their code.

  • When an element is searched through the DOM interface, the rendering engine finds the shadow-root element, determines that it is a shadow DOM, and directly skips the query operation of the shadow-root element.
  • When generating the layout tree, the rendering engine determines that shadow-root is a shadow DOM and uses the CSS properties inside the shadow DOM directly.

series

One of the principles of the browser: look at it in general

Browser principle two: JS, V8