---
title: ​使用 Loader
---

到目前为止，我们都是用 `useEffect` 来请求 BFF，加载联系人列表数据。

在启用 SSR 的情况下，`useEffect` 在服务器端是不会执行的，所以 SSR 过程中不会加载数据，这种 SSR 只能渲染很有限的 UI，是低价值的。

可以在当前项目中开启 [SSR](docs/apis/config/server/ssr)，看下这种效果。

```json
"server": {
  "ssr": true,
}
```

执行 `pnpm run dev`，在 devtools 的 network 面板里查看 HTML 请求的 Preview 面板，可以看到 SSR 的渲染结果只有导航栏和 Pending... 字符，并没有联系人数据：

![display5](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/11/display5.png)

前面提到过，MWA 项目里的 SSR 实现是 Serverless SSR，不仅在部署、运维环节，在开发环节对 Server 也是无感的，项目里不需要专门写服务器的实现。

上述加载初始数据的业务逻辑，也不应该像传统模式一样，在客户端代码和服务器端代码中分别实现。

Modern.js 提供一个叫 `useLoader` 的 hooks API，在这种场景下替代 `useEffect`，能自动在 SSR 环节做**预加载**，先获取足够的数据，再运行 app 代码渲染 HTML。

同时，在 CSR 环节中，自动检查当前 loader 需要的数据，如果在 SSR 环节中已经预加载，在客户端就不再重复执行 loader，直接用现成数据；反之就在客户端执行 loader（相当于一种 fallback）。

`useLoader` 的使用很简单，把当前项目中 `Contacts` 组件中的 `useEffect` 代码替换成：

```js
useLoader(
  async () => {
    if (!items.length && !error && !pending) {
      return actions.load();
    }
    return Promise.resolve();
  },
  {
    params: 'contacts',
  },
);
```

`useLoader` API 要求返回一个 promise，用于判断这个 loader 是否完成。第二个参数是当前 Loader 的唯一标识。

同时，在文件顶部的导入 `useLoader`：

```js
import { useLoader } from '@modern-js/runtime';
```

重新执行 `pnpm run dev`，查看 `view-source:http://localhost:8080/contacts/`，或在 devtools 的 Network 面板里查看 HTML 请求的「 Preview 」，可以看到 SSR 渲染出来的 HTML 已经包含完整的 UI：

![display6](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/11/display6.png)

查看 `http://localhost:8080/contacts/`，可以看到首屏是没有 pending 过程的。

---

> 本小节的代码可以在[这里查看](https://github.com/modern-js-dev/modern-js-examples/tree/main/tutorials/c11/hello-modern-3)。
