阅读更多
前言
针对首页和部分页面打开速度慢的问题,我们开始对单页式应用性能进行优化。本文介绍其中一个方案:基于 HTTP Chunk 的首屏数据渐进式预加载方案,该方案总体减少了单页应用1.2s的首屏呈现时间。同时对比其与同构渲染方案的异同。

背景介绍
单页式应用是近几年来前端技术栈发展与落地的最典型场景,Angular、Vue、React等,这些相关的技术栈目的都是从架构层面为单页式应用提供研发解决方案,着重解决单页式应用的研发效率。基础框架的进化也催生着关联工具链路的发展,如 Yeoman,Grunt -> Gulp -> 各种 cli,Webpack1/2, Babel 等。

随着研发链路体系的稳定成熟,在功能上能够及时满足用户后,我们开始将精力集中关注产品的可用性层面。经过和产品,运营,用研等多个团队配合,我们走访了多位使用我们产品的用户,产出了一份流量端产品可用性得报告。除了部分交互和产品流程设计上的问题,另一个主要问题就是用户反馈整体的系统流畅性不错,但首页和部分页面打开极其慢,针对这块问题,我们开始了对单页式应用性能优化的探索和实践。

本文接下来将一步一步阐述对应用首屏呈现中各个节点的拆解,并根据拆解的节点推导出我的优化思路,最终为大家介绍我提出并尝试的第一个性能优化方案: 首屏数据渐进式预加载

首屏呈现节点分析
在进行任何的性能优化之前,我们都应该先找出系统的性能瓶颈点,从而找出最有价值的优化方向。

绝大多数的单页式应用都符合 Application Shell 架构,根据这个架构我们可以看出一个应用首屏呈现节点可以分解为:请求入口页 -> 渲染应用外壳 -> 渲染首屏片段。我在此基础上进一步将三个节点细分如下:

即对渲染应用外壳和渲染片段这块细分为:应用资源加载,应用初始化,片段资源加载,片段初始化,片段数据加载,片段渲染这些节点。

有了这些细分节点,再将埋点记录的真实用户数据代入:

得出我们的首屏时间为:

T(s) = T1 + ... + T7 = 2800ms

注:我们一般都将首屏资源一起与应用资源打包在一起,因此这里耗时认为是0。

整个 timeline 如下:

首屏数据渐进式预加载方案
根据上面的节点数据,首屏数据渐进式预加载的优化思路也得到了体现:
  • 优化首屏数据加载节点的速度。
  • 预先加载首屏数据,使得多个串行节点并行化。
接下来详细介绍我们的优化步骤。第1点会在第一步优化中体现,但核心思路和主要优化收益更多体现在第2点:多个串行节点并行化

Step1:资源文件下载与首屏数据请求节点并行

为了达到资源下载与数据请求并行的效果,我们充分利用了HTTP Chunk 传输与浏览器的渐进式渲染特性
1、将入口页分为静态片段和数据片段:静态片段包含了各个资源标签(script,link),静态的导航栏,加载指示器等;数据片段则是包含首屏数据的内联脚本,大至如下:
   
<script>window.__APP_DATA__ = { /* 相关的首屏数据 */ };</script>

2、浏览器请求入口页时,入口页服务器(这里我们用了 NodeJS ) 并行 做以下操作:
  • HTTP Chunk 方式输出静态片段
  • 请求首屏数据并在所有数据请求完成后将数据片段和应用初始化代码返回给浏览器。
注:http chunk 方式输出在 NodeJS 中及其容易满足,简单的 res.write(chunk) 即可。

整体架构如下:

浏览器的渐进式渲染特性在收到静态片段并解析后立刻去下载资源,由此巧妙的将应用资源加载节点和首屏数据请求节点并行化;当应用初始化完毕后,首屏组件直接读取window.__APP_DATA__拿到数据渲染即可。

整个首屏呈现 timeline 变化如下:

最终并行化这块耗时为:Max(下载资源文件,请求首屏数据输出片段) = 1000ms。
根据变化后的节点我们算出首屏呈现时间为:2350ms

首屏呈现耗时的通用计算公式变为:

下载静态片段 + Max(下载资源文件,请求首屏数据) + 应用初始化 + 首屏初始化 + 首屏渲染

Step2:应用初始化,资源文件下载,首屏数据请求节点并行

在 Step1 的基础上继续分析,应用初始化节点耗时也很明显,同时该节点要进行必须等待资源文件下载完毕,但理论上可以不依赖我们的首屏数据,还是可以让其和首屏数据请求并行。

这里我们无法在 Step1 方案上直接将应用初始化和数据请求并行化,主要原因在于当首屏数据请求时间大于资源加载+应用初始化完成时间时,应用会在没有数据的情况下进入收入首屏渲染节点,从而导致异常。

