routeLoader$()
路由載入器在伺服器端載入數據,使其可在 Qwik 組件內使用。它們在 SPA/MPA 導航發生時觸發,因此可以在渲染過程中由 Qwik 組件呼叫。
路由載入器只能在 src/routes
資料夾中的 layout.tsx
或 index.tsx
檔案內宣告,而且它們必須被匯出。
如果要管理通用的可重複使用的 routeLoaders$,則必須從現有路由的 'layout.tsx' 或 '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$
。
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>
);
});
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()
。這是刻意設計的行為。
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
就像中介軟體或端點onRequest
和 onGet
,routeLoader$
可以存取RequestEvent
API,其中包含有關目前 HTTP 請求的資訊。
當載入器需要根據請求有條件地傳回資料,或者需要手動覆寫回應狀態、標頭或主體時,這些資訊會很有用。
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$
的資料。
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
的「錯誤」值,但也需要指示資料載入失敗時,這很有用。
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
來達成。
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 中介軟體處理程序(onRequest
、onGet
、onPost
等)之後以及 Qwik 組件渲染之前執行。這使得加載器可以盡快開始獲取數據,從而減少延遲。