详解Next.js页面渲染的优化方案


Posted in Javascript onJanuary 27, 2019

在过去一年的工作中我所使用的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 () => (<div>你瞅啥?这是home页</div>)

而其框架内置的Document组件中,已经帮开发者配置好传统的HTML文件的 <head> , <body> 这些标签作为静态资源的外壳。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 (
   <html>
    <Head>
     <title>没见过标题党吗?</title>
    </Head>
    <body>
     <Main />
     <NextScript />
    </body>
   </html>
  )
 }
}

服务端渲染样式

为了能让服务器端渲染样式,我们首先得先做两件事:

  1. 在页面首次加载的时候,也就是所谓的SSR.能让 renderPage 方法在服务器端就能对React Dom进行解析,让HTML归HTML,CSS归CSS;
  2. 能让 Document 组件在页面切换时,能及时更新 <head> ,这样不同的页面就能加载自己所需的script,style。

解决方案的登场

隆重介绍神器 styled-components 出场, styled-components 在github上目前为止已经超过1万stars,它的设计初衷在于在服务端渲染的时候,同时渲染出一个ServerStyleSheet,然后把这个ServerStyleSheet送入React DOM树中。它主要就做两件事:

  1. 把组件中 styles 抽离到 <style> 标签中;
  2. 把 <style> 标签放到 <head> 中

下面就是一段如何正确使用ServerStyleSheet的姿势步骤:

import { ServerStyleSheet } from "styled-components";

 static getInitialProps ({ renderPage }) {
  const sheet = new ServerStyleSheet()
  const transform = (App) => {
   return sheet.collectStyles(<App />);   
  }
  const styleTags = sheet.getStyleElement()
  const page = renderPage(transform);
  return { ...page, styleTags };
 }
 
 render(){
   return(
    <html lang="zh-Hans">
    <Head>
     <meta name="viewport" content="initial-scale=1.0, width=device-width" />
     <meta name="description" content="Kanseefoil"/>
     <link rel="shortcut icon" href="/static/favicon.ico" rel="external nofollow" ></link>
     {this.props.styleTags}
    </Head>
    </html>
    );
 }

上面的代码已经完美跟大家展示了如何将内联style抽离出dom,然后通过 <link style> 的方法渲染样式, 那么问题来了,如何在打包解析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 ,您老人家就安静地慢慢地加载吧。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Auntion-TableSort国人写的一个javascript表格排序的东西
Nov 12 Javascript
prototype Element学习笔记(篇一)
Oct 26 Javascript
让innerText在firefox火狐和IE浏览器都能用的写法
May 14 Javascript
向左滚动文字 js代码效果
Aug 17 Javascript
纯js分页代码(简洁实用)
Nov 05 Javascript
jquery交替变换颜色的三种方法 实例代码
Nov 19 Javascript
详解jquery事件delegate()的使用方法
Jan 25 Javascript
bootstrap下拉列表与输入框组结合的样式调整
Oct 08 Javascript
Bootstrap CSS组件之导航条(navbar)
Dec 17 Javascript
AngularJS中$http的交互问题
Mar 29 Javascript
解决vue this.$forceUpdate() 处理页面刷新问题(v-for循环值刷新等)
Jul 26 Javascript
apicloud拉起小程序并传递参数的方法示例
Nov 21 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
Jan 27 #Javascript
在Vue项目中引入JQuery-ui插件的讲解
Jan 27 #jQuery
在vue项目中使用Jquery-contextmenu插件的步骤讲解
Jan 27 #jQuery
jQuery实现左右两个列表框的内容相互移动功能示例
Jan 27 #jQuery
vue中子组件传递数据给父组件的讲解
Jan 27 #Javascript
jQuery实现模拟搜索引擎的智能提示功能简单示例
Jan 27 #jQuery
JavaScript中的回调函数实例讲解
Jan 27 #Javascript
You might like
需要使用php模板的朋友必看的很多个顶级PHP模板引擎比较分析
2008/05/26 PHP
PHP实现多图片上传类实例
2014/07/26 PHP
php的XML文件解释类应用实例
2014/09/22 PHP
weiphp微信公众平台授权设置
2016/01/04 PHP
PHP图片添加水印功能示例小结
2016/10/03 PHP
jQuery对象[0]是什么含义?
2010/07/31 Javascript
IE6背景图片不缓存问题解决方案及图片使用策略多个方法小结
2012/05/14 Javascript
最简单的JavaScript验证整数、小数、实数、有效位小数正则表达式
2015/04/17 Javascript
JS基于面向对象实现的拖拽功能示例
2016/12/20 Javascript
基于Express框架使用POST传递Form数据
2019/08/10 Javascript
使用easyui从servlet传递json数据到前端页面的两种方法
2019/09/05 Javascript
微信小程序防止多次点击跳转(函数节流)
2019/09/19 Javascript
Angular 多级路由实现登录页面跳转(小白教程)
2019/11/19 Javascript
vue+element table表格实现动态列筛选的示例代码
2021/01/14 Vue.js
[01:02:46]VGJ.S vs NB 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
把大数据数字口语化(python与js)两种实现
2013/02/21 Python
Python应用03 使用PyQT制作视频播放器实例
2016/12/07 Python
对python中两种列表元素去重函数性能的比较方法
2018/06/29 Python
详解Numpy中的广播原则/机制
2018/09/20 Python
详解基于python的多张不同宽高图片拼接成大图
2019/09/26 Python
python 计算方位角实例(根据两点的坐标计算)
2020/01/17 Python
简单介绍一下pyinstaller打包以及安全性的实现
2020/06/02 Python
Python利用命名空间解析XML文档
2020/08/10 Python
打造经典复古风格的品牌:Alice + Olivia(爱丽丝+奥利维亚)
2016/09/07 全球购物
Revolution Beauty美国官网:英国知名化妆品网站
2018/07/23 全球购物
开普敦通行证:Cape Town Pass
2019/07/18 全球购物
资生堂英国官网:Shiseido英国
2020/12/30 全球购物
法国包包和行李箱销售网站:Bagage24.fr
2020/03/24 全球购物
Java Servlet的主要功能和作用是什么
2014/02/14 面试题
新闻传媒系求职信范文
2014/04/19 职场文书
加强作风建设工作总结
2014/10/23 职场文书
正规欠条模板
2015/07/03 职场文书
公务员廉洁从政心得体会
2016/01/19 职场文书
Vue中foreach数组与js中遍历数组的写法说明
2021/06/05 Vue.js
解决mysql模糊查询索引失效问题的几种方法
2021/06/18 MySQL
virtualenv隔离Python环境的问题解析
2022/06/21 Python