Hello everyone, I'm Casson.
In a recent WWC22, builder.io
CTO miško hevery (and inventor of Angular
/ AngularJS
) gave an imaginative speech.
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:
- Download framework
JS
code - Execution framework
JS
code - 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:
- First measure
FCP
time - Bind events to elements in the page
- 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:
Qwik
中React
的useEffect
,但在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:
- 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 - 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?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。