23
头图

Hello everyone, I'm Casson.

In a recent WWC22, builder.io CTO miško hevery (and inventor of Angular / AngularJS ) gave an imaginative speech.

miško hevery

In his speech, he introduced a full-stack SSR framework -- Qwik , which claims to be able to help you remove 99% of the JS code in your project .

How he did it, in this article we will introduce Qwik .

Welcome to join the human high-quality front-end framework group , with flying

Poor performance? Code farmers do not take the blame

Let's talk about the background of the birth of Qwik .

For many 2C web applications (such as e-commerce), the performance indicators of the first screen are related to user retention, and user retention is related to how much money is earned.

Therefore, the opening speed of the application will affect the money making.

However, for front-end developers, above-the-fold performance metrics are not easy to optimize. The reason is not that the developers are not working hard enough.

Let's look at two performance metrics.

How to optimize FCP

FCP (First Contentful Paint) measures the time it takes for a page to finish rendering on the screen from the start of loading to any part of the page's content .

Currently web applications are generally developed with front-end frameworks , which means that a large number of JS codes will be introduced (the code of the framework itself, the code of third-party dependent packages...)

From HTML to parsing to the final page rendering, we have to go through:

  1. Download framework JS code
  2. Execution framework JS code
  3. The page rendering is done by the framework

This results in a drop in the FCP indicator.

In order to optimize FCP , the framework author proposed SSR (Server Side Render, server side rendering), which is required to generate the first screen on the server side HTML , which is FCP saves the time required for the above three steps.

However, the TTI indicator still needs to be optimized.

How to optimize TTI

TTI (Time to Interactive) measures the time it takes for a page to become fully interactive .

The main measure is the time required to go from 1 to 3 below:

  1. First measure FCP time
  2. Bind events to elements in the page
  3. After interacting with the element, the event response time is within 50ms

After using SSR , although FCP is reduced, the time required for the framework hydrate (water injection, that is, the framework enables the page to respond to interactions) TTI There will be an impact.

It can be seen that the source of the performance bottleneck lies in the JS code.

React18 Selective Hydration Optimized TTI metric by letting the user interact with the part prior to hydrate.

However, Qwik is more extreme, his goal is to kill all unnecessary JS time-consuming, and the time-consuming here consists of two parts:

  • JS -consuming to load as a static resource
  • JS Runtime time

Ultra-ultra-fine-grained hydrate

If we say traditional SSR the granularity is the whole page .

Then the granularity of React18 of Selective Hydration is the component that produces the interaction .

Then the granularity of Qwik is a method in the component .

For example, here is the HelloWorld component (it can be found that Qwik uses a syntax similar to React ):

The corresponding page rendering effect:

Open the browser Network panel, how many requests will this page have JS requests?

Since this is a static component with no logic, the answer is: no JS request.

Let's take a look at the classic counter Counter component. Compared with HelloWorld , the logic of the state change of the click button is added. The code is as follows:

The corresponding page rendering effect:

Open the browser Network panel, how many JS requests will this page have?

The answer remains: no JS request.

Note that in the code of these two components, the definition component is component$ , and there is a $ symbol.

In Counter , the onClick$ callback also has a $ symbol.

In Qwik , the functions with the suffix $ are lazy loaded .

The granularity of hydrate e06f8e6638185ba2a05a00edc5c52070--- depends on how finely $ is defined.

For example, in Counter , onClick$ with $ suffix, then the click callback is lazy loaded, so the first screen rendering will not include the logic corresponding JS the click- JS code.

After clicking the button, two JS requests will be initiated. The first request returns the logic after the click :

The second one JS request returns the logic of component re-render :

After these two pieces of code are executed, Counter becomes 1.

Inspecting the element will find that before clicking, the button on:click attribute holds the address where the logic is located :

After clicking, it will download JS code from the corresponding address, and execute the corresponding logic.

From excellent to extreme

Do you feel that it has been optimized to the extreme? not yet.

For some modules (such as carousels) that have long existed in the page and need to be driven by JS , it is not necessary for the module to correspond to JS before the module is displayed.

For example, in the example of the clock below, there is a long list on the page, more than one screen high, and there is a clock at the bottom of the list.

Here's how the list scrolls to the bottom:

In the Clock component's useClientEffect$ the logic of the clock pointer swing is defined:

QwikReactuseEffect ,但在Qwik中这个---ef611c1a87289af5fe50062f9a81368f Hook服务端/ Client executes.

To differentiate, useClientEffect is a useEffect that is executed only on the client side .

The $ suffix is added, which means that it is lazy loaded .

The specific effect is: when the page scrolls until the clock is exposed, the useClientEffect$ corresponding to JS code will not be requested.

When the clock is exposed, two JS resource requests will be initiated:

  • Logic of useClientEffect
  • Clock Component re-rendering logic

If you inspect the element, the element corresponding to the pointer will not move until the clock is exposed:

When the clock is exposed, the JS code is loaded and executed, and the animation starts:

hydrate the data

In legacy SSR , the data is actually initialized twice:

  1. The page is rendered for the first time. At this time, the data exported by the server HTML already carries the first screen rendering data
  2. After the frame hydrate , the data is converted into the state in the frame for subsequent rendering

In Qwik , when the page is initialized, there will be type for qwik/json script 641be9c0ec2dea174f5c659e659e659e659e64 Data :

What is being activated ?

For example, the following is the comment area of an article, this is what it looks like after the first screen rendering:

Will this comment data appear in the qwik/json saved data?

No, because there is no interaction to activate them.

We found that one of the comments was collapsed, and clicked to expand the comment:

Clicking on this action will request:

  • Click the logic corresponding to JS code
  • This comment corresponds to the re-rendering logic of the component

At this time, the comment data will only appear in qwik/json , because the click interaction activates this data.

So in Qwik the data is not initialized twice if it is not necessary.

There is inactive data in HTML qwik/json , and the script tag of ---60fc54172afceb4b0981a7aa86503cb5--- saves the activated data , this feature will bring a very interesting effect:

After copying the DOM structure under the Elements panel in the debugging tool, and then pasting it in a new page, the current interactive state of the page can be reproduced (for example, the previously entered content is still retained in the input box):

复制红框内的内容

Changing to other frameworks can only reproduce the initial state of the page .

Will it not get stuck when requesting JS again during interaction?

Some students may ask, if the network is not good, if the request is made during the interaction JS will the code not make the interaction lag?

Qwik Allows you to specify which components are likely to be operated by users (for example, in e-commerce applications, the shopping cart button has a high probability of being clicked).

The logic of these components corresponds to JS the code will be prefetch , which is pre-requested without affecting the rendering of the first screen:

And the order of these components prefetch can be adjusted.

This means that user behavior can be tracked, and the frequency of user interaction can be used as an indicator, as the basis for the priority of components prefetch , and heuristically improve application performance.

This is true user-oriented performance optimization, and it's fully automatic.

Summarize

Today is an era of front-end frameworks blooming, and different frameworks are looking for their own unique selling points.

The selling point of Qwik is: splitting JS code from common compile time (such as webpack block), runtime (such as dynamic import ), when it becomes interactive .

For JS the ultimate splitting of the code, only to achieve one purpose - to remove 99% of the JS code in your project when rendering above the fold.

What do you think of this move?


卡颂
3.1k 声望16.7k 粉丝