写点什么

JavaScript 的成本

  • 2018-01-02
  • 本文字数:3946 字

    阅读完需:约 13 分钟

在建立那些严重依赖于 JavaScript 网站的时候,有时我们会为自己发送的内容付出一些隐形的成本。在本篇文章中,我会介绍一些可以帮助你提升网站在移动设备上加载和运行速度的实用规则

tl;dr:更少的代码 = 更少的解析 / 编译 (parse/compile)+ 更少的传送 + 更少的解压缩

网络

大多数开发人员考虑 JavaScript 成本的时候,考虑的都是下载和执行成本。通过线路发送的 JavaScript 字节越多,所需时间就越长,用户连接就越慢。

即使是在发达国家,这也可能是一个问题,因为用户实际上用的有效网络连接类型可能并不是 3G、4G 或者 Wifi。表面上你可能连的是咖啡店的 Wifi,但实际上连到的是只有 2G 速度的蜂窝热点。

你可以通过以下的几种方式来降低 JavaScript 的网络传输成本:


(减少向用户发送 JavaScript 量的最佳做法。)

解析 / 编译

下载成功后,JavaScript** 绝大部分的时间都消耗在 JS 引擎对下载代码的解析 / 编译 ** 上。在 Chrome DevTools 中,解析和编译是下面性能面板(Performance panel)中黄色“脚本”时间的一部分。

Bottom-Up/Call Tree 允许我们去确切地查看解析 / 编译所用时间:


Chrome DevTools 性能面板下级菜单 >Bottom-U。启动 V8 的 Runtime Call Stats,就能看到不同阶段的时间消耗,比如解析 / 编译所用时间。

但是,这为什么会是个问题?

耗费很长的时间在解析 / 编译代码上,会严重延迟用户与你网站的交互时间。你发送的 JavaScript 越多,在网站实现交互前所用的解析 / 编译的时间就会越长

即使是同样多的字节,浏览器处理 JavaScript 也会比处理等大小的图片和网页字体消耗更高的成本——Tom Dale

相比于 JavaScript,处理等字节的图片所需要的时间成本很高(因为图片仍需要解码!)但是在一般的移动设备上,反而是 JS 更有可能对页面的交互产生负面的影响。


(JavaScript 字节和图像字节耗费的时间成本不同。图像通常不会阻塞主线程,也不会在解码和光栅化的时候阻止接口进行交互。然而 JS 会因为解析、编译和执行的时间消耗阻滞交互性。)

当我们说解析和编译的速度变慢的时候,要注意具体的网络端和设备端的情况,在这里我们针对的是普通手机。普通用户所使用手机的 CPU 和 GPU 速度比较慢,没有 L2/L3 缓存,甚至可能会有内存限制。

网络功能和设备功能并不总是相匹配的。有速度惊人的光纤连接的用户不一定会有最好的 CPU 来解析和评估发送到他们的设备的 JavaScript。反过来也是如此…你可能有糟糕的网络连接,但却有快速的 CPU。 - Kristofer Baxter,LinkedIn

JavaScript Start-up Performance 一文中,我曾提到过在低端和高端硬件上解析~1MB 解压缩过(简单)的 JavaScript 所需要消耗的时间。市面上的普通手机和运行速度最快的手机相比,解析 / 编译代码的所用的时间会有 2-5 倍的差距。


(在不同级别的台式和移动设备上解析 1MB 的 JavaScript 包(经 gzip 压缩,大小约为 250KB )。当分析解析成本时,我们需要考虑的是解压后的数据量,例如〜250KBgzip 压缩过的 JS 解压缩后约为〜1MB 的代码。)

那解析 / 编译真实网站的时间差异又会是怎样呢,比如 CNN.com 网站?

在高端的 iPhone 8 上解析 / 编译 CNN 网站的 JS 大约花费了 4 秒,相比于普通手机(Moto G4)的 13 秒左右。这可以显著地影响用户与 CNN 网站实现完全交互的速度。


(苹果公司的 A11 仿生芯片和更普通的 Android 硬件中的 Snapdragon 617 的解析时间上的性能比较

这就突出了在普通硬件(比如 Moto G4)上测试的重要性,而不仅仅是在自己恰好有的手机上测试。基于 _ 自己 _ 客户原有的设备和网络条件来进行优化是很重要的。

分析可以使你更加深入了解自己真实客户访问网站所用的移动设备的级别和这些设备 CPU/GPU 的局限性。

我们真的发送了太多的 JavaScript 了吗?呃…真有可能:)

