前言
React SSR最成熟的开源框架是Next.js,这么多年保持着强劲的生命力,它的创始团队vercel(曾用名zeit),如今更关注于SSR和serverless的结合。随着服务端的容器化技术以及serverless技术不断完善,在国外可能SSR的降级已经不是一个必要命题。但是,考虑到国内的服务环境,今天我们还是有必要从前端的技术点讨论一下如何去实现SSR的优雅降级。
旧版本的Next.js是利用getInitProps
实现服务端渲染以及静态站点生成。在Next.js 9.3版本后,getInitProps
这个api被替换成为三个不同的api,分别是:
-
getStaticProps
(静态页面生成SSG): 构建的时候生成页面 -
getStaticPaths
(静态页面生成SSG): 根据构建内容去生成动态路由 -
getServerSideProps
(服务端渲染SSR): 在每个请求中在服务端获取数据渲染页面
这三个api的使用是对一个项目中不同页面的更细程度的划分,它可以有效区分哪些页面走SSR、哪些页面走CSR和SSG。高效的划分了这三种不同的渲染模式。
What is JAMstack ?
静态页面生成SSG这种模式更加符合JAMstack的标准,所有的页面都是提前预渲染的,静态的页面可以直接托管在CDN上,有效降低运维成本,有助于你“高效下班”。Next.js官方建议你优先使用静态页面生成,不得已才使用服务端渲染。但是静态页面不能满足你的所有case。只有以下情况才比较适合静态页面生成:
- 数据能通过CMS接口有效渲染
- 数据能够公开缓存,并且不能用户特有的
- 页面必须预渲染,并且SEO敏感
Next.js已经能够在一个项目不同路由支持不同的渲染模式。
源码参考 brandonxiang/example-nextjs ,页面的逻辑放在modules文件夹里面,用一个自定义的函数getPrerenderProps
来保证页面的预渲染逻辑。这个预渲染逻辑如下,即获取数据传递到组件当中与Next.js的预渲染api类似。
// modules/Home.tsx
export const getPrerenderProps = async (ctx) => {
// SSG读取环境变量,并作为兜底参数
const defaultLimits = process.env.limits || 0;
// SSR和CSR动态渲染从URL上获取参数
const _limits = (ctx?.query?._limits) || defaultLimits;
// 获取远程动态数据
const res = await axios.get(
'https://jsonplaceholder.typicode.com/photos?_limit=' + _limits
)
// 传递给各种渲染模式
return { props: { photos: res.data } }
}
自定义页面渲染函数Page
来保证页面dom的渲染,这里的目标是“一份核心代码,多种渲染模式”。数据photos则会在页面中渲染。
// modules/Home.tsx
function Home({ photos }) {
let _photos = photos || []
return (
<div className="photos">
{
_photos.map((photo, index) => (
<figure key={index}>
<img src={photo.thumbnailUrl} alt={photo.title} />
<figcaption>{photo.title}</figcaption>
</figure>
))
}
</div>
)
}
export const Page = Home;
然后将它渲染到三种不同的模式当中。由于 Next.js 的文件路由设定,页面需要被设置成为三种:
- index.js SSR模式
- index_ssg.js SSG模式
- index_csr.js CSR模式
Next.js如何实现SSR
SSR模式需要将自定义的getPrerenderProps
输出到页面级别Next.js API的getServerSideProps
当中,获取数据的逻辑将会提前在服务端完成。此时,服务端可以实现页面的动态渲染。Page
则返回给整个页面的渲染函数。
// index.js
export {
Page as default,
getPrerenderProps as getServerSideProps
} from '../modules/Home';
Next.js如何实现CSR
CSR模式则是自定义的getPrerenderProps
在useEffect中渲染,在页面加载之后,重新对页面进行渲染,达到一个客户端渲染的效果。路由参数发生变化,页面会重新进行渲染,保证的页面的动态可用。这种模式页面的渲染会比较慢,时长主要是请求时长。
// index_csr.js
export default () => {
const router = useRouter();
const [extraProps, setExtraProps] = useState({});
useEffect(() => {
getPrerenderProps(router).then(({props}) => {
setExtraProps(props);
})
}, [router]);
return <Page {...extraProps}/>
}
Next.js如何实现SSG
SSG则是静态预渲染,参数不能动态从路由传入,只能构建的时候以环境变量的形式传入,所以页面渲染需要采用特殊的兼容读取方式。
将自定义的getPrerenderProps
输出到页面级别Next.js API的getStaticProps
当中,实现静态渲染。
// index_ssg.js
export {
Page as default,
getPrerenderProps as getStaticProps
} from '../modules/Home';
如何将SSR降级成为CSR
SSR服务端渲染由于是依赖服务器资源,在流量过大的情况下,有可能会出现服务不可用的情况,返回特殊的错误码例如500等。这时候我们可以实现优雅降级,利用 nginx 做对应的流量分发,当SSR页面返回异常错误的时候,nginx会将流量导入到CSR页面当中。
SSR页面和CSR页面基于Next.js采用同样的业务逻辑编写方式,有效保证页面逻辑的一致性,一份代码两端复用。
SSR优雅的降级总结
Next.js是非常成熟高效的服务端渲染框架,本文通过一些取巧的方式来实现“一份代码,多种渲染方式”,既能提高页面的性能,也能够保证页面的优雅降级。多种渲染模式采用同一份代码,保证了逻辑的一致性,有效地为QA节省了回归人力。在“质量”和“性能”上找到了一个很好的平衡点。
网友评论