什么是 SSR?为什么要 SSR?
SSR 是 Sever-side Render 的缩写,意思是指「在 Server 端就产生(Render)出画面」。目前大概有几种产生画面的时机点:
- Server Side Render: 在后端接收到 Request 即产生 HTML 画面,需要新的资料则需要重新发送 Request ,也会产生一个新的画面。后端的 Framework 通常都会有一个 HTML Template 的机制就是在产生画面的动作。
- Client Side Render: 在前端进行 Render,后端作为资料供应的 API 层。当需要新的资料时,前端发送 Request 后只需要部份更新画面就好。
- Isomorphic: 网站能够同时考虑 SSR 与 CSR 两种方式,之前有整理过关于 Isomorphic 的介绍。
最早期的网站都是 SSR 的方式进行,但每次的重新 Render 会造成后端的负担提升也让使用者的体验变差。而后来的 React/Vue 之类的 Framework 则是基于 CSR 的方式让使用者体验,并达到了前后端分离的优点。但 CSR 还是伴随着几个问题:
- 有些 CSR 的网站会造成爬虫取得的页面不是那么完整,造成 SEO 的问题
- 第一次载入画面是等到前端载入后,会有一段时间的空白
因此,如果能够兼具 CSR 与 SSR 的 Isomorphic ,成了一条新的解法。以下我们讲的 SSR 都是 Isomorphic 同时兼具两种方式的 SSR。
Next.js
「Next.js is a lightweight framework for static and server‑rendered applications.」
Next 是一个基于 React 的 SSR 解决方案,更多的细节可以参考官网。
开始第一个 Next.js 专案吧!
方法 ①: 手动安装
根据官方的教学,可以透过 NPM 的方式手动安装:
$ npm install --save next react react-dom
接着再把 package.json 中执行的方式改成 NEXT:
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
然后建立一个页面在 /pages/index.js 档案:
export default () => <div>Welcome to next.js!</div>
最后,运行指令就会将专案跑在 3000 PORT:
npm run dev
方法 ②: create-next-app
许多人开发 React 会搭配 create-react-app 的 scaffold 来跳过复杂的环境设定,Next.js 也有:
$ npm install -g create-next-app
$ create-next-app my-app
$ cd my-app/
$ yarn start
来看一个简单 Isomorphic 范例
增加一个新页面
Next 是根据 /pages 目录作为 Route 的分配,举例来说 /pages 的 index 就会对应到 Root URL。我们先在 /pages 目录下建立一个 hello.js 的档案:
const Hello = (props) => (
<div>
<h1>Hello Next</h1>
</div>
)
export default Hello
此时就会多了一个 Route 在 /hello 。
两个页面彼此连结
在 Next 中提供了 Link 的 Component 让我们可以进行页面间的 Client Side 换页:
import Link from 'next/link'
const Hello = (props) => (
<div>
<Link href={`/`}>
<a>index</a>
</Link>
<Link href={`/hello`}>
<a>hello</a>
</Link>
</div>
)
export default Hello
CSR 与 SSR 判断
我们现在有一个页面,上面有连结。我们前面有说过 Next.js 是一个兼具 CSR 与 SSR 的框架,那他是怎么处理的呢?先区分一下两种来源:
- 如果直接点 Link 连结,是在 Client Side 换页 => CSR
- 重新整理,或是第一次到这个画面上,必须先发 Request 到后端 => SSR
Next 的做法是透过 getInitialProps 的方式,在 Render 之前就先透过 req 来判断来源为何:
import Link from 'next/link'
const Hello = (props) => (
<div>
<h1>{props.text}</h1>
<Link href={`/hello`}>
<a>reload</a>
</Link>
</div>
)
Hello.getInitialProps = async function({req, query}) {
if (req)
return { text: 'hello server' }
return { text: 'hello client' }
}
export default Hello
这样执行之后,我们可以发现:
- 如果是 SSR 的话,会在 getInitialProps 取得 req
- 如果是 CSR 的话,getInitialProps 中不会得到 req
小结论
Next 可以优雅地替我们解决 Isomorphic 的复杂机制,达到同时有 SSR 与 CSR 的解法。透过了简单的实作来了解如何开始,如果之后新专案有需要是直得考虑导入的!
Reference
License
本著作由Chang Wei-Yaun (v123582)制作, 以创用CC 姓名标示-相同方式分享 3.0 Unported授权条款释出。