Skip to content

Commit e540981

Browse files
docs: how to delay Qwik core execution (#8041)
* doc: add document for loading core * use mdx suffix * fix typo * update after review * move to best-practices
1 parent f080be0 commit e540981

File tree

1 file changed

+142
-1
lines changed
  • packages/docs/src/routes/docs/(qwikcity)/guides/best-practices

1 file changed

+142
-1
lines changed

packages/docs/src/routes/docs/(qwikcity)/guides/best-practices/index.mdx

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ useVisibleTask$(({ cleanup }) => {
140140
});
141141
```
142142

143-
The above implementation causes more JavaScript to load eagerly, rather than responding precisely to user events. Increased upfront JavaScript loading results in slower app performance.
143+
The above implementation causes more JavaScript to load eagerly, rather than responding precisely to user events. Increased upfront JavaScript loading results in slower app performance. See [below](#delaying-core-execution) for more details.
144144

145145
Instead, use the `useOnDocument()` hook to register events on the `document` object, this way Qwik will not execute any JS until the event is triggered.
146146

@@ -198,3 +198,144 @@ However, exercise caution! If the required information (such as query parameters
198198
This approach helps to prevent eager loading of JavaScript and improves performance.
199199

200200
> See: [useLocation() Docs](/docs/(qwikcity)/api/index.mdx#uselocation)
201+
202+
203+
## Delaying Core Execution
204+
At load time we want to execute as little as possible to free main thread for other priority. With Qwik you can even delay the framework execution (called "core"), but there are some rules to follow.
205+
206+
Some things to keep in mind before checking the rules :
207+
- Delaying core execution usually comes at the price of devX, this is an advance performance trick.
208+
- Like other chunks, core will be preloaded so the app doesn't have to load it at execution time.
209+
210+
### useVisibleTask$
211+
212+
`useVisibleTask$` will **always** execute core before its callback is called.
213+
214+
```jsx
215+
// Requires core when component is visible in the viewport
216+
useVisibleTask$(() => {
217+
console.log('Hello core');
218+
});
219+
220+
// Requires core on requestIdleCallback
221+
useVisibleTask$(
222+
() => console.log('Hello core'),
223+
{ strategy: 'document-idle' }
224+
);
225+
```
226+
227+
In **some** cases you can replace `useVisibleTask$` with either `useOn` or `useTask$`
228+
229+
#### useOn
230+
231+
Replace `useVisibleTask$` with `useOn('qvisible')` / `useOn('qidle')` if
232+
233+
- You only need to trigger a callback once
234+
- The code must run on the client
235+
236+
`useOn`, `useOnDocument` & `useOnWindow` execute core if they use a variable from the component scope :
237+
238+
```jsx title="Comparing core execution"
239+
import { libId } from 'library';
240+
const globalId = 'global-id';
241+
const Component = component$(() => {
242+
const ref = useSignal();
243+
const id = useId();
244+
245+
// Executes core at load time
246+
useOnDocument('qidle', $(() => console.log(ref)));
247+
// Executes core at load time
248+
useOnDocument('qidle', $(() => console.log(id)));
249+
// Does not execute core at load time
250+
useOnDocument('qidle', $(() => console.log(globalId)));
251+
// Does not execute core at load time
252+
useOnDocument('qidle', $(() => console.log(libId)));
253+
// Does not execute core at load time
254+
useOnDocument('qidle', $(() => console.log('id')));
255+
256+
return (
257+
<p ref={ref}></p>
258+
<p id={id}></p>
259+
<p id={globalId}></p>
260+
<p id={libId}></p>
261+
<p id="id"></p>
262+
)
263+
})
264+
```
265+
266+
#### useTask$
267+
268+
Replace `useVisibleTask$` with `useTask$` if
269+
270+
- You need to listen on state changes
271+
- The code can execute on the server
272+
273+
```jsx
274+
const Component = component$(() => {
275+
const search = useSignal();
276+
// Does not execute until `search` changes
277+
useTask$(({ track }) => {
278+
track(search);
279+
console.log(search.value);
280+
});
281+
return <input bind:value={search} type="search" />;
282+
});
283+
```
284+
285+
#### useOn + useTask$
286+
287+
A classic usecase of `useVisibleTask$` is to start listening on browser specific event:
288+
289+
```jsx
290+
const isMobile = useSignal(false);
291+
useVisibleTask$(({ cleanup }) => {
292+
const query = window.matchMedia('(max-width: 400px)');
293+
const handler = (event) => {
294+
isMobile.value = event.matches;
295+
};
296+
query.addEventListener('change', handler);
297+
cleanup(() => query.removeEventListener('change', handler));
298+
});
299+
```
300+
301+
In this case we actually need core when the handler is triggered. Here is how to delay core execution :
302+
303+
```jsx
304+
const isMobile = useSignal(false);
305+
// On idle, start listening on the event
306+
useOnDocument(
307+
'qidle',
308+
sync$(() => {
309+
const query = window.matchMedia('(max-width: 400px)');
310+
const handler = (event) => {
311+
// Forward the event to the document
312+
const copy = new event.constructor('media-query:(max-width: 400px)', event);
313+
document.dispatchEvent(copy);
314+
};
315+
// Store mediaQuery & handler to cleanup later
316+
document['cleanup:media-query:(max-width: 400px)'] = () => {
317+
query.removeEventListener('change', handler)
318+
};
319+
query.addEventListener('change', handler);
320+
})
321+
);
322+
323+
// useOnDocument execute core when it actually needs it
324+
useOnDocument(
325+
'media-query:(max-width: 400px)',
326+
$((event) => {
327+
isMobile.value = event.matches;
328+
})
329+
);
330+
331+
// useTask$ is used to cleanup event listeners
332+
useTask$(({ cleanup }) => {
333+
cleanup(() => {
334+
if (!document['cleanup:media-query:(max-width: 400px)']) return;
335+
document['cleanup:media-query:(max-width: 400px)']();
336+
delete document['cleanup:media-query:(max-width: 400px)'];
337+
});
338+
});
339+
```
340+
341+
As we can see, this is a LOT of work, and it's not a great dev experience ! But if you're building a library or just trying to go as lean as possible, this is possible.

0 commit comments

Comments
 (0)