构建时预渲染:网页首帧优化实践

前言

自JavaScript诞生以来,前端技术发展非常迅速。移动端白屏优化是前端界面体验的一个重要优化方向,Web 前端诞生了 SSR 、CSR、预渲染等技术。在美团支付的前端技术体系里,通过预渲染提升网页首帧优化,从而优化了白屏问题,提升用户体验,并形成了最佳实践。

在前端渲染领域,主要有以下几种方式可供选择:

CSR预渲染SSR同构
优点不依赖数据FP 时间最快客户端用户体验好内存数据共享不依赖数据FCP 时间比 CSR 快客户端用户体验好内存数据共享SEO 友好首屏性能高,FMP 比 CSR 和预渲染快SEO 友好首屏性能高,FMP 比 CSR 和预渲染快客户端用户体验好内存数据共享客户端与服务端代码公用,开发效率高
缺点SEO 不友好FCP 、FMP 慢SEO 不友好FMP 慢客户端数据共享成本高模板维护成本高Node 容易形成性能瓶颈

通过对比,同构方案集合 CSR 与 SSR 的优点,可以适用于大部分业务场景。但由于在同构的系统架构中,连接前后端的 Node 中间层处于核心链路,系统可用性的瓶颈就依赖于 Node ,一旦作为短板的 Node 挂了,整个服务都不可用。

结合到我们团队负责的支付业务场景里,由于支付业务追求极致的系统稳定性,服务不可用直接影响到客诉和资损,因此我们采用浏览器端渲染的架构。在保证系统稳定性的前提下,还需要保障用户体验,所以采用了预渲染的方式。

那么究竟什么是预渲染呢?什么是 FCP/FMP 呢?我们先从最常见的 CSR 开始说起。

以 Vue 举例,常见的 CSR 形式如下:

一切看似很美好。然而,作为以用户体验为首要目标的我们发现了一个体验问题:首屏白屏问题

为什么会首屏白屏

浏览器渲染包含 HTML 解析、DOM 树构建、CSSOM 构建、JavaScript 解析、布局、绘制等等,大致如下图所示:

要搞清楚为什么会有白屏,就需要利用这个理论基础来对实际项目进行具体分析。通过 DevTools 进行分析:

  • 等待 HTML 文档返回,此时处于白屏状态。
  • 对 HTML 文档解析完成后进行首屏渲染,因为项目中对
    加了灰色的背景色,因此呈现出灰屏。
  • 进行文件加载、JS 解析等过程,导致界面长时间出于灰屏中。
  • 当 Vue 实例触发了 mounted 后,界面显示出大体框架。
  • 调用 API 获取到时机业务数据后才能展示出最终的页面内容。

由此得出结论,因为要等待文件加载、CSSOM 构建、JS 解析等过程,而这些过程比较耗时,导致用户会长时间出于不可交互的首屏灰白屏状态,从而给用户一种网页很“慢”的感觉。那么一个网页太“慢”,会造成什么影响呢?

“慢”的影响

Global Web Performance Matters for ecommerce的报告中指出:

  • 57%的用户更在乎网页在3秒内是否完成加载。
  • 52%的在线用户认为网页打开速度影响到他们对网站的忠实度。
  • 每慢1秒造成页面 PV 降低11%,用户满意度也随之降低降低16%。
  • 近半数移动用户因为在10秒内仍未打开页面从而放弃。

我们团队主要负责美团支付相关的业务,如果网站太慢会影响用户的支付体验,会造成客诉或资损。既然网站太“慢”会造成如此重要的影响,那要如何优化呢?

优化思路

在User-centric Performance Metrics一文中,共提到了4个页面渲染的关键指标:

基于这个理论基础,再回过头来看看之前项目的实际表现:

可见在 FP 的灰白屏界面停留了很长时间,用户不清楚网站是否有在正常加载,用户体验很差。

试想:如果我们可以将 FCP 或 FMP 完整的 HTML 文档提前到 FP 时机预渲染,用户看到页面框架,能感受到页面正在加载而不是冷冰冰的灰白屏,那么用户更愿意等待页面加载完成,从而降低了流失率。并且这种改观在弱网环境下更明显。

