最佳實務

在模板中內嵌操作

如果操作在模板中內嵌,Qwik 優化器可以更好地優化應用的反應性。

次優的實現方式
// Don't do this!
export default component$(() => {
  const signal = useSignal(0);
  const isBiggerThanZero = signal.value > 0 ? 'Bigger than zero' : 'Smaller than zero';
  return (
    <div>
      <button onClick$={() => signal.value++}>+</button>
      <button onClick$={() => signal.value--}>-</button>
      <div>{isBiggerThanZero} - Current value: { signal.value }</div>
    </div>
  );
});

當信號改變時,上述實現方式將導致整個模板重新渲染。這是因為 isBiggerThanZero 並未內嵌在模板中。

最佳的實現方式
export default component$(() => {
  const signal = useSignal(0);
  return (
    <div>
      <button onClick$={() => signal.value++}>+</button>
      <button onClick$={() => signal.value--}>-</button>
        <div>
          {signal.value > 0 ? 'Bigger than zero' : 'Smaller than zero'} - Current
          value: {signal.value}
        </div>
    </div>
  );
});

將信號讀取移至 useTask$useComputed$

這與上述技巧類似。

每次 Qwik「讀取」信號/儲存值時,進行「讀取」的函式將在該信號每次變更時再次執行。

這就是為什麼最好盡可能在 useTask$useComputed$ 函式內「讀取」值(並追蹤它們),而不是在組件函式內。

因為否則,您的組件函式將在每次變更時重新執行。

次優的實現方式
// Don't do this!
export default component$(() => {
  const count = useSignal(1);
  const doubleCount = count.value*2;
  return (
    <div>{doubleCount}</div>
  );
});

當信號改變時,上述實現方式將導致整個模板重新渲染。

在下方,只有 useComputed$ 函式會在 count.value 發生任何變更時重新執行

最佳的實現方式
export default component$(() => {
  const count = useSignal(1);
  const dobuleCount = useComputed$(() => count.value*2);
  return (
    <div>{doubleCount.value}</div>
  );
});

useVisibleTask$() 作為最後手段

儘管很方便,但 useVisibleTask$() 會急切地執行程式碼並阻塞主線程,從而阻止使用者互動,直到任務完成。您可以將其視為一個緊急出口。

如果有疑問,請使用以下方法代替 "useVisibleTask$()"

  • useTask$() -> 在 SSR 模式下執行程式碼。
  • useOn() -> 監聽「目前組件」根元素上的事件。
  • useOnWindow() -> 監聽 window 物件上的事件。
  • useOnDocument() -> 監聽 document 物件上的事件。

不過,有時候這是達成結果的唯一方法。

在這種情況下,您可以在 "useVisibleTask$" 之前的行中添加 // eslint-disable-next-line qwik/no-use-visible-task 以移除警告。

使用 useOn()useOnWindow()useOnDocument() 註冊 DOM 事件

Qwik 允許使用 useOn() 或 JSX 以宣告方式註冊事件監聽器。

當使用 useVisibleTask$() 以程式化方式註冊事件時,即使沒有觸發事件,我們也會急切地下載並執行 JavaScript。

次優的實現方式
// Don't do this!
useVisibleTask$(({ cleanup }) => {
  const listener = (event) => {
    const mouseEvent = event as MouseEvent;
    console.log(mouseEvent.x, mouseEvent.y);
  };
  document.addEventListener('mousemove', listener);
 
  cleanup(() => {
    document.removeEventListener('mousemove', listener);
  });
});

上述實現方式會導致更多 JavaScript 急切載入,而不是精確地回應使用者事件。增加的前期 JavaScript 載入會導致應用程式效能變慢。

請改用 useOnDocument() 鉤子在 document 物件上註冊事件,這樣 Qwik 在事件觸發之前不會執行任何 JS。

最佳的實現方式
useOnDocument(
  'mousemove',
  $((event) => {
    const mouseEvent = event as MouseEvent;
    console.log(mouseEvent.x, mouseEvent.y);
    // No manual clean up required!
  })
);

避免從 window 物件存取位置資訊

不要直接存取 window.location,請改用 useLocation() 鉤子。

次優的實現方式
// Don't do this!
useVisibleTask$(()=> {
    if (window.location.href).includes('foo') {
        //... do the thing
    }
})
// or
useTask$(() => {
  if (isBrowser) {
        if (window.location.href).includes('foo') {
        //... do the thing
    }
  }
})

許多與位置資訊相關的操作都可以在初始伺服器端渲染期間執行,從而產生沒有任何 JavaScript 開銷的純 HTML。

強制在用戶端執行此邏輯會增加前期 JavaScript 並導致急切載入。

最佳的實現方式
// Do this!
const location = useLocation();
 
if (location.url.href.includes('foo')) {
  // Do the thing
}

例外

當對純靜態檔案使用 SSG 時,在建構時不可避免地需要依賴沒有目前位置資訊的伺服器。

但是,請謹慎操作!如果在使用者事件發生之前不需要所需資訊(例如查詢參數),請將檢查納入您的事件處理程式碼中。

這種方法有助於防止急切載入 JavaScript 並提高效能。

請參閱:useLocation() 文件

貢獻者

感謝所有幫助改進此文件的貢獻者!

  • mhevery
  • the-r3aper7
  • manucorporat
  • jakovljevic-mladen
  • kerbelp
  • wfairclough
  • cunzaizhuyi
  • reemardelarosa
  • un33k
  • egmaleta
  • mugan86
  • octoper
  • mrhoodz
  • VinuB-Dev
  • anartzdev
  • adamdbradley
  • hamatoyogi
  • maiieul