解决方案是将数据片段的输出变成 promise 片段:
1、pending promise 片段,与静态片段一起输出,大概如下:
    <script>
    window.__APP_DATA__ = {
     RESOLVERS: {}
     userInfo: new Promise((resolve, reject) => {
       // 超时认为失败
       let timer = setTimeout(reject.bind(null, {message: 'timeout'}), 12000);
       window.__APP_DATA__.userInfo = (err, data) => {
         clearTimeout(timer);
         err ? reject(err) : resolve(data)
       }
     })
    };
    </script>

2、resolve promise 片段,该片段在数据请求成功返回后输出,大概如下:
   
<script>window.__APP_DATA__.RESOLVERS.userInfo(null, data); </script>

3、reject promise 片段,该片段在数据请求失败后输出,大概如下:
   
<script>window.__APP_DATA__.RESOLVERS.userInfo(error); </script>

即此时应用初始化完毕后可以无视首屏数据的完成度,直接进入首屏渲染节点,组件在数据 promise 被 resolve 后渲染即可:window.__APP_DATA__.userInfo.then(data => component.render());

通过对数据片段的promise化改造,使得应用初始化节点也加入了并行队列。

整个首屏呈现 timeline 变化如下:

根据变化后的节点我们得到首屏呈现时间为:1800ms

首屏呈现耗时的通用计算公式变为:

下载静态片段 + Max(下载资源文件 + 应用初始化,请求首屏数据) + 首屏初始化 + 首屏渲染

优化小结

经过上述2个步骤改进,我们应用首屏呈现时间从 2800ms -> 2350ms -> 1800ms,总体效果约为36%,可以看到是收益还是很可观的。

在实际项目中耗时是在1600ms左右,比1800ms还要小,主要原因如下:
  • 用户在请求入口页中半个RTT时间,服务器就开始了数据请求。
  • 数据请求在服务端进行减少了浏览器与服务端的请求创建开销,同时数据请求在内网进行,总体调用速度也会加快。
当首屏数据请求数超过浏览器并发请求数时,该方案收益会更明显,因为 NodeJS 端没有并发限制,甚至在NodeJS端与后端服务的交互中可以采用更高效的协议如HTTP2来提高调用速度。

与服务端同构渲染对比
看到这里,相信很多人会问,为啥不用服务端渲染直出HTML呢,或者和服务端渲染方案相比有何优势?

事实上,一开始我和大多数人想到的优化方案就是服务端渲染,但真正的障碍在于服务端渲染依赖视图层框架的支持,而我们的项目历史悠久,视图层框架并不支持这一点,为了优化而丧失产品的稳定性得不偿失。

当然,在另辟蹊径使用了数据渐进式预加载方案后,我总结该方案与与服务端同构渲染对比如下。

优势
  • 对客户端代码来说数据渐进式预加载方案实现成本非常简单,基本可以做到透明化,我们在实际的开发过程中采用基于 uIoC(ecomfe/uioc ) 提供的AOP拦截方案,通过配置化的方式让客户端的代码改造仅局限在配置文件,应用代码基本未改动。
  • 对NodeJS端来说,分层合理的应用只需要将数据层简单适配下 NodeJS 端即可完成数据渐进式预加载,这对底层基础框架在视图层没有支持同构的应用来说,整个改造成本可以说大大减小,且收益明显。我们目前的应用基于自有的一套MVC框架,仅仅是将 Model 层简单适配 NodeJS 端执行输出数据。
  • 服务端渲染方案如果未能提供较基于 BigPipe 的渲染,总体的页面呈现速度还是不如数据渐进式预加载的,且目前我也暂时还没有在三大框架中发现有一套基于BigPipe的服务端渲染方案。
不足
整体呈现速度可能不如结合了BigPipe的服务端渲染方案,但这点没有经过论证,毕竟数据渐进式预加载与服务端同构渲染的区别仅仅在于渲染环节放在客户端还是服务端:渲染看的是CPU,服务端的CPU资源是有限的,要服务诸多请求,而客户端渲染则基本无此压力,渲染能力未必弱于服务端。

总结
我们在单页应用的性能优化上基于很朴素的并行化理念实施了首屏数据渐进式预加载方案,在实际项目中也得到了较为明显的效果,减少了1.2s的加载时间,整体的节点变化如下:

优化前:

优化后:

最终数据渐进式预加载方案的首屏呈现时间计算公式为:

下载静态片段 + Max(应用资源加载 + 应用初始化,请求首屏数据) + 首屏初始化 + 首屏渲染

这里忽略了影响很小的片段传输时间,有打算尝试的朋友可以将自己应用的相关节点数据代入计算即可。

