Web 现代应用程序架构下的性能优化,渐进式的极致艺术。

发表于 3年以前  | 总阅读数:251 次

前言

本文是 Rendering on the Web: Performance Implications of Application Architecture (Google I/O ’19)[1] 这篇谷歌工程师带来的现代应用架构体系下的优化相关演讲的总结,演讲介绍了以下优化手段:

  • 预渲染
  • 同构渲染
  • 流式渲染
  • 渐进式注水(非常精彩)

应用架构体系

当我们讨论「应用架构」的时候,可以理解为通过以下几个部分组合来构建网站。

  1. Component model 组件模型。
  2. Rendering and loading 渲染和加载。
  3. Routing and transitions 路由和过渡。
  4. Data/state management 数据、状态的管理。

image

性能指标

在分析页面渲染性能之前,先了解一下几个比较重要的指标,方便下文理解:

  1. FP: First Paint,是 Paint Timing API 的一部分,是页面导航与浏览器将该网页的第一个像素渲染到屏幕上所用的中间时,渲染是任何与输入网页导航前的屏幕上的内容不同的内容。
  2. FCP: First Contentful Paint,首次有内容的渲染是当浏览器渲染 DOM 第一块内容,第一次回馈用户页面正在载入。
  3. TTI: Time to interactive 第一次可交互时间,此时用户可以真正的触发 DOM 元素的事件,和页面进行交互。
  4. FID: First Input Delay 第一输入延迟测量用户首次与您的站点交互时的时间(即,当他们单击链接,点击按钮或使用自定义的 JavaScript 驱动控件时)到浏览器实际能够的时间回应这种互动。
  5. TTFB: Time to First Byte 首字节时间,顾名思义,是指从客户端开始和服务端交互到服务端开始向客户端浏览器传输数据的时间(包括 DNS、socket 连接和请求响应时间),是能够反映服务端响应速度的重要指标。

如果你还不太熟悉这些指标也没关系,接下来的内容中,会结合实际用例分析这些指标。

渲染开销 The cost of rendering

客户端渲染 Client-side rendering

从服务端获取 HTML、CSS、JavaScript 都是需要成本的,以一个 CSR(客户端渲染)的网站为例,客户端渲染的网站依赖框架库(bundle)、应用程序(app)来进行初始化渲染,假设它有 1MB 的 JavaScript Bundle 代码,那么只有当这一大段的代码加载并执行完成以后,用户才能看到页面。

它的结构一般如下:

分析一下它的流程:

  1. 用户输入网址进入网站,拉取 HTML 资源。

2 . HTML 资源中发现 script 标签加载的 bundle 再一次发起请求拉取 bundle。此时也是性能统计指标中的 FP 完成。

在这个阶段,页面基本上是没什么意义的,当然你也可以放置一些静态的骨架屏或者加载提示,来友好的提示用户。

3 . JavaScript bundle 下载并执行完毕,此时页面才真正渲染出有意义的内容。对应 FCP 完成。

当框架对 DOM 节点添加各类事件绑定后,用户才真正可以和页面交互,此时也对应 TTI 完成。

它的缺点在于,直到整个 JavaScript 依赖执行完成之前,用户都看不到什么有意义的内容。

服务端同构渲染 SSR with Hydration

基于以上客户端渲染的缺点以及用户对于 CSR 应用交互更加丰富的需求,于是诞生了集 SSR 和 CSR 的性能、SEO、数据获取的优点与一身的「同构渲染」,简单点说,就是:

  1. 第一次请求,在服务端就利用框架提供的服务端渲染能力,直接原地请求数据,生成包含完整内容的 html 页面,用户不需要等待框架的 js 加载就可以看到内容。
  2. 等到页面渲染后,再利用框架提供的 Hydration(注水)能力,让服务端返回的“干瘪”的 HTML 注册事件等等,变的丰富起来,拥有了各种事件后,就和传统 CSR 一样拥有了丰富多彩的客户端交互。

在同构应用中,只要 HTML 页面返回,用户就可以看到丰富多彩的页面:

而 JavaScript 加载完毕后,用户就可以和这些内容进行交互(比如点击放大、跳转页面等等……)

代码对比

典型的 CSR React 应用的代码是这样的:

而 SSR 的代码则需要服务端的配合,

先由服务端通过 ReactDOMServer.renderToString 在服务端把组件给序列化成 html 字符串,返回给前端:

前端通过 hydrate 注水,使得功能交互变的完整:

Vue 的 SSR 也是同理:

同构的缺陷

至此看来,难道同构应用就是完美的吗?当然不是,其实普通的同构应用只是提升了 FCP 也就是用户看到内容的速度,但是却还是要等到框架代码下载完成,hydrate 注水完毕等一系列过程执行完毕以后才能真正的可交互

并且对于 FID 也就是 First Input Delay 第一输入延迟这个指标来说,由于 SSR 快速渲染出内容,更容易让用户误以为页面已经是可交互状态,反而会使「用户第一次点击 - 浏览器响应事件」 这个时间变得更久。

因此,同构应用很可能变成一把「双刃剑」。

下面我们来讨论一些方案。

Pre-rendering 预渲染。

