Tasks & Lifecycle === # Tasks :::spoiler ... Tasks are meant for running asynchronous operations as part of component initialization or change of component state. > Note: Tasks are similar to useEffect() in React, but there are enough differences that we did not want to call them the same so as not to bring preexisting expectations about how they work. The main differences are: * Tasks are asynchronous. * Task run on server and browser. * Tasks run before rendering and can block rendering. `useTask$()` should be your default go-to API for running asynchronous (or synchronous) work as part of component initialization or state change. It is only when you can't achieve what you need with `useTask$()` that you should consider using `useVisibleTask$()` or `useResource$()`. The basic use case for `useTask$()` is to perform work on component initialization. `useTask$()` has these properties: * It runs on the server and the browser. * It runs before rendering and blocks rendering. * If multiple tasks are running then they are run sequentially in the order they were registered. An asynchronous task will block the next task from running until it completes. Tasks can also be used to perform work when a component state changes. In this case, the task will rerun every time the tracked state changes. See: `track()`. Sometimes a task needs to run only on the browser and after rendering, in that case, you should use `useVisibleTask$()`. Sometimes a task should fetch data asynchronously and produce a signal (and not block rendering), in that case, you should use `useResource$()`. ::: #### 任務用於運行異步操作作為組件初始化或組件狀態更改的一部分。 > 注意:任務與 React 中的 `useEffect()` 類似,但存在足夠多的差異,因此我們不想對它們進行相同的稱呼,以免對它們的工作方式帶來預先存在的期望。 主要區別是: * 任務是異步的。 * 任務在服務器和瀏覽器上運行。 * 任務在渲染之前運行並且可以阻止渲染。 ## useTask$() 默認首選 API `useTask$()` 應該是作為組件初始化或狀態更改的一部分運行異步(或同步)工作的默認首選 API。 只有當您無法使用 `useTask$()` 實現所需的內容時,才應考慮使用`useVisibleTask$()` 或 `useResource$()` 。 `useTask$()` 的基本用例是執行組件初始化工作。 `useTask$()` 具有以下屬性: * 它運行在服務器和瀏覽器上。 * 它在渲染之前運行並阻止渲染。 * 如果多個任務正在運行,那麼它們將按照它們註冊的順序依次運行。 異步任務將阻止下一個任務運行,直到它完成。 任務也可用於在組件狀態更改時執行工作。 在這種情況下,每次跟踪的狀態發生變化時,任務都會重新運行。 請參閱:`track()`。 有時任務只需要在瀏覽器上運行並在渲染後運行,在這種情況下,您應該使用 `useVisibleTask$()` 。 有時任務應該異步獲取數據並產生信號(而不是阻塞渲染),在這種情況下,您應該使用 `useResource$()` 。 # Lifecycle 元件生命週期鉤子 https://qwik.builder.io/docs/components/tasks/#lifecycle :::spoiler ... Thanks to Resumability, component lifetime and its lifecycle extend across server and browser runtime. Sometimes the component will first be rendered in the server, and other times it could be rendered in the browser. However, in both cases, the lifecycle (order) will be the same, only its execution location will be in different environments (server vs browser). ::: 由於可恢復性,組件生命週期及其生命週期擴展到服務器和瀏覽器運行時。 有時組件會首先在服務器中呈現,有時它可能會在瀏覽器中呈現。 然而,在這兩種情況下,生命週期(順序)將是相同的,只是它的執行位置將在不同的環境中(服務器server vs 瀏覽器browser)。 :::spoiler ... > Note: For systems that use hydration the execution of the application happens twice. Once on a server (SSR/SSG) and once on the browser (hydration). For this reason, many frameworks have "effects" which only execute on the browser. That means that the code that runs on the server is different than the code that runs on the browser. Qwik execution is unified, meaning if the code has already been executed on the server it does not re-execute it on the browser. ::: >注意:對於使用 hydration 的系統,應用程序會執行兩次。 一次在服務器 (SSR/SSG) 上,一次在瀏覽器上 (hydration)。 > 出於這個原因,許多框架都有隻在瀏覽器上執行的“效果”。 這意味著在服務器上運行的代碼與在瀏覽器上運行的代碼不同。 > Qwik 執行是統一的,這意味著如果代碼已經在服務器上執行過,它不會在瀏覽器上重新執行。 :::spoiler ... ## In Qwik, there are only 3 lifecycle stages: Task - run before rendering and also when tracked state changes. (Tasks run sequentially, and block rendering.) Render - runs after TASK and before VisibleTask VisibleTask - runs after Render and when the component becomes visible ::: ## 在 Qwik 中,只有 3 個生命週期階段: 1. Task:任務 - 在渲染之前運行,也在跟踪狀態更改時運行。 (任務按順序運行,並阻止渲染。) 2. Render:渲染 - 在 TASK 之後和 VisibleTask 之前運行 3. VisibleTask:可見任務 - 在 Render 之後和組件變得可見時運行 ``` useTask$ -------> RENDER ---> useVisibleTask$ | | --- SERVER or BROWSER --- | ----- BROWSER ----- | | pause|resume ``` :::spoiler ... SERVER: Usually the life of a component starts on the server (during SSR or SSG), in that case, the useTask$ and RENDER will run in the server, and then the VisibleTask will run in the browser, after the component is visible. Notice that because the component was mounted in the server, only useVisibleTask$() runs in the browser. This is because the browser continues the same lifecycle, that was paused in the server right after the render and resumed in the browser. BROWSER: Sometimes a component will be first mounted/rendered in the browser, for example when the user SPA navigates to a new page, or a "modal" component first appears in the page. In that case, the lifecycle will run like this: useTask$ --> RENDER --> useVisibleTask$ | -------------- BROWSER --------------- | Notice that the lifecycle is exactly the same, but this time all the hooks run in the browser, and not in the server. ::: SERVER:通常組件的生命週期從服務器開始(在 SSR 或 SSG 期間),在這種情況下,useTask$ 和 RENDER 將在服務器中運行,然後 VisibleTask 將在組件可見後在瀏覽器中運行。 請注意,因為該組件已安裝在服務器中,所以只有 useVisibleTask$() 在瀏覽器中運行。 這是因為瀏覽器繼續相同的生命週期,即在渲染後立即在服務器中暫停並在瀏覽器中恢復。 BROWSER:有時組件會首先在瀏覽器中掛載/呈現,例如當用戶 SPA 導航到新頁面時,或者“模態”組件首次出現在頁面中時。 在這種情況下,生命週期將像這樣運行: `useTask$ --> RENDER --> useVisibleTask$` | -------------- 瀏覽器 -------------- | 請注意,生命週期完全相同,但這次所有掛鉤都在瀏覽器中運行,而不是在服務器中運行。 :::spoiler ... # useTask$() When: BEFORE component's first render, and when tracked state changes Times: at least once Platform: server and browser useTask$() registers a hook to be executed upon component creation, it will run at least once either in the server or in the browser, depending on where the component is initially rendered. Additionally, this task can be reactive and will re-execute when tracked state changes. Notice that any subsequent re-execution of the task will always happen in the browser, because reactivity is a browser-only thing. ::: ``` (state change) -> (re-execute) ^ | | v useTask$(track) -> RENDER -> CLICK -> useTask$(track) ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ | ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ | ----- SERVER ------ | ----------- BROWSER ----------- | | pause|resume ``` :::spoiler ... > If`useTask$()`does not track any state, it will run exactly once, either in the server or in the browser (not both), depending where the component is initially rendered. Effectively behaving like an "on-mount" hook. > useTask$() will block the rendering of the component until after its async callback resolves, in other words, tasks execute sequentially even if they are asynchronous. (Only one task executes at a time / Tasks block rendering). Let's look at the simplest use case of the task to run some asynchronous work on component initialization. ::: # useTask$() 1. 何時:在組件首次渲染之前,以及跟踪狀態更改時 2. 幾次:至少一次 3. 平台:服務器和瀏覽器 > 如果`useTask$()`不跟踪任何狀態,它將僅在服務器或瀏覽器(不是兩者)中運行一次,具體取決於組件最初呈現的位置。 有效地表現得像一個“掛載”掛鉤。 useTask$() 註冊了一個在組件創建時執行的鉤子,它將在服務器或瀏覽器中至少運行一次,具體取決於組件最初呈現的位置。 此外,此任務可以是被動的,並且會在跟踪的狀態更改時重新執行。 請注意,任務的任何後續重新執行都將始終在瀏覽器中發生,因為反應性是瀏覽器獨有的事情。 讓我們看一下在組件初始化上運行一些異步工作的任務的最簡單用例。 ```javascript= import { component$, useSignal, useTask$ } from '@builder.io/qwik'; export default component$(() => { const fibonacci = useSignal<number[]>(); useTask$(async () => { const size = 40; const array = []; array.push(0, 1); for (let i = array.length; i < size; i++) { array.push(array[i - 1] + array[i - 2]); await delay(100); } fibonacci.value = array; }); return <div>{fibonacci.value?.join(', ')}</div>; }); const delay = (time: number) => new Promise((res) => setTimeout(res, time)); ``` ``` 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986 ``` :::spoiler ... In this example >The `useTask$()`computes the fibonacci number one entry per 100 ms. So 40 entries take 4 seconds to render. The `useTask$()` executes on the server as part of the SSR (the result may be cached in CDN.) Because the`useTask$()`blocks rendering, the rendered HTML page takes 4 seconds to render. Because this task has no track() it will never rerun, making it effectively an initialization code. Because this component only renders on the server, the`useTask$()`will never run on the browser. (code will never download to browser.) >Notice that`useTask$()`runs BEFORE the actual rendering and on the server. Therefore if you need to do DOM manipulation, use useVisibleTask$() instead, which runs on the browser after rendering. ::: 在這個例子中 >`useTask$()`每 100 毫秒計算一次斐波那契數列(fibonacci number)。 所以 40 個條目需要 4 秒來呈現。 `useTask$()` 作為 SSR 的一部分在服務器上執行(結果可能緩存在 CDN 中。) 因為 `useTask$()` 會阻止呈現,所以呈現的 HTML 頁面需要 4 秒才能呈現。 因為這個任務沒有 `track()` 它永遠不會重新運行,使其成為有效的初始化代碼。 因為此組件僅在服務器上呈現,所以 `useTask$()` 永遠不會在瀏覽器上運行。 (代碼永遠不會下載到瀏覽器。) >請注意 `useTask$()` 在實際渲染之前和在服務器上運行。 因此,如果您需要進行 DOM 操作,請改用 `useVisibleTask$()`,它會在渲染後在瀏覽器上運行。 :::spoiler ... >Use`useTask$()`when you need to: > >* Run async tasks before rendering >* Run code only once before the component is first rendered >* Programmatically run side-effect code when state changes ::: 使用 useTask$()你需要注意: * 在渲染之前運行異步任務`async` * 在首次渲染組件之前只運行一次代碼 * 狀態`state`更改時以編程方式運行副作用`side-effect`代碼 :::spoiler ... ## track() There are times when it is desirable to re-run a task when a component state changes. This is done by using the track() function. The track() function allows you to set up a dependency on a component state on the server (if initially rendered there) and then re-execute the task when the state changes (always on the browser) on the browser. >Note: If all you want to do is compute a new state from an existing state synchronously, you should use useComputed$() instead. ::: ## track() 有時需要在組件狀態更改時重新運行任務。 這是通過使用 `track()` 函數完成的。 `track()`函數允許您在服務器上設置對組件狀態的依賴(如果最初在服務器上呈現),然後當狀態在瀏覽器上發生變化時(總是在瀏覽器上)重新執行任務。 >注意:如果您只想從現有狀態同步計算新狀態,則應改用 `useComputed$()`。 ```javascript import { component$, useSignal, useTask$ } from '@builder.io/qwik'; import { isServer } from '@builder.io/qwik/build'; export default component$(() => { const text = useSignal('Initial text'); const delayText = useSignal(''); useTask$(({ track }) => { track(text); const value = text.value; const update = () => (delayText.value = value); isServer ? update() // don't delay on server render value as part of SSR : delay(500).then(update); // Delay in browser }); return ( <div> Enter text: <input bind:value={text} /> <div>Delayed text: {delayText}</div> </div> ); }); const delay = (time: number) => new Promise((res) => setTimeout(res, time)); ``` > ChatGPT 我們使用 `useTask$` 來實現一個延遲更新功能。當用戶輸入文本時,我們使用 `useSignal` 來監聽文本變化,並將其傳遞給 `useTask$`。 在 `useTask$` 中,我們使用 `track` 函數來追踪文本信號的更改。然後,我們使用 `delay` 函數來延遲 500 毫秒,並在延遲後將文本信號的值更新到 `delayText` 變量中。 最後,我們將文本輸入框的值綁定到 `text` 信號上,並在 UI 中渲染 `delayText` 變量,以便用戶可以看到延遲更新的效果。 在使用 `useTask$` 時,如果您想要追踪一個 `useSignal` 返回的信號的更改,您需要使用 `track` 函數將其傳遞給 `useTask$`。如果您沒有使用 `track` 函數,則 `useTask$`將無法知道信號何時發生更改,因此任務可能無法正確執行或更新。 :::spoiler ... > > ### On the server: * The `useTask$()` runs on the server and the `track()` function sets up a subscription on text signal. * The page is rendered. ### On the browser: * he `useTask$()` does not have to run (or be downloaded) eagerly because Qwik knows that the task is subscribed to the text signal from the server execution. * When the user types in the input box, the text signal changes. Qwik knows that the `useTask$()` is subscribed to the text signal and it is at this time that the `useTask$()` closure is brought into the JavaScript VM to be executed. The `useTask$()` * The `useTask$()`blocks rendering until it completes. If you don't want to block rendering (as in this case) make sure that the task is resolved, and run the delay work on a separate unconnected promise. (In our case we don't await `delay()`. Doing so would block rendering.) * Sometimes it is required to only run code either in the server or in the client. This can be achieved by using the isServer and isBrowser booleans exported from @builder.io/qwik/build as shown above. ::: ### 在服務器上: * `useTask$()` 在服務器上運行,而 `track()` 函數設置對文本信號的訂閱。 * 頁面呈現。 ### 在瀏覽器上: * `useTask$()` 不必急切地運行(或下載),因為 Qwik 知道該任務訂閱了來自服務器執行的文本信號。 * 當用戶在輸入框中鍵入內容時,文本信號會發生變化。 Qwik 知道 useTask$() 訂閱了文本信號,此時 useTask$() 閉包被引入 JavaScript VM 中執行。 `useTask$()` * `useTask$()` 會阻止渲染直到完成。 如果您不想阻止渲染(如本例),請確保任務已解決,並在單獨的未連接promise上運行延遲工作。 (在例子中,我們不await `delay()`。因為那樣會阻塞渲染。) * 有時只需要在服務器或客戶端中運行代碼。 這可以通過使用從 @builder.io/qwik/build 導出的 isServer 和 isBrowser booleans來實現,如上所示。 ## track() as a function :::spoiler ... In the above example track() was used to track a specific signal. However, track() can also be used as a function to track multiple signals at once. ::: 在上面的示例中,`track()` 用於跟踪特定信號。 但是,`track()` 也可以用作同時跟踪多個信號的函數。 ```javascript= import { component$, useSignal, useTask$ } from '@builder.io/qwik'; import { isServer } from '@builder.io/qwik/build'; export default component$(() => { const isUppercase = useSignal(false); const text = useSignal(''); const delayText = useSignal(''); useTask$(({ track }) => { const value = track(() => isUppercase.value ? text.value.toUpperCase() : text.value.toLowerCase() ); const update = () => (delayText.value = value); isServer ? update() // don't delay on server render value as part of SSR : delay(500).then(update); // Delay in browser }); return ( <div> Enter text: <input bind:value={text} /> Is uppercase? <input type="checkbox" bind:checked={isUppercase} /> <div>Delay text: {delayText}</div> </div> ); }); function delay(time: number) { return new Promise((resolve) => setTimeout(resolve, time)); } ``` https://qwik.builder.io/demo/tasks/track-fn/ ```javascript const value = track(() => isUppercase.value ? text.value.toUpperCase() : text.value.toLowerCase() ); // ChatGPT // 在這個例子中,回調函數中依賴的信號有 isUppercase 和 text。 // 當 isUppercase 或 text 信號的值發生變化時,track 函數就會重新執行回調函數, // 重新計算 value 變數的值。 // 這個值會在延遲後更新到 delayText 信號中,並在 UI 中渲染出來。 ``` :::spoiler ... >In this example the track() takes a function that not only reads the signal but also transforms its value to uppercase/lowercase. The track() is doing subscription on multiple signals and computes their value. ::: >在此示例中,`track()` 採用一個函數,該函數不僅讀取信號而且還將其值轉換為大寫/小寫。 `track()` 正在對多個信號進行訂閱併計算它們的值。 ## cleanup() :::spoiler ... Sometimes when running a task cleanup work needs to be performed. When a new task is triggered, the previous task's cleanup() callback is invoked. (Also when the component is removed from the DOM then the cleanup() callback is also invoked.) * The cleanup() function is not invoked when the task is completed. It is only invoked when a new task is triggered or when the component is removed. * The cleanup() function is invoked on the server after the applications are serialized into HTML. * * The cleanup() function is not transferable from server to browser. (Cleanup is meant to release resources on the VM where it is running. It is not meant to be transferred to the browser.) This example shows how to implement a debounce feature using the cleanup() function. ::: 有時在運行任務時需要執行清理工作。 當觸發新任務時,將調用前一個任務的 `cleanup()` 。 (同樣,當組件從 DOM 中移除時,也會調用 `cleanup()` 。) * 任務完成時不會調用 `cleanup()` 。 它僅在觸發新任務或刪除組件時調用。 * `cleanup()` 在應用程序序列化為 HTML 後在服務器上調用。 * `cleanup()` 不能從服務器轉移到瀏覽器。 (清理是為了釋放它正在運行的 VM 上的資源。它並不意味著轉移到瀏覽器。) 此示例說明如何使用 `cleanup()` 函數實現去抖動功能。 ```javascript= import { component$, useSignal, useTask$ } from '@builder.io/qwik'; export default component$(() => { const text = useSignal(''); const debounceText = useSignal(''); useTask$(({ track, cleanup }) => { const value = track(() => text.value); const id = setTimeout(() => (debounceText.value = value), 500); cleanup(() => clearTimeout(id)); }); return ( <div> Enter text: <input bind:value={text} /> <div>Debounced text: {debounceText}</div> </div> ); }); ``` ```javascript // ChatGPT // 在上面提供的例子中,我們使用了 "setTimeout" 函數來實現延遲 500 毫秒後更新 // "debounceText" 信號的功能。這是一個異步任務,因為它需要等待 500 毫秒後才能完成。 // 當異步任務完成後,"cleanup" 函數會被觸發,用於清理定時器資源。 // // 然而,在這個例子中,我們沒有使用 "await" 關鍵字等待異步任務完成。 // 這是因為 "useTask$" Hook 的 "track" 函數可以追踪信號的更改, // 並在信號變化時自動重新執行回調函數。 // 因此,當定時器完成時,"track" 函數會重新執行回調函數, // 並將更新後的值賦值給 "debounceText" 信號,從而更新 UI。 // // 總的來說,在這個例子中,"useTask$" Hook 可以幫助我們管理異步任務, // 並使用 "track" 函數追踪信號的更改。 // 這些功能使得我們不需要使用 "await" 關鍵字等待異步任務完成,就能達到更新 UI 的效果。 ``` # useVisibleTask$() :::spoiler ... Sometimes a task needs to run only on the browser and after rendering, in that case, you should use useVisibleTask$(). The useVisibleTask$() is similar to useTask$() but it only runs on the browser and after initial rendering. useVisibleTask$() registers a hook to be executed when the component becomes visible in the viewport, it will run at least once in the browser, and it can be reactive and re-execute when some tracked state changes. useVisibleTask$() has these properties: * runs on the client only. * eagerly executes code on the client when the component because visible. * Runs after initial rendering. * Does not block rendering. Caution: The useVisibleTask$() should be used as a last resort, because it eagerly executes code on the client. Qwik through resumability goes out of its way to delay the execution of code on the client, and useVisibleTask$() is an escape hatch that should be used with caution. See Best Practices for more details. If you need to run a task on a client consider useTask$() with a server guard. ::: 有時任務只需要在瀏覽器上運行並在渲染後運行, 在這種情況下,您應該使用 `useVisibleTask$()`。 `useVisibleTask$()` 類似於 `useTask$()` 但它僅在瀏覽器上運行並在初始渲染之後運行。 `useVisibleTask$()` 註冊一個鉤子,當組件在視口中可見時執行, 它會在瀏覽器中至少運行一次,並且它可以是反應式的,並在某些跟踪狀態發生變化時重新執行。 `useVisibleTask$()` 具有以下屬性: * 僅在客戶端上運行。 * 當組件可見時,急切地在客戶端上執行代碼。 * 在初始渲染後運行。 * 不阻止渲染。 > 注意:`useVisibleTask$()` 應該作為最後的手段使用,因為它急切地在客戶端執行代碼。 Qwik 通過可恢復性不遺餘力地延遲客戶端上的代碼執行,而 `useVisibleTask$()` 是一個應謹慎使用的逃生艙口。 有關詳細信息,請參閱最佳實踐。 如果您需要在客戶端上運行任務,請考慮將 `useTask$()` 與服務器防護一起使用。 ```javascript= import { component$, useSignal, useTask$ } from '@builder.io/qwik'; import { isServer } from '@builder.io/qwik/build'; export default component$(() => { const text = useSignal('Initial text'); const isBold = useSignal(false); useTask$(({ track }) => { track(text); if (isServer) { return; // Server guard } isBold.value = true; delay(1000).then(() => (isBold.value = false)); }); return ( <div> Enter text: <input bind:value={text} /> <div style={{ fontWeight: isBold.value ? 'bold' : 'normal' }}> Text: {text} </div> </div> ); }); const delay = (time: number) => new Promise((res) => setTimeout(res, time)); ``` :::spoiler ... In the above example the useTask$() is guarded by isServer. The track() is before the guard which allows the server to set up the subscription but does not execute any code on the server. The client then executes the useTask$() once the text signal changes. In the above example the useTask$() is guarded by isServer. The track() is before the guard which allows the server to set up the subscription but does not execute any code on the server. The client then executes the useTask$() once the text signal changes ::: 在上面的示例中,`useTask$()` 由 isServer 保護。 `track()` 在允許服務器設置訂閱但不在服務器上執行任何代碼的守衛之前。 一旦文本信號發生變化,客戶端就會執行 `useTask$()` :::spoiler ... This example shows how to use useVisibleTask$() to initialize a clock on the browser only when the clock component becomes visible. ::: 此示例說明如何僅在時鐘組件可見時使用 `useVisibleTask$()` 在瀏覽器上初始化時鐘。 ```javascript import { component$, useSignal, useVisibleTask$, type Signal, } from '@builder.io/qwik'; export default component$(() => { const isClockRunning = useSignal(false); return ( <> <div style="position: sticky; top:0"> Scroll to see clock. (Currently clock is {isClockRunning.value ? ' running' : ' not running'}.) </div> <div style="height: 200vh" /> <Clock isRunning={isClockRunning} /> </> ); }); const Clock = component$<{ isRunning: Signal<boolean> }>(({ isRunning }) => { const time = useSignal('paused'); useVisibleTask$(({ cleanup }) => { isRunning.value = true; const update = () => (time.value = new Date().toLocaleTimeString()); const id = setInterval(update, 1000); cleanup(() => clearInterval(id)); }); return <div>{time}</div>; }); ``` https://qwik.builder.io/demo/tasks/use-visible-task/ :::spoiler ... Notice how the clock's useVisibleTask$() does not run until the <Clock> component became visible. The default behavior of useVisibleTask$() is to run the task when the component becomes visible. This behavior is implemented through intersection observers. ::: 請注意時鐘的 `useVisibleTask$()` 如何在 `<Clock>` 組件變得可見之前不運行。 `useVisibleTask$()`的默認行為是在組件可見時運行任務。 此行為是通過intersection observers實現的。 ## Option eagerness :::spoiler ... At times it is desirable to run useVisibleTask$() eagerly as soon as the application is loaded in the browser. In that case the useVisibleTask$() needs to run in eagre mode. This is done by using the { strategy: 'document-ready' }. ::: 有時希望在瀏覽器中加載應用程序後立即立即運行 `useVisibleTask$()` 。 在這種情況下,`useVisibleTask$()` 需要以 eagre 模式運行。 這是通過使用 { strategy: 'document-ready' } 完成的。 # useResource$() :::spoiler ... useResource$() is half way between useTask$() and useComputed$(). useResource$() similarities and differences between useTask$() / useComputed$(): useResource$() does not block rendering unlike useTask$() * It returns a value (Signal) just like useComputed$(), but it is asynchronous unlike useComputed$(). * It is not a pure function like useComputed$(). It can have side effects. Specifically, it is intended that useResource$() will have side effects such as network requests. * It uses track()/cleanup() functions similar to useTask$(). ::: #### `useResource$()` 介於 `useTask$()` 和 `useComputed$()` 之間。 #### `useResource$()` `useTask$()` / `useComputed$()` 的異同點: #### 與 `useTask$()` 不同,`useResource$()` 不會阻止渲染 * 它像 `useComputed$()` 一樣返回一個值(Signal),但與 `useComputed$()` 不同,它是異步的。 * 它不是像 `useComputed$()` 這樣的純函數。 它可能有副作用。 具體來說,`useResource$()` 會產生網絡請求等副作用。 * 它使用類似於 `useTask$()` 的 track()/cleanup() 函數。 ```javascript= import { component$, Resource, useResource$, useSignal, } from '@builder.io/qwik'; export default component$(() => { const prNumber = useSignal(3576); const prTitle = useResource$(async ({ track }) => { track(prNumber); // Requires explicit tracking of inputs const response = await fetch( `https://api.github.com/repos/BuilderIO/qwik/pulls/${prNumber.value}` ); const data = await response.json(); return (data.title || data.message || 'Error') as string; }); return ( <> <input type="number" bind:value={prNumber} /> <h1> PR#{prNumber}: <Resource value={prTitle} onPending={() => <>Loading...</>} onResolved={(title) => <>{title}</>} /> </h1> </> ); }); ``` ## previous :::spoiler ... When useResource$() is re-executed, previous contains the previous value of the resource. ::: 重新執行 useResource$() 時,previous 包含資源的先前值。 # Use Hook Rules :::spoiler ... When using lifecycle hooks, you must adhere to the following rules: They can only be called at the root level of component$ (not inside conditional blocks) They can only be called at the root of another use* method, allowing for composition. ::: 使用生命週期鉤子時,必須遵守以下規則: 它們只能在 component$ 的根級別調用(不能在條件塊內) 它們只能在另一個 use* 方法的根部調用,允許組合。 ```javascript= useHook(); // <-- ❌ does not work export default component$(() => { useCustomHook(); // <-- ✅ does work if (condition) { useHook(); // <-- ❌ does not work } useTask$(() => { useNavigate(); // <-- ❌ does not work }); const myQrl = $(() => useHook()); // <-- ❌ does not work return <button onClick$={() => useHook()}></button>; // <-- ❌ does not work }); function useCustomHook() { useHook(); // <-- ✅ does work if (condition) { useHook(); // <-- ❌ does not work } } ``` ###### tags: `Lifecycle Hooks` `Qwik docs` `Component`