详解Next.js页面渲染的优化方案
原文:https://m.jb51.net/article/155440.htm

在过去一年的工作中我所使用的js框架是Next.js,尽管这个框架在前后端同构方面有着绝佳的体验,但是当页面js档案过大以及preload过多的时候还是会出现页面跳转卡顿和渲染阻塞等比较糟糕的使用者体验问题。由于我之前既不知道这个框架的工作原理,自然也就不知道如何去优化它。乘着农历春节前工地活少所以稍微研究一下。
第一个问题:宣称前后台同构的Next.js为何会出现卡顿现象?
Next.js 中的特有生命周期hook 函式 getInitialProps会在页面渲染的时候判断浏览器是否为首次渲染,如果是则是服务端渲染网页,如果不是则是客户端渲染。在页面首次渲染的时候,会载入 commons.xxxxx.js 档案,这个档案中打包了react.js、next.js 以及相关的框架程式码也就是如果是客户端渲染打包后的 commons.xxxxx.js 负载了整个前端的页面逻辑,这个档案相对比较大一般会在180kb以上。如果仅从档案大小角度来说,这个档案并不算大,就算利用了next.js 的 preload机制把档案大小放到300kb以上,也还行。但是一旦这个档案阻塞了页面的渲染,页面的渲染要等到 commons.xxxxx.js 载入完毕之后才渲染,那问题就来了。
在next7中使用的打包工具是webpack4,这在打包和载入过程有一个比较蠢的机制(或许仅仅是我个人观点),那就是但凡React DOM上绑定了style 这些DOM都不会在服务端渲染出来,而是打包抽离成一个小的js档案,在 commons.xxxxx.js 载入完毕之后,再载入这个js,将DOM和内联style渲染到HTML。这就在某种程度上导致了next.js首次渲染是SSR失效了,更为糟糕的是卡顿感十足。
可能有人会说,那就不要写内联style不就好了。但是事实是在大量的后台资料动态渲染页面和使用者自定义页面的情况下,不可能做到完全不写内联样式,而去傻乎乎地写一堆className。
所以我们要解决一个问题那就是如何保证,内联style的react dom在首次渲染页面的时候是服务器端直接输出后扔给后台,而不是让 commons.xxxxx.js 卡卡卡卡卡,然后砰的一下蹦出来。
要解决上一个问题,首先要了解Next.js是如何渲染页面的?
在Next.js的规则中,所有页面级的程式码都是写在pages资料夹中,比如/pages/home:
export default () => (你瞅啥?这是home页)
而其框架内建的Document元件中,已经帮开发者配置好传统的HTML档案的 , 这些标签作为静态资源的外壳。Document元件中有一个 renderPage() 方法,如果程式码正常执行,该方法就会将pages资料夹中的程式码和它外部同步渲染到浏览器中。如果开发者希望自定义Document元件只需新增 /pages/_document.js 档案即可。
renderPage()本质是一个回拨函式,它的作用只有一个那就是 执行React源代码中渲染逻辑 同步载入到Next.js的Document元件中形成DOM节点。
import Document, { Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
static getInitialProps ({ renderPage }) {
// renderPage()位于next.js特有生命周期函式getInitialProps中。
return renderPage();
}
render () {
return (
没见过标题党吗?
)
}
}
服务端渲染样式
为了能让服务器端渲染样式,我们首先得先做两件事:
在页面首次载入的时候,也就是所谓的SSR.能让 renderPage 方法在服务器端就能对React Dom进行解析,让HTML归HTML,CSS归CSS;能让 Document 元件在页面切换时,能及时更新 ,这样不同的页面就能载入自己所需的script,style。解决方案的登场
隆重介绍神器 styled-components 出场, styled-components 在github上目前为止已经超过1万stars,它的设计初衷在于在服务端渲染的时候,同时渲染出一个ServerStyleSheet,然后把这个ServerStyleSheet送入React DOM树中。它主要就做两件事:
把元件中 styles 抽离到 标签中;把 标签放到 中下面就是一段如何正确使用ServerStyleSheet的姿势步骤:
import { ServerStyleSheet } from "styled-components";
static getInitialProps ({ renderPage }) {
const sheet = new ServerStyleSheet()
const transform = (App) => {
return sheet.collectStyles();
}
const styleTags = sheet.getStyleElement()
const page = renderPage(transform);
return { ...page, styleTags };
}
render(){
return(
{this.props.styleTags}
);
}
上面的程式码已经完美跟大家展示了如何将内联style抽离出dom,然后通过 的方法渲染样式, 那么问题来了,如何在打包解析react dom时,给服务器一个"纯洁、干净、无暇"的DOM呢?
这个时候就需要使用 babel-plugin-styled-components 包,在babel中进行解析。
程式码如下:
{
"presets": [
"next/babel"
],
"plugins": [
["styled-components", { "ssr": true, "displayName": true, "preprocess": false } ]
]
}
这个时候在去开启next.js页面就会发现,那家伙、那场面渲染速度嗖嗖的。至于负责前端逻辑的 commons.xxxxx.js ,您老人家就安静地慢慢地载入吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支援指令码之家。