“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情”
随着前端技术的发展,和越来越多的前端架构出现,对于实现页面渲染,出现了几种渲染方式。已下我们会详细介绍。 在介绍之前,我们先看几个前端页面的性能指标
- TTFB: 发出页面请求到接收到应答数据第一个字节所花费的毫秒数.包括DNS解析时间、TCP链接时间、http请求时间、第一个字节相应时间。建议此时间在500ms以内
- FP: 表示浏览器第一次渲染像素点的时间,也就是首次发生视觉变化的时间。
- FCP: 渲染出第一个内容的时间,可以是文本、图片、canvas等
- TTI: 可交互时间,网页第一次完全达到可以进行交互的时间,及可以相应页面上的点击等事件。可以标识页面多长时间能达到可用的状态。
CSR(client-side rendering)
客户端渲染,也叫浏览器端渲染,现在主流的框架React\Vue\Angular\Svelte等都属于此种渲染方式。
最终部署的制品都已一些静态资源,包括html、js、css等。第一次请求页面返回的html是一些无语义的代码,然后通过javascript来发起数据请求、静态资源请求等一系列内容请求,最终展示在页面中。
由于所有的资源和请求都是在浏览器中通过javascript进行的,所以只有页面js资源加载完成后,才会进行后续的内容请求。这就要求,为了尽快展示页面内容,需要尽可能的减少请求资源大小,只展示和请求所需要的。
SSR(server-side rendering)
服务端渲染,也就是浏览器在请求地址时,页面内容首先会在服务器端尽可能的生成所需要的内容信息,并组装成html文本,再返回到浏览器中,进行展示。减少了浏览器上的逻辑处理和模版数据的往返请求,
由于服务端渲染,需要在服务器中进行逻辑处理,可能会增加TTFB的时间,同时由于减少了浏览器资源的下载和请求,所以减少了TTI的时间。
现有一些框架,比如react的next.js,vue中的nuxt 都提供了SSR的支持。
SSG(static-side generation)
静态页面生成。服务端渲染,虽然能够减少浏览器逻辑处理和请求资源,但是每次浏览器请求页面,都会重新进行一次渲染,会带来一些计算开销成本。,虽然可以通过使用浏览器缓存和数据缓存来减少此种性能,但是对于一些页面,比如文章页面 、login页面等,页面上的数据不会经常变化的页面,就可以使用此处的SSG(静态页面生成)。 在next.js中,其提供getStaticProps来生成此种页面。其调用时机如下
- 在测试开发环境,每次请求都会调用此方法,是为了方便调试和代码修改
- 在部署阶段,只会在build阶段运行一次,提供相应的html,供用户下载使用
比如,在next中使用
export const getStaticProps:GetStaticProps = async (ctx) => {
return {
props:{
name: 'aaa',
token: 'ksie'
}
}
}
// 在tsx页面中,指定name数据
....
<title>Create Next App-{name}</title>
....
在build以后,会生成
其中静态的html中,会使用后台传入的props填充相应的数据信息
由于SSG是在build的时候,根据后台传入的数据,生成相应的静态文件的,如果返回的参数比较多的时候,会生成不同的html页面,打包构建的时候,可能会慢一些。
ISR(incremental static regeneration)
增量静态生成。由于SSG生成的页面都是静态的内容,如果需要数据更新的话,是不会更新的,除非重新进行build操作,为了解决这个问题,next.js使用ISR的技术,可以做到后期的数据更新。具体可以参考这里 代码中,使用getStaticProps返回revalidate时间(秒),从而支持ISR
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// 在第一次请求后的10s后,next会重新生成数据,从而更新页面
revalidate: 10, // In seconds
}
}
具体的流程是
- 在第一次请求之后和10s之前,页面的任何请求都会被缓存
- 在10s后,下一个请求将仍然显示缓存页面
- 同时next.js会在后台触发页面的重新生成
- 一旦页面生成成功,下一次请求就会是新的内容,如果生成失败,则缓存不更新,展示的还是旧页面内容
使用ISR有个问题,就是在变更后台数据,或者一些配置信息时,如果不到revalidate指定的时间后,是不会更新页面内容的。为了解决内容变更后,页面能快速获取到新的内容,可以在Next.js中编写一个validate的api路由。
// pages/api/revalidate.js
export default async function handler(req, res) {
// 可以传入token,验证是否有权限去更新内容
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' });
}
try {
// 通过调用revalidate指定的更新页面路径,从而更新内容
await res.revalidate('/path-to-revalidate');
return res.json({ revalidated: true });
} catch (err) {
return res.status(500).send('Error revalidating');
}
}
也可以在使用getStaticPaths, 指定fallback,来创建ISR页面
// pages/products/[id].js
export async function getStaticPaths() {
const products = await getTop1000Products();
const paths = products.map((product) => ({
params: { id: product.id },
}));
return { paths, fallback: 'blocking' };
}
- fallback: ‘blocking’,指定blocking后,会在请求url地址时,如果在静态页面里没有发现此路径页面,next.js会进行后端渲染,后续的页面会从缓存中获取
- fallback: true, 当没有找到静态页面时,页面中会展示loading状态,直到数据获取完成,页面会根据数据展示,并缓存,提供给后续页面请求使用。