routeLoader$()
路由加载器在服务器上加载数据,使其可在 Qwik 组件内部使用。它们在 SPA/MPA 导航发生时触发,因此可以在渲染期间由 Qwik 组件调用。
路由加载器只能在 src/routes
文件夹中声明,在 layout.tsx
或 index.tsx
文件中,并且必须导出。
如果要管理通用的可重用路由加载器,则必须从现有路由的 '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 组件渲染之前执行。这使得加载器能够尽快开始获取数据,从而减少延迟。