routeLoader$()

路由載入器在伺服器端載入數據,使其可在 Qwik 組件內使用。它們在 SPA/MPA 導航發生時觸發,因此可以在渲染過程中由 Qwik 組件呼叫。

路由載入器只能在 src/routes 資料夾中的 layout.tsxindex.tsx 檔案內宣告,而且它們必須被匯出。

如果要管理通用的可重複使用的 routeLoaders$,則必須從現有路由的 'layout.tsx' 或 'index.tsx' 檔案中重新匯出此函式,否則它將無法執行或引發例外狀況。如需詳細資訊,請參閱操作指南

src/routes/product/[productId]/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductDetails = routeLoader$(async (requestEvent) => {
  // This code runs only on the server, after every navigation
  const res = await fetch(`https://.../products/${requestEvent.params.productId}`);
  const product = await res.json();
  return product as Product;
});
 
export default component$(() => {
  // In order to access the `routeLoader$` data within a Qwik Component, you need to call the hook.
  const signal = useProductDetails(); // Readonly<Signal<Product>>
  return <p>Product name: {signal.value.product.name}</p>;
});

路由載入器非常適合從資料庫或 API 擷取資料。例如,您可以使用它們從 CMS、天氣 API 或資料庫中的使用者清單擷取資料。

您不應該使用 routeLoader$ 來建立 REST API,為此,您最好使用端點,這讓您可以嚴格控管回應標頭和主體。

多個 routeLoader$

整個應用程式允許多個 routeLoader$,並且可以在任何 Qwik 元件中使用它們。您甚至可以在同一個檔案中宣告多個 routeLoader$

src/routes/layout.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import { Footer } from '../components/footer.tsx';
 
export const useProductData = routeLoader$(async () => {
  const res = await fetch('https://.../product');
  const product = (await res.json()) as Product;
  return product;
});
 
export default component$(() => {
  const signal = useProductData();
  return (
    <main>
      <Slot />
      <Footer />
    </main>
  );
});
src/components/footer.tsx
import { component$ } from '@builder.io/qwik';
 
// Import the loader from the layout
import { useProductData } from '../routes/layout.tsx';
 
export const Footer = component$(() => {
  // Consume the loader data
  const signal = useProductData();
  return <footer>Product name: {signal.value.product.name}</footer>;
});

上面的範例顯示在不同檔案的不同元件中使用 useProductData()。這是刻意設計的行為。

src/routes/admin/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useLoginStatus = routeLoader$(async ({ cookie }) => {
  return {
    isUserLoggedIn: checkCookie(cookie),
  };
});
 
export const useCurrentUser = routeLoader$(async ({ cookie }) => {
  return {
    user: currentUserFromCookie(cookie),
  };
});
 
export default component$(() => {
  const loginStatus = useLoginStatus();
  const currentUser = useCurrentUser();
  return (
    <section>
      <h1>Admin</h1>
      {loginStatus.value.isUserLoggedIn ? (
        <p>Welcome {currentUser.value.user.name}</p>
      ) : (
        <p>You are not logged in</p>
      )}
    </section>
  );
});

上面的範例顯示在同一個檔案中使用兩個 routeLoader$。通用的 useLoginStatus 載入器用於檢查使用者是否已登入,而更具體的 useCurrentUser 載入器則用於擷取使用者資料。

RequestEvent

就像中介軟體端點onRequestonGetrouteLoader$ 可以存取RequestEvent API,其中包含有關目前 HTTP 請求的資訊。

當載入器需要根據請求有條件地傳回資料,或者需要手動覆寫回應狀態、標頭或主體時,這些資訊會很有用。

src/routes/product/[user]/index.tsx
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductRecommendations = routeLoader$(async (requestEvent) => {
  console.log('Request headers:', requestEvent.request.headers);
  console.log('Request cookies:', requestEvent.cookie);
  console.log('Request url:', requestEvent.url);
  console.log('Request method:', requestEvent.method);
  console.log('Request params:', requestEvent.params);
 
  // Use request details to fetch personalized data
  const res = fetch(`https://.../recommendations?user=${requestEvent.params.user}`);
  const recommendedProducts = (await res.json()) as Product[];
 
  return recommendedProducts;
});

在另一個 routeLoader$ 中存取 routeLoader$ 資料

您可以使用 requestEvent.resolveValue 方法在另一個 routeLoader$ 中存取一個 routeLoader$ 的資料。

src/routes/product/[productId]/index.tsx
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductDetails = routeLoader$(async (requestEvent) => {
  const res = await fetch(`https://.../products/${requestEvent.params.productId}`);
  const product = await res.json();
  return product;
});
 
export const useProductRecommendations = routeLoader$(async (requestEvent) => {
  // Resolve the product details from the other loader
  const product = await requestEvent.resolveValue(useProductDetails);
 
  // Use the product details to fetch personalized data
  const res = fetch(`https://.../recommendations?product=${product.id}`);
  const recommendedProducts = (await res.json()) as Product[];
 
  return recommendedProducts;
});

相同的 API 也可用於存取 routeAction$globalAction$ 的資料。

routeLoader$ 的失敗值

routeLoader$ 可以使用 fail 方法傳回失敗值,這是一個特殊值,表示載入器未成功載入預期的資料。

此外,fail 函式允許 routeLoader$ 覆寫 HTTP 狀態碼,例如傳回 404。

當載入器需要傳回不是 undefined 的「錯誤」值,但也需要指示資料載入失敗時,這很有用。

src/routes/product/[productId]/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductDetails = routeLoader$(async (requestEvent) => {
  const product = await db.from('products').filter('id', 'eq', requestEvent.params.productId);
  if (!product) {
    // Return a failed value to indicate that product was not found
    return requestEvent.fail(404, {
      errorMessage: 'Product not found'
    });
  }
  return {
    productName: product.name
  };
});
 
export default component$(() => {
  const product = useProductDetails();
 
  if (product.value.errorMessage) {
    // Render UI for failed value
    return <div>{product.value.errorMessage}</div>;
  }
  return <div>Product name: {product.value.productName}</div>;
});

處理載入器中的相對 URL

在伺服器端執行環境中,將相對 URL 轉換為絕對 URL 至關重要,才能正常運作。這可以透過在相對 URL 前面加上 useLocation() 函式的 origin 來達成。

建立絕對 URL
import { component$ } from '@builder.io/qwik';
import { useLocation } from '@builder.io/qwik-city';
 
export default component$(() => {
  const location = useLocation();
  const relativeUrl = '/mock-data';
  const absoluteUrl = location.url.origin + relativeUrl;
 
  return (
    <section>
      <div>Relative URL: {relativeUrl}</div>
      <div>Absolute URL: {absoluteUrl}</div>
    </section>
  );
});

效能考量

路由載入器會在每次導航後於伺服器上執行。這表示每次使用者在 SPA 或 MPA 中瀏覽至頁面時,以及即使使用者瀏覽至相同頁面時,它們都會執行。

加載器在 Qwik 中介軟體處理程序(onRequestonGetonPost 等)之後以及 Qwik 組件渲染之前執行。這使得加載器可以盡快開始獲取數據,從而減少延遲。

貢獻者

感謝所有為改進此文檔做出貢獻的貢獻者!

  • manucorporat
  • mhevery
  • wtlin1228
  • AnthonyPAlicea
  • the-r3aper7
  • hamatoyogi
  • steve8708
  • iamyuu
  • n8sabes
  • mrhoodz
  • mjschwanitz
  • adamdbradley
  • gioboa