用 HTTP Archive(qian500K 站点)来分析移动设备上 JavaScript 的状态时,我们可以看到,50%的站点需要 14 秒才能取得交互。这些网站光是用来解析和编译 JS 的时间就长达 4 秒。

考虑到获取和处理 JS 和其他资源所耗费的时间,也就不奇怪用户可能需要在页面可用之前等待一段时间了。我们绝对可以在这个方面做的更好。

从网页中删除不必要的 JavaScript 可以减少传输时间、CPU 密集型解析和编译以及潜在的内存消耗,同时也有助于加快网页的交互速度。

执行时间

不光是解析和编译会有时间成本。执行 JavaScript(在解析 / 编译之后运行代码)也是需要在主线程上进行的操作之一。长的执行时间也会延迟用户与你网站的交互时间。

如果脚本执行的时间超过了 50ms,那么延迟交互的时间将会是下载、编译和执行 JS 所需时间的总和——Alex Russell

为了解决这个问题,可以将 JavaScript 脚本分为几个小块来执行,以避免锁定主线程。探索一下是否可以减少脚本执行过程中进行中工作量的可能性。

减少 JavaScript 交付成本的模式

当你试图降低 JavaScript 解析 / 编译和网络传输所用时间时,类似于基于路由分块和 PRPL 这些模式也会有用。

PRPL 是一种通过激进的(aggresive)代码分割和缓存来优化交互性的模式:

为了能将PRPL 的影响以视觉化方式表现出来。

我们用V8 引擎中的Runtime Call Stats 分析了流行移动网站和progressive Web Apps(PWA)的加载时间。正如我们所看到的,解析部分(用橙色表示)是很多网站页面加载时产生显著时间消耗的部分。

Wego 网站就使用了 PRPL 来保持较低的路由解析时间,让页面交互得以快速的进行。以上的很多站点都试图采用代码分割和性能预算来降低 JS 的消耗。

JavaScript 的其他消耗

JavaScript 还可以通过其他方式来影响页面性能:

  • 存储。页面可能会因为垃圾回收(GC,garbage colleciton),页面可能会出现画面中断卡顿(junk)和暂停。因为当一个浏览器回收内存的时候,JS 的执行也会被中止,所以经常回收垃圾的浏览器会比我们想象中的更频繁地中止 JS 的执行。在这种情况下,可以通过避免内存溢出和频繁内存回收来保持页面的流畅。
  • 在运行时,长时间的运行 JavaScript 会阻塞主线程,导致页面没有响应。这种情况下,可以将脚本的工作量分成多个小的板块(具体可用 requestAnimationFrame() 或者 requestIdleCallback() 进行任务调度)来执行,以此减少页面响应的问题。

Progressive Bootstrapping

很多网站将优化内容可视性作为保证交互性所需代价的一部分。为了在 JavaScript 有大体积包体时改善首屏性能,开发人员有时会先用服务器端渲染帮助客户提前看到页面内容,然后再在 JavaScript 最终执行完成后“升级”附加上事件处理程序。

但是值得注意的是,这样做也是有代价的。你 1)通常会发送一个更大的 HTML 响应来增加交互性,2)在一段时间内,用户会处在一半的页面交互体验缺失的奇怪状况下,直到 JavaScript 处理完成。

Progressive Bootstrapping 或许会是一个更好的处理方式。浏览器请求一个最少化的功能页面(仅由当前路由所需要的 HTML/JS/CSS 组成)。当有更多的资源请求的时候,应用程序则可以懒加载(lazy-load)和解锁更多的功能。


Progressive Bootstrapping visual by Paul Lewis

仅加载可视区域内的代码是其中的关键。PRPL 和 Progressive Bootstrapping 模式均可以用来实现这一点。

结论

传输脚本的大小对低端网络至关重要,而解析时间对于 CPU 有局限性的设备很重要。降低传输脚本的大小和减少解析消耗时间是有必要的。

有团队发现采用严格的性能预算可以成功降低他们 JavaScript 的传输和解析 / 编译的时间消耗。具体可参照 Alex Russell 的“ Can You Afford It?: Real-world Web Performance Budgets ” 一文中关于移动设备预算的指导。