通过对比 FP、FCP、FMP 这三个时期 DOM 的差异,发现区别在于:

  • FP:仅有一个 div 根节点。
  • FCP:包含页面的基本框架,但没有数据内容。
  • FMP:包含页面所有元素及数据。

仍然以 Vue 为例, 在其生命周期中,mounted 对应的是 FCP,updated 对应的是 FMP。那么具体应该使用哪个生命周期的 HTML 结构呢?

mounted (FCP)updated (FMP)
缺点只是视觉体验将 FCP 提前,实际的 TTI 时间变化不大构建时需要获取数据,编译速度慢构建时与运行时的数据存在差异性有复杂交互的页面,仍需等待,实际的 TTI 时间变化不大
优点不受数据影响,编译速度快首屏体验好对于纯展示类型的页面,FP 与 TTI 时间近乎一致

通过以上的对比,最终选择在 mounted 时触发构建时预渲染。由于我们采用的是 CSR 的架构,没有 Node 作为中间层,因此要实现 DOM 内容的预渲染,就需要在项目构建编译时完成对原始模板的更新替换。

至此,我们明确了构建时预渲染的大体方案。

构建时预渲染方案

构建时预渲染流程:

配置读取

由于 SPA 可以由多个路由构成,需要根据业务场景决定哪些路由需要用到预渲染。因此这里的配置文件主要是用于告知编译器需要进行预渲染的路由。

在我们的系统架构里,脚手架是基于 Webpack 自研的,在此基础上可以自定义自动化构建任务和配置。

触发构建

项目中主要是使用 TypeScript,利用 TS 的装饰器,我们封装了统一的预渲染构建的钩子方法,从而只用一行代码即可完成构建时预渲染的触发。

装饰器:

使用:

构建编译

从流程图上,需要在发布机上启动模拟的浏览器环境,并通过预渲染的事件钩子获取当前的页面内容,生成最终的 HTML 文件。

由于我们在预渲染上的尝试比较早,当时还没有 Headless Chrome 、 Puppeteer、Prerender SPA Plugin等,因此在选型上使用的是 phantomjs-prebuilt(Prerender SPA Plugin 早期版本也是基于 phantomjs-prebuilt 实现的)。

通过 phantom 提供的 API 可获得当前 HTML,示例如下:

为了提高构建效率,并行对配置的多个页面或路由进行预渲染构建,保证在 5S 内即可完成构建,流程图如下:

方案优化

理想很丰满,现实很骨感。在实际投产中,构建时预渲染方案遇到了一个问题。

我们梳理一下简化后的项目上线过程:

开发 -> 编译 -> 上线

假设本次修改了静态文件中的一个 JS 文件,这个文件会通过 CDN 方式在 HTML 里引用,那么最终在 HTML 文档中的引用方式是 <script src="http://cdn.com/index.js"></script>。然而由于项目还没有上线,所以其实通过完整 URL 的方式是获取不到这个文件的;而预渲染的构建又是在上线动作之前,所以问题就产生了:

构建时预渲染无法正常获取文件,导致编译报错

怎么办?

请求劫持

因为在做预渲染时,我们使用启动了一个模拟的浏览器环境,根据 phantom 提供的 API,可以对发出的请求加以劫持,将获取 CDN 文件的请求劫持到本地,从而在根本上解决了这个问题。示例代码如下:

构建时预渲染研发流程及效果

最终,构建时预渲染研发流程如下:

开发阶段:

  • 通过 TypeScript 的装饰器单行引入预渲染构建触发的方法。
  • 发布前修改编译构建的配置文件。

发布阶段:

  • 先进行常规的项目构建。
  • 若有预渲染相关配置,则触发预渲染构建。
  • 通过预渲染得到最终的文件,并完成发布上线动作。

完整的用户请求路径如下:

通过构建时预渲染在项目中的使用,FCP 的时间相比之前减少了 75%。

