React H5 页面加载优化调研

2,139 阅读5分钟

常见的性能优化方法

  1. 在 HTML 内实现 Loading 态或者骨架屏;
  2. 去掉外联 css;
  3. 缓存基础框架;
  4. 使用动态 polyfill;
  5. 使用 SplitChunksPlugin 拆分公共代码;
  6. 正确地使用 Webpack 4.0 的 Tree Shaking;
  7. 使用动态 import,切分页面代码,减小首屏 JS 体积;
  8. 编译到 ES2015+,提高代码运行效率,减小体积;
  9. 使用 lazyload 和 placeholder 提升加载体验。

什么是首屏加载

首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容

关于计算首屏时间

利用performance.timing提供的数据

通过DOMContentLoad或者performance来计算出首屏时间

// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
    console.log('first contentful painting');
});
// 方案二:
performance.getEntriesByName("first-contentful-paint")[0].startTime

// performance.getEntriesByName("first-contentful-paint")[0]
// 会返回一个 PerformancePaintTiming的实例,结构如下:
{
  name: "first-contentful-paint",
  entryType: "paint",
  startTime: 507.80000002123415,
  duration: 0,
};

加载慢的原因

在页面渲染的过程,导致加载速度慢的因素可能如下:

  • 网络延时问题
  • 资源文件体积是否过大
  • 资源是否重复发送请求去加载了
  • 加载脚本的时候,渲染内容堵塞了

解决方案

常见的几种SPA首屏优化方式

  • 减小入口文件积
  • 静态资源本地缓存
  • UI框架按需加载
  • 图片资源的压缩
  • 组件重复打包
  • 开启GZip压缩
  • 使用SSR

H5页面加载优化

历史架构的原因,H5页面的加载过程是Server向Smarty模板注入json_encode后的接口主数据,响应给浏览器带有页面完全JSON数据的页面,然后在浏览器端执行JavaScript,最终Paint给用户,流程如下图:

H5优化前的加载与渲染流程

image.png

H5首屏关键路径耗时拆解

image.png

两个影响H5首屏内容(FCP,First Contentful Paint,首次内容绘制)的关键路径为:

  1. 网络耗时:依赖Server端数据查询及模板编译,当数据查询慢时延迟了首字节到达(TTFB,Time To First Byte,首字节时间)。
  2. 内核渲染耗时:依赖JavaScript执行读取页面主数据并生成完全的DOM结构。

因此,我们针对性地设计并实施了“路由分离+预静态化+WebView预创建”的方案,改进后的页面加载与渲染流程如下图:

image.png

H5优化后的加载与渲染流程

路由分离

路由分离之前H5页面的URL由Server分配,前端负责编写TPL模板产物,TPL与最终URL的对应关系在Server通过配置文件做映射,日益暴露出启动开发慢,页面后期维护交接沟通成本高的问题。

所以我们希望页面路由规范化,让前端开发者自主控制页面入口格式,让后端开发者更专注于API接口数据逻辑。因此,我们设计了前端路由分离方案,约定了页面URL与页面源代码目录映射关系,规则如下图:

image.png

预定式URL路由规范

NGINX直接响应预静态化的HTML文件,首字节到达不依赖数据查询与模版编译。

预静态化

前端路由分离直接在NGINX代理层返回HTML文件,但没有页面完全渲染需要的数据,在执行AJAX请求没有返回之前,需要规避页面一直处于白屏或全局loading状态,提前FCP的时间。

我们采用了预静态化页面的方案。预静态化不像服务器渲染那样即时编译产出完全静态化的 HTML,它只在构建时为了特定的路由生成特定的几个静态页面,我们可以通过 webpack插件将一些特定页面在编译时就注入DOM结构,这样做有几个好处:第一缩短页面白屏时间,第二相对于服务器端渲染节省云基础设施资源成本,第三输出给搜索引擎爬取页面通用内容。

结合实际应用场景和市面上主流的预静态化方式,最终基于ReactDomServer原生的服务端渲染能力的webpack插件,提升预静态化性能和效率。

通过webpack插件系统获取每次构建的compilation上下文,通过html-webpack-plugin的before AssetTagGeneration hook获取当前页面bundle,afterTemplateExecution hook获取当前页面编译后的模板HTML,通过eval执行bundle导出的整个页面APP模块,通过ReactDomServer对单页应用的每个路由产出APP HTML与模板HTML合并后落盘为预静态化的HTML。

H5预静态化调用序列如下

image.png

WebView预创建

在APP启动时立即初始化好WebView组成的缓存池,保证加载每个URL时省去了WebView初始化的时间,并利用上预静态化的HTML缓存,最终使页面无白屏加载态。

React服务端渲染SSR

参考:

React 16 加载性能优化指南

前端工程化之H5性能优化篇

SPA(单页应用)首屏加载速度慢怎么解决?

一文吃透 React SSR 服务端渲染和同构原理

React-SSR Github

react ssr 服务端渲染入门

我的React服务端渲染实践

说说React服务端渲染怎么做?原理是什么?

服务端渲染(SSR)