(考虑一下在我们所做的架构决策下,JS 有多大的空间可以让我们的应用程序具有逻辑)

如果你正在建一个用于移动设备上的站点,请尽可能的在代表性硬件上开发,保持较低的 JavaScript 解析 / 编译的时间成本,并采用性能预算来确保团队对自身 JavaScript 的成本关注。

更多相关学习


(点击观看视频,作者 2017 年在 Chrome Dev Summit 上的演讲,其中涵盖了本文内容,其后就 Pinterest 和 Tinder 这样的制作网站进行了性能案例研究

作者感谢了来自 Nolan Lawson,Kristofer Baxter 和 Jeremy Wagner 的反馈。

原文链接: The Cost Of JavaScript

感谢徐川对本文的审校。

2018-01-02 17:314187

评论

发布
暂无评论
发现更多内容

iLogtail——一款延迟仅在毫秒级的千万实例可观测采集器利器来了 | 龙蜥技术

OpenAnolis小助手

阿里云 开源 数据采集 技术分享

自我认为挺全面的【Web Service渗透测试总结】

H

网络安全 渗透测试

netty系列之:channelHandlerContext详解

程序那些事

Java Netty nio 程序那些事 2月月更

一文带你使用 Python 实现Socket编程

宇宙之一粟

Python socket 2月月更

百度AI融通创新工坊招生中!

百度大脑

图计算 on nLive:Nebula 的图计算实践

NebulaGraph

图数据库 知识图谱 图计算 分布式图数据库

大咖说|《商业评论》主编颜杰华:如何看待未来商业的管理趋势?

大咖说

大咖说 财经 数智化 商业评论

网络安全kali渗透学习 web渗透入门 NMAP高级使用技巧和漏洞扫描

学神来啦

一文搞懂MySQL体系架构

程序猿阿星

架构 MySQL 数据库 MySQL InnoDB

面试突击:说一下线程生命周期,以及转换过程?

CRMEB

剑指Offer——JVM 这些基础知识点你全掌握了吗

No Silver Bullet

JVM 垃圾回收 offer 2月月更

3步教你把个人应用服务部署到云服务器ECS上

阿里云弹性计算

Docker 征文投稿 玩转ECS

Flink on K8s 企业生产化实践|社区征文

张浩_house

flink 特征平台 新春征文

关于 AI 边云协同解决方案的研究与讨论 | 社区征文

liuzhen007

AI 新春征文 2月月更

基于飞桨实现的“太空保卫战士”——地球同步静止轨道空间目标检测系统

百度大脑

腾讯音乐知识图谱搜索实践

NebulaGraph

图数据库 知识图谱 分布式图数据库

编译ORB-SLAM 3 出现slots_reference错误

Ayosh

slam

龙蜥下游发行版 Alibaba Cloud Linux 3 安全基线正式通过 CIS 认证,云上企业安全性保障更上层楼

OpenAnolis小助手

Linux 开源 cis

罗马建立在水渠上:为什么需要优先建设绿色光网?

脑极体

Geospatial Data 在 Nebula Graph 中的实践

NebulaGraph

图数据库 知识图谱 分布式图数据库

35款FL插件免费下载

懒得勤快

开源每周问答精选:PolarDB for PostgreSQL 是专门面向 OLAP 场景吗?

阿里云数据库开源

数据库 阿里云 开源 polarDB

政企机构用户注意!蠕虫病毒Prometei正在针对局域网横向渗透传播

火绒安全

局域网共享 渗透 蠕虫 病毒 政企

使用goby检测log4j漏洞

网络安全学海

黑客 网络安全 信息安全 渗透测试 WEB安全

Web Components 系列(十一)—— 实现 MyCard 的可复用

编程三昧

前端 组件化 2月月更 webComponents

学术论坛第七期:基于统计的预测算法

云智慧AIOps社区

统计学 预测模型

SQL注入如何预防?

喀拉峻

网络安全

解密体育背后AI黑科技:花样滑冰动作识别、多模视频分类和精彩片段剪辑

百度大脑

百年奥运的凌空之美,AI云智剪背后的新算法

阿里云CloudImagine

1分钟了解Prometheus的WAL机制

johncming

Prometheus WAL

WeOpen Good 开源公益计划正式启动!聚开源智慧·行科技向善

腾源会

开源 公益 腾源会

JavaScript的成本_JavaScript_Addy Osmani_InfoQ精选文章