作者简介

  • 寒阳,美团资深研发工程师,多年前端研发经历,负责美团支付钱包团队和美团支付前端基础技术。

招聘信息

我们美团金融服务平台大前端研发组在高速成长中,我们欢迎更多优秀的 Web 前端研发工程师加入,感兴趣的朋友可以将简历发送到邮箱:shanghanyang@meituan.com。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/479090.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文浅尝 | 利用指针生成网络的知识图谱自然语言生成

论文笔记整理&#xff1a;谭亦鸣&#xff0c;东南大学博士&#xff0c;研究方向为知识图谱问答。来源&#xff1a;Neurocomputing 382: 174-187 (2020)链接&#xff1a;https://www.sciencedirect.com/science/article/abs/pii/S0925231219316820?via%3Dihub指针生成网络在自然…

学术工业界大佬联合打造:ML产品落地流程指南

文 | 白鹡鸰给小铁比了个心编 | 小轶给白鸟鸟比了个赞卖萌屋原创出品&#xff0c;本文禁止转载前言现在已经有了许多现成的ML开发部署工具&#xff0c;所以想要完成一个ML产品并不困难。但在实际开发过程中&#xff0c;人多手杂&#xff0c;免不了一顿兵荒马乱。相比之下&#…

基于GAN的个性化短标题生成在1688平台的实践应用

原文链接&#xff1a;https://developer.aliyun.com/article/770631 基于GAN的个性化短标题生成在1688平台的实践应用 在电商情境下&#xff0c;卖家为了吸引买家兴趣&#xff0c;也为了提高商品被搜索引擎检索命中的概率&#xff0c;通常趋向于写过于冗长的商品标题。如何从过…

LeetCode 16. 最接近的三数之和(固定左端+滑动窗口)

1. 题目 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数&#xff0c;使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。 例如&#xff0c;给定数组 nums [-1&#xff0c;2&#xff0c;1&#xff0c;-4], 和 …

Android官方开发文档Training系列课程中文版:OpenGL绘图之添加动态效果

原文地址&#xff1a;http://android.xsoftlab.net/training/graphics/opengl/motion.html 在屏幕上绘制物体只是OpenGL基础的基础&#xff0c;除了OpenGL&#xff0c;你还可以使用Canvas及Drawable对象做到同样的功能。OpenGL还提供了额外的功能&#xff0c;我们可以使用这些…

领域应用 | 2020 年中国知识图谱行业分析报告

本文转载自公众号&#xff1a;艾瑞咨询。 核心摘要&#xff1a;人工智能本质是解决生产力升级的问题&#xff0c;人类生产力可以归类为知识生产力和劳动生产力&#xff0c;人工智能走入产业后&#xff0c;可以分为感知智能、认知智能和行为智能&#xff0c;后两者更与生产力相…

Category 特性在 iOS 组件化中的应用与管控

背景 iOS Category功能简介 Category 是 Objective-C 2.0之后添加的语言特性。 Category 就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下&#xff0c;动态地给这个类添加一些方法。在 Objective-C&#xff08;iOS 的开发语言&#xff0c;下文用 OC 代替&…

OpenAI亲谈:我们眼中的GPT-3、大规模语言模型的局限性与出路在哪

编译 | 陈彩娴、青暮编辑 | 陈大鑫近日&#xff0c;OpenAI政策研究主管Miles Brundage在推特上分享了一篇新论文&#xff0c;论文内容是对一个GPT-3研讨会的总结。2020年10月14日&#xff0c;来自OpenAI、斯坦福大学HAI研究所等机构的研究人员召集在一起&#xff0c;讨论围绕GP…

事实感知的生成式文本摘要

哈工大的事实感知的生成式文本摘要

Android官方开发文档Training系列课程中文版:OpenGL绘图之响应触摸事件

原文地址&#xff1a;http://android.xsoftlab.net/training/graphics/opengl/touch.html 使图形按照程序设计的轨迹旋转对OpenGL来说还是不能发挥出它应有的实力。但要是能使用户可以直接控制图形的旋转&#xff0c;这才是OpenGL的真正目的。它真正的关键所在就是使程序可以交…

