Contents
- 1 1. Prerequisite knowledge: ssr (you can understand Qwik only if you understand here)
- 2 2. What can be optimized in the ssr process
- 3 3. What is Qwik
- 4 Fourth, the initialization project
- 5 Five, hooks
- 6 6. There is a big pit in the click event
- 7 Seven, code template to help
- 8 8. Is the reform of usage really good?
- 9 9. What is $cached?
- 10 10. The cache is partitioned: the Host tag
- 11 11. How does Qwik handle latency? prefetch
- 12 12. Recoverability
- 13 Thirteen, delay loading demonstration, buried points and other events
- 14 14. My afterthought
- 15 15. My thoughts
1. Prerequisite knowledge: ssr (you can understand Qwik only if you understand here)
From the beginning of learning front-end development, we continue to learn various front-end optimization methods to improve the performance of front-end code, among which “server-side rendering (ssr)” mode helps us greatly improve the first screen of projects developed using front-end frameworks performance, so what is the workflow of ssr? Let’s briefly sort it out.
The first step: server-side splicing html
When a user requests a page, the server side will stitch together the html structure of a page and return it to the client, such as the following structure:
<!DOCTYPE html> <head> <title>Document</title> </head> <body> <div id="App"> <button>点击弹出: hello</button> <ul> <li>1</li> <li>2</li> </ul> </div> <script src="/_ssr/2046328.js" defer></script> </body> </html>
Step 2: The html loaded by the client is displayed
As can be seen from the above code, html
structure can be displayed after loading, but for example, click on leave, such interaction events still do not exist, you need to load /_ssr/2046328.js
before the page can interact (Live), so we still have to request a bunch of js
files to the local.
Step 3: js can interact only after the hydration stage is completed
2. What can be optimized in the ssr process
How do you feel after reading the above ssr process? Do you feel that ssr may be a “visual liar”, we simply list a few points that can be optimized:
- Although the display speed of the first screen is faster, it is not interactive, so his tti (page interactive time) is not much optimized, but it is undeniable that there is an improvement, but not too much.
- The downloaded js is still a relatively full amount of js code.
- When the js code is executed, a lot of logic still needs to be processed, and the DOM on the page must be reprocessed.
In 2020, the project I was in charge of was built using ssr
technology. The speed of the first screen has indeed improved, but the disadvantage is that it consumes more server resources, and maintenance costs go up, such as occasional memory leaks, and Every time I update the code, I need to manually execute some commands on the server (the team pipeline was not perfect at that time), which gave me a direct feeling at the time that the battle was quite big and the benefits were a little small.
3. What is Qwik
Qwik
can be understood as a front-end ssr
frame that has a syntax close to react
, but is more radical than the traditional ssr
- Greatly optimized or even canceled the hydration process
- Not only lazy loading components, but also code such as click events can be lazy loaded
- Almost can be done, only load the currently used js code and css code
- If the dom element does not appear in the visual area of the screen, the internal method of the component is not executed
Qwik
The goal is to delay loading all the code, such as a button before you click it, then Qwik
will not load the click-related logic, and even he will not load react Relevant code, after all, sometimes the user does not perform any operation after entering the page, so we do not need to load all the resources.
Of course, after reading the above description, you will feel that using Qwik
will make the operation lag, take the ticket with questions and let us study it in depth.
Fourth, the initialization project
Briefly explain the installation and basic usage, so that everyone will have a clearer concept in their minds, but I feel that the official website is well written, so please go to the qwik official website for detailed usage.
Initialize the project
The following command is the command to create the project:
npm init qwik@latest
The first time I used this command, I was stunned, because I only used npm init
to initialize an empty project, at best I used npm init -y
this way, but I checked the official website and found out that the original You can also use it like this:
// 原命令 npm init qwik@latest // 相当于 npx create-qwik // 要注意, 不是 npx qwik@latest/create
So it can be seen that we can directly npm install create-qwik -g
and then create-qwik
to initialize the project in the same way:
The specific capabilities of the options are not the focus of this article. This time, we will first experience the overall experience Qwik
we will have the opportunity to deduct the details later.
start up
Startup during development
npm install npm start
Startup after packaging
npm run build npm run serve
click event
Since the whole is almost the same as react
, let’s get straight to the point and first look at how to define a component and define its click event:
import { component$, useStore } from "@builder.io/qwik"; export const Home = component$(() => { const state = useStore({ count: 0, }); return ( <button onClick$={() => (state.count += 1)}>home组件: {state.count}</button> ); });
We can find that the component is generated by a component$
function. With this function, the component can be an asynchronous component, that is, when the component is not used on the user’s screen, the relevant code of this component will not be loaded.
onClick$
This name also has a $
, the meaning is similar, that is, when we do not trigger the click event, we will not download the code of the click event, which is very detailed.
Five, hooks
useStore defines variables
The way to define variables is different from react
:
const state = useStore({ count: 0, name: '金毛cc' });
Modify the value directly on the body, state
state.name = '被修改啦'
Suddenly there is a feeling of writing vue
.
useServerMount$
Register a server mount hook that only runs in the server when the component is first mounted.
This hook only runs on the server side and is written as follows:
useServerMount$(async () => { console.log("什么时候执行: useServerMount$"); const n: number = await new Promise((resolve) => { setTimeout(() => { resolve(9); }, 3000); }); state.count = n; });
The printed text cannot be seen in the browser, and can be viewed in the vscode console during development:
useClientEffect$ to monitor the visibility of elements
Only when rendering on the client side, of course there are corresponding lifecycle methods:
useClientEffect$(() => { console.log("初始化: useClientEffect$"); });
This hook can monitor whether the component is displayed on the screen, that is to say, it is only executed when the component can be seen by the user, so let’s experiment. We push the home component out of the screen and observe whether useClientEffect$ is executed:
<Host> <h1 style={{ marginBottom:'1200px'}}>Welcome to QwikCity</h1> <Home></Home> </Host>
But when we scroll we show the home component:
In fact, he uses IntersectionObserver
this method monitors the state of dom
, so if some of our components need to display the requested data, then we can display the requested data when this component appears on the screen Please request again later.
The reason why he can provide such a method is because of the characteristics of the Qwik
framework, and we will understand it when we talk about the Host
component later.
Changes in useWatch$ subscription value (with big pits)
Let’s write that whenever count
changes, it will trigger this watch
:
useWatch$((track) => { const count = track(store, 'count'); store.doubleCount = 2 * count; });
There is a big hole here, that is, when your component code has useServerMount
method and it is below useWatch
, useWatch
can only be executed once, that is, only once It is executed once at the server
end, and will not be executed subsequently.
Here is the wrong usage:
// 错误示范 // 书写在上方 useWatch$((track) => { const count = track(state, "count"); state.doubleCount = count + 2; }); // 书写在下方 useServerMount$(async () => { const n: number = await new Promise((resolve) => { setTimeout(() => { resolve(9); }, 500); }); state.count = n; });
So when we need to continuously monitor the change of a certain value, we need to put useWatch
useServerMount$
:
// 正确写法 // 书写在上方 useServerMount$(async () => { const n: number = await new Promise((resolve) => { setTimeout(() => { resolve(9); }, 500); }); state.count = n; }); // 书写在下方 useWatch$((track) => { const count = track(state, "count"); state.doubleCount = count + 2; });
6. There is a big pit in the click event
After reading the official website of qwik
from the beginning, I found that all the examples he gave were inline functions, as in the picture:
<button onClick$={()=>state.count += 1}> home组件: {state.count} </button>
But in fact, we usually use the following form:
const handleClick = ()=>{ state.count += 1 } return <button onClick$={handleClick}> home组件: {state.count} </button>
Good guy, I just called good guy, is this not letting me reuse the method? There is no way I didn’t try it successfully, and finally I had to change to the following form:
The meaning here is that this function is not serializable, so it cannot be used, and I thought that as long as the serializable method can be placed here? There is the following third way of writing:
It won’t work if you put it in the scope of the component, then I put it outside the component, but the following error is still reported:
But unless we export the method, there will be no error:
So at least the methods imported from the outside world can still be used. The methods in the current component scope can only be written in the inline method of dom
, and the key points are these bug
in his There is no detailed description in the official website documentation, and it is all up to the developers to explore by themselves, which makes my experience very poor.
Seven, code template to help
Qwik
component code itself is a bit special, so he also provides several code templates to help users generate code, in the qwik.code-snippets
file of vscode:
use:
8. Is the reform of usage really good?
From the various usages of the Qwik
framework, it can be seen that their team’s ambitions, and the official website also mentioned why they did not use the grammar of react
, and the reason they gave react
The current architecture cannot achieve the desired effect Qwik
, so it can only be achieved by overturning and refactoring Qwik
.
But all the difficulties they mentioned are problems encountered in the realization of the ‘process’, and the final usage should belong to the ‘result’, in the case of not being 10 times better, why should developers learn the writing method of heart? And These spellings are still full of bugs.
Of course, all innovations are worthy of encouragement. Even a little change may change this monotonous world, but if you are uncomfortable with it, you can say it generously.
9. What is $cached?
We briefly introduced the usage above, then we will mainly talk about the principle, taking the dimension of click event as an example, when we define a click event on button
, then the compiled The structure is like this:
It can be seen that on:click
event corresponds to a string of 字符串
, why is the click event not a function?
In fact, this is because of the click event mechanism of Qwik
8c4cb4b996367d9696b6cedb91ccf8f0—, first Qwik
will monitor the click event globally, and then when a dom is clicked Qwik
will detect the body of the dom Whether there is an onclick event and read the corresponding string, then load the corresponding file according to the address of the string, and execute the corresponding method after loading the file.
So how did we write click事件
converted into a string? Here is a concept called ”:
// 转换前 <button onClick$={() => { state.count += 1; }} // 转换后 <button onClick$={qrl('./chunk-c.js', 'Home_onClick', [store, props])}
So it can be understood as qrl
method is specially responsible for converting some logic to the corresponding js文件
method, so here we understand why the component onClick
are so many restrictions on how events are written, because these logics, such as compliance, can be abstracted into 独立的js文件
. If they cannot be abstracted, asynchronous loading cannot be achieved.
10. The cache is partitioned: the Host tag
Host
The label is the outermost layer of almost every component, that is, as shown in the figure below, any component should be wrapped Host
:
return ( <Host> ....// </Host> );
The reason for an extra layer of wrapping Host
Let’s take a look at the explanation given by the official website:
Host elements are used to mark component boundaries. Without a host element, Qwik would not know where the component starts and ends. This information is required so that components can render independently and out of order, which is a key feature of Qwik.
I have written several articles about react-keep-alive
react
dom
and I have some understanding of this aspect. Inserting sub-components is not responsive, and the specifics are not clear in one or two sentences. You can read my previous article: Some knowledge about the keep-alive function of react is here
Since the official says ‘must have a Host’ tag, it means that each component of our rendering will have an extra layer dom
, since it cannot be avoided, use it as much as possible, first we can specify Host
what is the tag dom
attributes:
Note that tagName
is the second parameter of the component$
method:
So now why do you Qwik
inside, you can use useClientEffect$
method to monitor whether the component is in the visible area, because the outer layer of the component basically has a Host
-Element, so even the following writing can detect the explicitness and concealment Host
the element:
<Host> <> <div>1</div> <div>2</div> <div>3</div> </> <div>4</div> </Host>
11. How does Qwik handle latency? prefetch
I have always had a question when I first looked at this framework, the delayed loading of click events may cause 卡顿
, at least it also increases the processing time of click events, in case the code of this click event is a bit large And the user’s network is not good, isn’t it a cool song?
Qwik
The team has of course thought about these issues, at least I was convinced by the reasons they gave, the English version of the official website is a bit difficult to understand, so I will explain it in my language:
ssr
框架是需要js文件
的, js
文件加载完毕注水
完毕才是页面可交互, 串行
的, Qwik
, click
字符串
-Then its rendering speed is of course fast, and at the same time 字符串
Qwik
will be turned on webWorker
The prefetching of the code happens on other threads than the main thread, and not all at once, but The code of several components currently in use, so that as long as the click event is monitored to request a certain file in the future, then webWorker
will pass the corresponding file directly without requesting the network.
And Qwik
also shows that: Loading js文件
may take more time than 执行js逻辑
.
And it is not one js文件
there is only one method, but multiple related methods will be used. For example, if a click event is triggered, other methods will also be loaded, so there is no need to load them one by one.
12. Recoverability
可恢复性
is a signature concept launched by Qwik
, we will discuss this feature from three aspects:
- Reduce
注水
: As I said before, set the click listener event globally, so that you don’t have to load the component every time注水
to interact. - Component tree: In the traditional
ssr
mode, after the server rendering is completed, there may be somedom
the structure has changed, and you need to reQwik
注水
Qwik
It is possible to rebuild the component hierarchy when the component code does not actually exist, and the component code can remain inert. My understanding here isHost
The credit of the component, such as a certain The position of the dom has been adjusted, so just adjustHost
. Qwik
allows to restore any component without parent component code, what I understand is the currentssr
the framework needs the parent component to create the child component, butQwik
lot of states are built in, so you can do independent delayed rendering. For example, A is a parent component and a is a child component, then when I load the a component, I don’t need to load all the A componentjs
Logic.
Thirteen, delay loading demonstration, buried points and other events
Next, let’s see when it will load react代码
, because the source code of react
is still a bit slow to run, the following figure shows a page request without any interaction Record:
It can be seen that the loaded file is very small. At the first sight I thought it did not depend on react
, when we clicked the button:
Download after the click event is triggered core.js
It should be noted here that theoretically everything that needs to use the hooks of react
will trigger the loading of this file. For example, if there is useClientEffect$
in the code, then it will be downloaded when it is executed core.js
.
But executing useServerMount$
this kind of server execution hooks
will not trigger loading core.js
, so everyone knows how to write more efficiently!!
14. My afterthought
There are still a lot of problems in use. Although the official website has written a lot of content, there are still too few specific examples, and the examples are not in place. They are just perfect examples under ordinary conditions. .
And it may be because the official website has not been updated very much. Some methods I pasted can’t be used, and I still need to study for a long time…
I am not in favor of introducing a set of syntax that is quite different from react
to developers, and the benefits of changing the syntax are not great.
15. My thoughts
This framework has also brought me a lot of thinking. It can take so many subtle points to the extreme and optimize each line of logic into your logic. Then I also have some of my suggestions:
- Can developers specify which events need to be asynchronous at will, such as launching
onClick
andonClick$
two methods to distinguish whether the code needs to be loaded asynchronously. - Whether it is possible to make a click event trigger record for each user, for example, a certain user often triggers certain events, then theoretically, the codes of these events can be preloaded first, and statistics can be performed by burying the click events.
- Put the frequently loaded
js
file on the server with better performance or closerCDN
, that is, differential deployment.
From the framework itself, inspiration for actual business:
- Can we focus on each
click
event by burying it, such as recording the top ten click events every week, and then start focusing on optimization along these events. 注水
that is, interactivity, whether it is really important, for example, whether some pages come in and just look at them and leave, there is nothing to usereact
the place of ability , then do we load a file likecore.js
without coming in.- Encapsulate a component similar to the
Host
component, so that the component within it can appear in the visible area before loading the component. - There may be multiple child components inside a parent component, but there may be only one child component most commonly used, so is it possible to lazy load the parent component and the rest of the child components?