首页
/ Modern.js 数据获取机制深度解析

Modern.js 数据获取机制深度解析

2025-07-08 06:39:37作者:侯霆垣

Modern.js 作为现代 Web 开发框架,提供了一套完善的数据获取解决方案。本文将深入探讨其数据获取机制,帮助开发者更好地理解和运用这一核心功能。

一、Modern.js 数据获取概览

Modern.js 的数据获取能力是其核心特性之一,它通过一系列精心设计的 API 帮助开发者高效管理应用数据。这些 API 并非直接发起请求的工具,而是提供了更高级的数据管理抽象,能够显著提升项目性能。

二、推荐方案:Data Loader

2.1 基本概念

Modern.js 推荐使用约定式路由进行路由管理。在这种模式下,每个路由组件(layout.tspage.ts)可以拥有一个同名的 data 文件(早期版本为 loader 文件)。该文件需要导出一个函数,在组件渲染前执行,为路由组件提供所需数据。

2.2 基础示例

典型的项目结构如下:

.
└── routes
    ├── layout.tsx
    └── user
        ├── layout.tsx
        ├── layout.data.ts
        ├── page.tsx
        └── page.data.ts

数据获取流程:

  1. data 文件中定义数据获取逻辑
  2. 在路由组件中使用 useLoaderData 获取数据
// routes/user/page.tsx
import { useLoaderData } from '@modern-js/runtime/router';
import type { ProfileData } from './page.data.ts';

export default function UserPage() {
  const profileData = useLoaderData() as ProfileData;
  return <div>{profileData}</div>;
}
// routes/user/page.data.ts
export type ProfileData = {
  /* 类型定义 */
};

export const loader = async (): Promise<ProfileData> => {
  const res = await fetch('https://api/user/profile');
  return await res.json();
};

2.3 执行环境差异

  • CSR 环境loader 函数在客户端执行,可以使用浏览器 API
  • SSR 环境:无论是首屏渲染还是客户端导航,loader 都只在服务端执行,可以调用任何 Node.js API

三、高级特性详解

3.1 loader 函数参数

loader 函数接收两个重要参数:

  1. params:动态路由参数

    export const loader = async ({ params }: LoaderFunctionArgs) => {
      const { id } = params;
      const res = await fetch(`https://api/user/${id}`);
      return res.json();
    };
    
  2. request:Fetch Request 实例

    export const loader = async ({ request }: LoaderFunctionArgs) => {
      const url = new URL(request.url);
      const userId = url.searchParams.get('id');
      return queryUser(userId);
    };
    

3.2 错误处理

Modern.js 提供了完善的错误处理机制:

export const loader = async () => {
  const res = await fetch('https://api/user/profile');
  if (!res.ok) {
    throw res;  // 触发 ErrorBoundary
  }
  return res.json();
}

配合 ErrorBoundary 组件可以实现优雅的错误展示:

const ErrorBoundary = () => {
  const error = useRouteError() as Response;
  return (
    <div>
      <h1>{error.status}</h1>
      <h2>{error.statusText}</h2>
    </div>
  );
};

3.3 数据缓存策略

Modern.js 采用智能的数据缓存策略:

  1. 路由导航时,只加载变化部分的数据
  2. 触发数据更新的情况:
    • 执行 Data Action 后
    • URL 查询参数变化时
    • 点击当前页面链接
    • shouldRevalidate 返回 true

3.4 客户端专用 Loader

在 SSR 项目中,可以通过 Client Loader 实现:

  1. SSR 降级时直接请求数据源
  2. 利用客户端缓存避免重复请求
// 启用客户端 loader
export const config = {
  disableLoader: true
};

export const clientLoader = async () => {
  // 客户端专用逻辑
}

四、最佳实践与常见问题

4.1 注意事项

  1. 数据可序列化loader 返回值必须是可序列化的
  2. 避免直接调用:不应手动调用 loader 函数
  3. 模块隔离:避免路由组件与 loader 文件间相互导入
  4. 服务端限制:避免在服务端代码中使用 __filename 等 Node 特有变量

4.2 常见问题解答

Q1:Data Loader 与 BFF 函数的关系?

  • CSR 项目中:loader 在客户端执行,可直接调用 BFF
  • SSR 项目中:loader 可作为 BFF 的替代方案,减少转发层级

Q2:为何 SSR 项目中 Data Loader 只在服务端执行?

这种设计带来了多重优势:

  • 简化使用方式
  • 减少网络传输数据量
  • 减小客户端包体积
  • 提升主线程性能

五、总结

Modern.js 的数据获取机制通过精心设计的 API 和智能的缓存策略,为开发者提供了高效、灵活的数据管理方案。无论是简单的数据获取还是复杂的状态管理,都能通过这套机制优雅地实现。理解并合理运用这些特性,将显著提升应用的性能和开发效率。