东亚银行、岚图汽车带你解锁 AIGC 时代的数字化人才培养各赛道新模式! 了解详情
写点什么

前端黑科技:美团网页首帧优化实践

  • 2018-11-22
  • 本文字数:2499 字

    阅读完需:约 8 分钟

前端黑科技:美团网页首帧优化实践

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


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



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


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


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


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



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

为什么会首屏白屏?

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



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



  • 等待 HTML 文档返回,此时处于白屏状态。

  • 对 HTML 文档解析完成后进行首屏渲染,因为项目中对 id="app 加了灰色的背景色,因此呈现出灰屏。

  • 进行文件加载、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 时触发构建时预渲染。由于我们采用的是 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%。


作者简介

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


更多内容,可关注前端之巅公众号(ID:frontshow)



2018-11-22 07:003817

评论 2 条评论

发布
用户头像
有一些问题想请教:
1. 是不是必须用 history mode, hash模式是不是不行?
2. 如果created里面有调用api怎么处理?
3. 在生产最终html页面的时候, 是不是得在vue.mounted里面控制不执行api调用?
4. 在执行最终生成页面的时候 ,会不会有冲突。比如vue componet渲染创建已经存在的html元素 。
谢谢
2018-11-27 04:55
回复
用户头像
ssr与同构有什么区别?
2018-11-22 10:02
回复
没有更多了
发现更多内容

主流定时任务解决方案全横评

阿里巴巴中间件

阿里云 云原生 定时任务

05_Linux基础-NGINX编译安装^判断是否启动^修改端口^启动停止重启^相关路径^中文乱码-Windows、Linux文件传输

mycpen

Linux

Java编程之语法结构

魏铁锤

Nacos 企业版如何提升读写性能和可观测性

阿里巴巴中间件

阿里云 微服务 云原生 中间件 可观测

三面阿里,offer到手定级P7,过程很艰辛结果很满意

程序知音

程序员 阿里 java面试 后端技术 Java八股文

当你的老板站在你背后,看你处理故障......

嘉为蓝鲸

运维 IT 故障 上班

设计模式的艺术 第二十一章备忘录设计模式练习(设计一款RPG网游,为了给玩家提供更多方便,在游戏过程中可以设置一个恢复点,用于保存当前的游戏场景。如果在后续游戏过程中玩家角色“不幸牺牲”,可以返回到先前保存的场景,从所设恢复点开始重新游戏)

代廉洁

设计模式的艺术

MobPush 安卓推送消息数据解析推荐实现

MobTech袤博科技

android 移动推送

06_Linux基础-NGINX和浏览器、网页的关系-云服务器ssh登陆-安装NGINX-上传网页-压缩命令-xz-gzip-bzip2-zip-tar-配置NGINX服务器支持下载功能-备份脚本

mycpen

Linux

百余位顶级投资人齐聚无锡,DEMO CHINA创新中国峰会即将揭幕

创业邦

卡塔尔世界杯足球赛dapp系统智能合约开发

开发微hkkf5566

测评:腾讯Coding平台好不好用?

PingCode

预训练模型在金融 NLP场景下的应用

澜舟孟子开源社区

人工智能 自然语言处理 大规模预训练模型

到底什么样的数字化才是企业需要的?用2个数字化案例告诉你

优秀

数字化转型

数据赋能智慧重庆,巴适得很!

浪潮云

云计算

视频会议,WebRTC及RingCentral解决之道

RingCentral铃盛

WebRTC 视频会议 企业号九月金秋榜

【计算讲谈社】第十一讲|商用车智能驾驶商业化实践:“科技+保险”模式探索

大咖说

智能驾驶 商用车

图文实录|多模态自然语言处理最新进展

澜舟孟子开源社区

人工智能 自然语言处理 算法 后端

重拾面向对象软件设计

阿里巴巴中间件

阿里云 技术 中间件 技术代码

了解数字机器人最新发展动向,不要错过华为数字机器人秋季发布会​

王吉伟频道

RPA 机器人流程自动化 智慧政务 机器人开发 华为数字机器人

盘点:专业OKR管理工具有哪些?

PingCode

超越所有人的成就,牛顿的光芒也无法掩盖的天才数学巨人

图灵社区

数学 微积分 数学家

看了深入Java虚拟机:JVMG1GC的算法与实现文档,我悟了

程序知音

Java JVM 垃圾回收 java架构 后端技术

数字藏品系统开发,NFT数字藏品开发说明

开源直播系统源码

软件开发 NFT 数字藏品 数字藏品软件开发 数字藏品系统

【8.26-9.2】写作社区精彩技术博文回顾

InfoQ写作社区官方

优质创作周报

超越所有人的成就,牛顿的光芒也无法掩盖的天才数学巨人

图灵教育

数学 微积分 数学家

基于预训练模型的金融事件分析及应用

澜舟孟子开源社区

人工智能 自然语言处理 金融科技 大规模预训练模型

​孟子轻量化技术迈上新台阶:登顶 ZeroCLUE 和 FewCLUE 榜单,已开源并提供 SDK

澜舟孟子开源社区

人工智能 自然语言处理 后端 大规模预训练模型

阿里云对边缘计算的探索和实践

阿里云视频云

边缘计算

极狐GitLab Helm Chart 已上线,玩转云原生极狐GitLab!

极狐GitLab

DevOps gitlab 云原生 Helm Kubernetes, 云原生, eBPF

落地好OKR的8大关键步骤及常用辅助工具

PingCode

OKR

前端黑科技:美团网页首帧优化实践_大前端_寒阳_InfoQ精选文章