对于不经常发生变化的内容来说,使用预渲染是一种很好的办法,它在代码构建时就通过框架能力生成好静态的 HTML 页面,而不是像同构应用那样在用户请求页面时再生成,这让它可以几乎立刻返回页面。

当然它也有很大的限制:

  1. 只适用于静态页面。
  2. 需要提前列举出需要预渲染的 URLs。

流式渲染 Streaming

流式渲染可以让服务端对大块的内容分片发送,使得客户端不需要完整的接收到 HTML,而是接受到第一部分时就开始渲染,这大大提升了 TTFB 首字节时间。

在 React 中,可以通过 renderToNodeStream 来使用流式渲染:

渐进式注水 Progressive Hydration

我们知道 hydrate 的过程需要遍历整颗 React 节点树来添加事件,这在页面很大的情况下耗费的时间一定是很长的,我们能否先只对关键的部分,比如视图中可见的部分,进行「注水」,让这部分先一步可以进行交互?

想象一下它的特点:

  1. 组件级别的渐进式注水。
  2. 服务端依旧整页渲染。
  3. 页面可以根据优先级来分片“启动”组件。

通过一张动图来直观的感受一下普通注水(左)和渐进式注水(右)的区别:

可以看到用户第一次可以交互的时间大大的提前了。

光说不做假把式,我们看看用 React 完成这个功能的代码,首先我们需要准备一个组件 Hydrator 用来实现当某个组件进入视图范围以后再进行注水。

首先来看看应用的整体结构:

let load = () => import('./stream');
let Hydrator = ClientHydrator;

if (typeof window === 'undefined') {
  Hydrator = ServerHydrator;
  load = () => require('./stream');
}

export default function App() {
  return (
    <div id="app">
      <Header />
      <Intro />
      <Hydrator load={load} />
    </div>
  );
}

根据客户端和服务端的环境区分使用不同的 Hydrator,在服务端就直接返回普通的 html 文本:

function interopDefault(mod) {
  return (mod && mod.default) || mod;
}

export function ServerHydrator({ load, ...props }) {
  const Child = interopDefault(load());
  return (
    <section>
      <Child {...props} />
    </section>
  );
}

而客户端,则需要实现渐进式注水的关键部分:

export class Hydrator extends React.Component {
  render() {
    return (
      <section
        ref={c => (this.root = c)}
        dangerouslySetInnerHTML={{ __html: '' }}
        suppressHydrationWarning
      />
    );
  }
}

首先 render 部分,利用 dangerouslySetInnerHTML 来使得这部分初始化为空的 html 文本,并且由于 server 端肯定还是和往常一样全量渲染内容,而客户端由于初始化需要先不做任何处理,会导致 React 内部对于服务端内容和客户端内容的「一致性检测」失败。

而利用 dangerouslySetInnerHTML 的特性,会让 React 不再进一步 hydrate 遍历 children 而是直接沿用服务端渲染返回的 HTML,保证在注水前渲染的样式也是 OK 的。

再利用 suppressHydrationWarning 取消 React 对于内容一致性检测失败的警告。

export class Hydrator extends React.Component {
  componentDidMount() {
    new IntersectionObserver(async ([entry], obs) => {
      if (!entry.isIntersecting) return;
      obs.unobserve(this.root);

      const { load, ...props } = this.props;
      const Child = interopDefault(await load());
      ReactDOM.hydrate(<Child {...props} />, this.root);
    }).observe(this.root);
  }

  render() {
    return (
      <section
        ref={c => (this.root = c)}
        dangerouslySetInnerHTML={{ __html: '' }}
        suppressHydrationWarning
      />
    );
  }
}

接下来,组件在客户端初始化的时候,利用 IntersectionObserver 监控组件元素是否进入视图,一旦进入视图了,才会动态的去 import 组件,并且利用 ReactDOM.hydrate 来真正的进行注水。

此时不光注水是动态化的,包括组件代码的下载都会在组件进入视图时才发生,真正做到了「按需加载」。

动图中紫色动画出现,就说明渐进式 hydrate 完成了。

对比一下全量注水和渐进式注水的性能会发现首次可交互的时间被大大提前了:

当然,我们了解原理就发现,不光可以通过监听组件进入视图来 hydrate,甚至可以通过 hoverclick 等时机来触发,根据业务需求的不同而灵活调整吧。

可以访问图片中的网址获取你喜欢的框架在这方面的相关文章:

总结

本文通过总结了 Rendering on the Web: Performance Implications of Application Architecture (Google I/O ’19)[2] 这段 Google 团队的精彩演讲,来介绍了现代应用架构体系中的优化手段,包括:

  • 预渲染
  • 同构渲染
  • 流式渲染
  • 渐进式注水

在不同的业务场景下选择对应的优化手段,是一名优秀的前端工程师必备的技能,相信看完这篇文章的你一定有所收获。

参考资料

[1]Rendering on the Web: Performance Implications of Application Architecture (Google I/O ’19): https://www.youtube.com/watch?v=k-A2VfuUROg&feature=youtu.be&t=1036

[2]Rendering on the Web: Performance Implications of Application Architecture (Google I/O ’19): https://www.youtube.com/watch?v=k-A2VfuUROg&feature=youtu.be&t=1036

本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/qsrwU6bPTVK6JY-czvXhgQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237231次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8065次阅读
 目录