数据渐进式预加载,服务端同构渲染,客户端渲染三种方案各有优缺和场景,个人未来计划是将三种方案结合实时流量数据动态切换:在服务器压力不大时用同构渲染;服务器压力较大时用数据预加载;服务器压力很大时用客户端渲染。
  • 大小: 45.2 KB
  • 大小: 39.4 KB
  • 大小: 68.9 KB
  • 大小: 52.5 KB
  • 大小: 63.8 KB
  • 大小: 47.8 KB
  • 大小: 53.2 KB
来自: zhihu
1
0
评论 共 1 条 请登录后发表评论
1 楼 imtpcf 2017-07-03 11:25
文章写得很好,有帮助。

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 百度SSP单页式应用性能优化实践

    本文将介绍其中一个性能优化方案:基于HTTPChunk的首屏数据渐进式预加载方案,该方案减少了1.2s的加载时间。同时对比其与同构渲染方案的异同。单页式应用是近几年来前端技术栈发展与落地的最典型场景,Angular、Vue...

  • angular 首屏加载优化_单页式应用性能优化-首屏数据渐进式预加载

    本文介绍其中一个方案:基于 HTTP Chunk 的首屏数据渐进式预加载方案,该方案总体减少了单页应用1.2s的首屏呈现时间。同时对比其与同构渲染方案的异同。注:原文为百度SSP单页式应用性能优化实践,专栏这里以稍微...

  • 【 Vue3 性能优化】页面加载性能 与 更新性能

    Vue 在大多数常见场景下性能都是很优秀的,通常不需要手动优化。然而,总会有一些具有挑战性的场景需要进行针对性的微调。

  • 使用 React.js 的渐进式 Web 应用程序:第 2 部分 - 页面加载性能

    系列第二篇,来看看基于 React 路由分块的页面加载优化。 原文地址:Progressive Web Apps with React.js: Part 2 — Page Load Performance 原文作者:Addy Osmani ...使用 React.js 的渐进式 Web 应用程序...

  • 谈谈前端性能优化-面试版

    当我们去面试的时候,很大概率会被面试官问这么一个问题:你有尝试过对项目做性能优化吗?或者你了解哪些性能优化的方法?听到这个问题的你可能是这样的:似曾相识但又说不清楚,往往只能零散地说出那么几点,难以...

  • Vue -渐进式JavaScript框架

    介绍vue 中文网vue githubVue.js 是一套构建用户界面(UI)的渐进式JavaScript框架库和框架的区别我们所说的前端框架与库的区别?Library库,本质上是一些函数的集合。每次调用函数,实现一个特定的功能,接着把控制权...

  • 首屏渐进式渲染设想

    这篇文章是关于 HTML 渲染的首屏优化的,主要是针对 web app 这种相对交互较多的应用场景,首屏优化主要为了改善用户对于页面的感知,而服务端渲染(SSR)是 web app 优化首屏依赖的重要手段. 昨晚录了视频, 解释了一遍, ...

  • 前端性能优化总结

    前端性能优化总结

  • 前端框架/架构,性能优化,负载均衡,首屏渲染

    前端数据结构与算法- https://zhuanlan.zhihu.com/p/27659059&amp;gt; 前端重构方案 前端重构方案了解一下- https://blog.csdn.net/vM199zkg3Y7150u5/article/details/80681697 webApp重构之路——性能和用户体验...

  • 前端项目性能优化(全面解析)

    基于HAR存储于重建性能信息 重要测量指标 速度指数(Speed Index)4秒 TTFB 衡量请求到响应一共多少时间 页面加载时间,页面加载完一共要用多久 首次渲染 不能一直是白屏,然后在后来的某一刻突然都出来,要给用户一...

  • Vue -渐进式 JavaScript 框架

    Vue -渐进式 JavaScript 框架 介绍 官网 github Vue.js 是一套构建用户界面(UI)的渐进式 JavaScript 框架 库和框架的区别 库: ?&amp;gt; 库,本质上是一些函数的集合。每次调用函数,实现一个特定的功能,...

  • vue 判断页面加载完成_使用Vue做 SPA应用的思考及总结

    SPA应用也是我的知识复盘计划的组成部分。笔者亲身经历客户端和服务端技术变革,在以前没有所谓的前端开发概念,有些系统页面由网页设计师做好静态页面交给后端工程师的一并完成的,服务端渲染是主要的数据传递方式...

  • 聊一聊前端性能优化

    性能优化 ,每个工程师跑不掉的一个话题。这里是本人结合Mr.Max的一门课,总结的一些优化手法,希望对大家有所帮助,后续也会继续更新,想要看视频的可以直接去某课网搜。附 演示源码和PPT...

  • 单页面应用首屏时间慢问题解决方法

    路由的懒加载 加一个首屏loading图或骨架屏,提高用户的体验 尽可能使用CSS Sprites和字体图标库 图片的懒加载 将公用的JS库通过script标签... 首屏数据渐进式预加载 https://zhuanlan.zhihu.com/p/26543645 ...

  • python源码基于mediapipe设计实现人体姿态识别动态时间规整算法DTW和LSTM(长短期记忆循环神经网络.rar

    本项目基于Python源码,结合MediaPipe框架,实现了人体姿态识别功能,并进一步采用动态时间规整算法(DTW)和长短期记忆循环神经网络(LSTM)对人体动作进行识别。项目涵盖了从姿态估计到动作识别的完整流程,为计算机视觉和机器学习领域的研究与实践提供了有价值的参考。 MediaPipe是一个开源的多媒体处理框架,适用于视频、音频和图像等多种媒体数据的处理。在项目中,我们利用其强大的姿态估计模型,提取出人体的关节点信息,为后续的动作识别打下基础。DTW作为一种经典的模式匹配算法,能够有效地处理时间序列数据之间的差异,而LSTM则擅长捕捉长时间序列中的依赖关系。这两种算法的结合,使得项目在人体动作识别上取得了良好的效果。 经过运行测试,项目各项功能均表现稳定,可放心下载使用。对于计算机相关专业的学生、老师或企业员工而言,该项目不仅是一个高分资源,更是一个难得的实战演练平台。无论是作为毕业设计、课程设计,还是项目初期的立项演示,本项目都能为您提供有力的支持。

  • web期末大作业-电影动漫的源码案例.rar

    本学期末,我们为您呈现一份精心准备的电影动漫源码案例,它不仅是课程设计的优秀资源,更是您实践技能的有力提升工具。经过严格的运行测试,我们确保该案例能够完美兼容各种主流开发环境,让您无需担心兼容性问题,从而更加专注于代码的学习与优化。 这份案例资源覆盖了前端设计、后端逻辑处理、数据库管理等多个关键环节,旨在为您提供一个全面而深入的学习体验。无论您是计算机专业的在校学生,还是对编程充满热情的爱好者,亦或是希望提升技能的企业员工,这份案例都将为您提供宝贵的实战经验。 此外,我们还特别准备了详细的使用指南和在线支持,确保您在学习和使用的过程中能够得到及时有效的帮助。您可以放心下载和使用这份资源,让它成为您学习道路上的得力助手。让我们携手共进,通过实践探索编程的无限可能!

  • java图书管理系统毕业设计(源代码+lw).zip

    本设计是为图书馆集成管理系统设计一个界面,图书馆集成管理系统是用MICROSOFT VISUAL Foxpro 6.0 来建库(因特殊原因该用 MICROSOFT Access来建库)。它包括: 中文图书数据库; 西文图书数据库; 发行商数据库; 出版商数据库; 读者数据库; 中文期刊数据库; 西文期刊数据库; 中文非印刷资料库; 西文非印刷资料库; 典藏库; 流通库; 预约库; 流通日志库;

  • 项目实战+C#+在线考试系统+毕业项目

    该系统主要以在线模拟考试使用为出发点,以提高学生的学习效率和方便学生随时随地检测学习成果为目的,主要采用了DreamweaverMX、FireworksMX、FrontPage软件进行设计、使用ASP开发语言进行编程,所选用的数据库是微软公司开发的Access数据库。 ASP是通过一组统称为ADO的对象模块来访问数据库,ASP提供的ADO对象模块包含6个对象和3个集合,常用的有Connection、 Record set 、Command 、field等对象。 ASP是一种服务器端的指令环境,用来建立并执行请求的交互式WEB服务器端运行的应用程序, ASP程序只能在WEB服务器端执行,当浏览器向服务器要求运行ASP程序时,服务器会读取该程序,然后执行该程序并将结果转换为HTML文件 ,再将HTML文件传送给浏览器,待浏览器收到HTML文件以后,便会将执行结果显示在浏览器上。

  • 应用背景这个源码包非常适合研究蚁群算法的同学使用,

    应用背景这个源码包非常适合研究蚁群算法的同学使用,其中介绍了蚁群算法的基本程序,有利于各位从中吸取有益经验,也可以结合其它方法进行改善。关键技术蚁群算法应用广泛,在此提供一个可以运行的、完整的源码以供参考,各位可以在此基础上进行改进,改善其在不同场合中的应用效果Matlab.zip

  • 传递矩阵中矩阵元素的传递与变换.zip

    传递矩阵中矩阵元素的传递与变换.zip

Global site tag (gtag.js) - Google Analytics