LeetCode 26. 删除排序数组中的重复项

1. 题目 给定一个排序数组&#xff0c;你需要在原地删除重复出现的元素&#xff0c;使得每个元素只出现一次&#xff0c;返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 来源&#xff1a;力扣&…

论文浅尝 | Data Intelligence - 多篇语义资源论文

本文转载自公众号&#xff1a;DI数据智能 。 编者按&#xff1a;Data Intelligence最新发表一组语义资源论文&#xff0c;包括世界著名语义网技术专家荷兰阿姆斯特丹自由大学计算机科学系Frank van Harmelen教授团队的Constructing and Cleaning Identity Graphs in the LOD C…

人物志 | 美团首席科学家夏华夏:不断突破边界的程序人生

“成长没有什么秘笈&#xff0c;就是坚持不断地一点点突破自己的边界就好。” 这是美团首席科学家、无人配送部总经理夏华夏在刚刚过去的“1024 程序员节”时送给技术同行的一句话。 这也是夏华夏自己的人生写照&#xff1a;从没摸过计算机的山东高考状元到清华计算机系的学霸&…

我的《Android官方开发文档Training系列课程中文版》的中期翻译计划

从2016年的3月份开始到现在&#xff0c;对于Android文档的翻译已经进行了两个月的时间。虽然数量还不及总篇数的一半&#xff0c;但是经过一番整理&#xff0c;发现翻译的文章还不少&#xff0c;目前为止已经有56篇了。这个过程也陆陆续续的坚持了下来。现在回头看刚开始翻译的…

Github星标超3k的推荐系统入门资料合集(含教程、论文、代码、数据)

本篇文章是对公众号《机器学习与推荐算法》历史文章的汇总以及对干货内容的梳理&#xff0c;力争把最全面的干货与最完整的知识体系以最清晰的方式呈现给大家&#xff0c;希望大家能够精准快速地获取到自己想学习的内容&#xff0c;尽到一个干货推荐系统应尽的职责。1 历史文…

Python3中遇到UnicodeEncodeError: ‘ascii‘ codec can‘t encode characters in ordinal not in range(128)

原文链接&#xff1a;https://blog.csdn.net/th_num/article/details/80685389 Python3中遇到UnicodeEncodeError: ascii codec cant encode characters in ordinal not in range(128) 但是在windows上面运行代码正常。 原因是因为&#xff1a;linux系统语言导致的。 查看了一下…

论文浅尝 | 低资源文本风格迁移数据集

来源&#xff1a;AAAI2020论文链接&#xff1a;https://www.msra.cn/wp-content/uploads/2020/01/A-Dataset-for-Low-Resource-Stylized-Sequence-to-Sequence-Generation.pdf概述&#xff1a;低资源样式化的序列到序列&#xff08;S2S&#xff09;生成是高需求的。但由于数据集…

Android官方开发文档Training系列课程中文版:动画视图之转场框架介绍

原文地址&#xff1a;http://android.xsoftlab.net/training/transitions/index.html 引言 Activity所呈现的UI经常会由用户的输入或者其它事件而发生变化。比如&#xff0c;一个含有输入框的Activity&#xff0c;在用户输入要查找的关键字之后&#xff0c;这个输入框就会隐藏…

机器学习竞赛中,为什么GBDT往往比深度学习更有效?

在过去的几年里&#xff0c;大多数的推荐算法都是基于深度学习&#xff08;DL&#xff09;方法。遵循我们领域的一般研究实践&#xff0c;这些工作证明了新的DL方法在离线实验中优于其他不基于深度学习的模型。然而&#xff0c;在与推荐相关的机器学习竞赛中&#xff08;如与年…

2020年算法工程师技术路线图

原文链接&#xff1a;https://cloud.tencent.com/developer/article/1689082 重磅干货&#xff0c;第一时间送达作者丨字节知乎来源丨https://zhuanlan.zhihu.com/p/192633890极市导读算法工程师如何获得技术方面的成长&#xff1f;本文从工程基础、算法基础、算法工程交叉、工…