---
title: "卷起来，前端建站SSG，SSR，ISR，Hydration, Island...一网打尽"
description: "React, Vue, Angular 三足鼎立之后，前端界又开始“卷起来了”，不过战火已经单页渲染蔓延到了服务端渲染建站。"
url: "https://hicc.pro/p/blog/ssg-ssr-isr-hydration"
author: "tyler wang"
published: "2022-08-29T08:52:42.000Z"
updated: "2024-02-24T08:11:29.000Z"
category: "blog"
tags:
  - "Island"
  - "astro"
  - "SSR"
  - "SSG"
  - "ISR"
  - "Hydration"
image: "https://image.hicc.pro/pro/20220829165707FleE7Ee63s_QxeqhY2GI9EMFcOZAEChASquF1WnOJ7RHf1gdE.jpg"
lang: "zh-CN"
site: "Hipo日志"
---

# 卷起来，前端建站SSG，SSR，ISR，Hydration, Island...一网打尽

React, Vue, Angular 三足鼎立之后，前端界又开始“卷起来了”，不过战火已经单页渲染蔓延到了服务端渲染建站。

[NextJs](https://nextjs.org/)成名已久，功能全面，[astro](https://astro.build/) Island 独步天下，[qwik](https://qwik.builder.io) No hydration 异军突起。[remix](https://remix.run/) 守正出奇，无招胜有招。

今天我们来一块说道说道。

## 让建站酷起来

### SSR

SSR (server-side rendering)相对比较好理解，它算是是SPA大行其道之前主流的方式，简单来说就是服务端，拉取数据组装页面，返回前端HTML。

这种方式优点在于简单直接，每次访问都能够拿到最新数据。但缺点是有点费网，如果流量大，用户分布广就需要费不少功夫优化，这里不再展开。

### SSG

SSG(static site generation)顾名思义，就是提前生成静态的网站。优点是性能好，HTML推送到CDN，成本体验也是最佳。这里的问题是适用场景有限，一般用作内容不太变化的场景。

### ISR

ISR（Incremental Static Regeneration）渐进式的静态内容生成。应该是[NextJS的首创](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration)，从一定程度上优化了SSG了的问题。简单来说就是提供一种机制能够在server中自动的执行SSG，这点优点也很明显，一是能够让内容尽量保持新鲜，而是从访问行为上仍然保持静态访问。

照搬next的思路，有两种方式：

#### 轮询式刷新

简单来说就是类似js setInterval的方式按照一定是时间段刷新server端的构建。

```jsx
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every 10 seconds
    revalidate: 10, // In seconds
  }
}

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // We'll pre-render only these paths at build time.
  // { fallback: blocking } will server-render pages
  // on-demand if the path doesn't exist.
  return { paths, fallback: 'blocking' }
}

export default Blog
```

#### 按需刷新

而按需的方式是算是NextJS对上述方式的优化。机制server 对外提供http的API。内容变动调用这个API就好。

```js
// pages/api/revalidate.js

export default async function handler(req, res) {
  // Check for secret to confirm this is a valid request
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  try {
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    await res.revalidate('/path-to-revalidate')
    return res.json({ revalidated: true })
  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating')
  }
}
```

当然ISR仍然是SSG的范畴，复杂场景仍然无法应对。

### Edge Rendering

这个严格来说是站点的部署形态，算是新时代边缘计算的一种应用场景。类似以前静态的资源推送到CDN，让用户能够就近享受最佳的体验。现在动态网站也能够在边缘渲染，让用户享受到更佳的体验。

这里的问题仍在在于数据，除非是经过特意的改造，一般网站的数据仍需要请求到一个中心化的源服务中。

![](https://image.hicc.pro/mweb/16617546795024.jpg)

一方面用户体验仍然不佳，另一方面源服务压力仍然巨大，成本不低。

上面NextJs的ISR或多或少也是为了解决这个问题。当然另一个更彻底的思路，在边缘的节点上也能有数据持久化的能力，例如[cloudflare](https://www.cloudflare.com/)，或者使用一些分布式的数据库，这里不再展开。

## 酷的“代价”

这些更现代的建站方式确实很炫酷，但是也不是没有缺点。一点就是页面数据会变大(移动站点不太友好)，另一方面首次可交互时间也会延后。

这里实际上就涉及Hydration注水的概念。

### Hydration

Hydration注水，大家可能较少听到，但是它却是现代前端spa，mpa同构框架的关键。

**同一份代码，先server端跑生成一份一定状态计算后的HTML，然后需要在前端“活过来”的过程大概就称之为注水了。**

这里不同的框架实现的细节不同，但是通用的问题是，事件在注水之后才能交互。页面越复杂这段时间越长，越影响体验，这点也是各大框架大显身手的地方。

### Selective Hydration

渐进可选式的注水，这里的代表就是大名鼎鼎的React，借助于fiber架构，React能够打断传统递归式的注水，让应用能够优先处理交互事件，这里框架层面比较复杂，具体效果怎么样也有待观察。

### Islands Architecture

Islands Architecture隔离交互组件。[astro](https://astro.build/)框架首打的特性。

![](https://image.hicc.pro/mweb/16617581385434.jpg)

这里理解也比较简单，让静态页面保持静态，只让页面上需要复杂交互的才去走注水的那一套，保持交互性。

无交互场景

```jsx
---
// Example: Use a static React component on the page, without JavaScript.
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<!-- 100% HTML, Zero JavaScript loaded on the page! -->
<MyReactComponent />
```

有交互的场景。

```jsx
---
// Example: Use a dynamic React component on the page.
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<!-- This component is now interactive on the page! 
     The rest of your website remains static and zero JS. -->
<MyReactComponent client:load />
```

### No hydration

最快的注水，就是无注水😄。这就是[qwik](https://qwik.builder.io) 框架的主要卖点。

```jsx
const Counter = component$(() => {
  const store = useStore({
    htmlCount: 0,
    cmpCount: 0,
  });

  return (
    <div>
      <button onClick$={() => store.htmlCount++}>{store.htmlCount}</button>
      <CmpButton onClick$={() => store.cmpCount++}>{store.cmpCount}</CmpButton>
    </div>
  );
});
```

语法和react类似，区别于`$`的标注，框架依赖这些标注，在构建的时候会将这些组件或者逻辑代码独立成单个js。

```jsx
<div>
  <button q:obj="1" on:click="./chunk-a.js#Counter_button_onClick[0]">0</button>
  <div>...</div>
</div>
```

事件在点击之后或者prefetch，才会懒加载对应的js。以此来实现无注水。

### 没噱头的remix

[remix](https://remix.run) 是react-router团队的新作。

> Focused on web standards and modern web app UX, you’re simply going to build better websites

从介绍上来看似乎没什么噱头，但似乎这就是它们最大的卖点。坚持标准的语法，只使用经典有效的手段。从[remix-vs-next](https://remix.run/blog/remix-vs-next)的文章来看效果确实不错，守正出奇。

上面只是蜻蜓点水介绍了现代框架中比较新潮点。每个地方深挖下去都有不错的收获。花拳百出，核心都是为了提高开发体验，增强用户体验。

***

参考

* [https://github.com/reactwg/react-18/discussions/130](https://github.com/reactwg/react-18/discussions/130)
* [https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration)
* [https://remix.run/blog/remix-vs-next](https://remix.run/blog/remix-vs-next)
* [https://qwik.builder.io/docs/components/events/](https://qwik.builder.io/docs/components/events/)
* [https://docs.astro.build/en/concepts/islands/](https://docs.astro.build/en/concepts/islands/)

---

原文 / Source: <https://hicc.pro/p/blog/ssg-ssr-isr-hydration>
