JavaScript 的时间消耗

2017-12-24 dwqs 前端那些事儿

随着我们的网站越来越依赖 JavaScript, 我们有时会(无意)用一些不易追踪的方式来传输一些(耗时的)东西. 在这篇文章中, 我会介绍一些能让你的网站在移动设备上快速加载且可交互的方式.

摘要: 更少的代码 = 更少的解析/编译(时间) + 更少的传输(时间) + 更少的解压(时间)

网络

大多数开发者考虑 JavaScript 的时间消耗时, 都会首先考虑到 JavaScript 的下载和执行消耗. 脚本传输的字节越多, 花费的时间越长, 用户连接的就越慢.

即使在网络发达的国家, 这也是需要面对的一个问题, 因为用户有效的网络连接类型不一定就是 3G、4G 或者 Wifi. 你可以连接咖啡店的 Wifi, 也可能连接上一个 2G 网络的蜂窝热点.

因而, 开发者需要想办法减少 JavaScript 在网络上的传输时间. 我这提供一些参考的方式:

  • 通过代码分割(Code Splitting), 只传输用户需要的代码.

  • 减少代码体积(对于 ES5 可以使用 Uglify; 对于 ES2015, 可以使用 babel-minify 或 uglify-es)

  • 压缩代码(可以使用 Brotli ~ q11, Zopfli 或 gzip). Brotli 在压缩比上优于 gzip. 这种方式帮助 CertSimple 网站把脚本体积减少了 17%, 并帮助 LinkedIn 节省了 4% 的脚本加载时间.

  • 移除无用的代码. 可以通过 DevTools 查看代码覆盖率情况. 对于代码分离, 可以了解 tree-shaking、Closure Compiler等高级优化方式. 对于公共库则可以使用一些代码优化插件, 如针对 lodash 的代码优化插件 lodash-babel-plugin, 可用于像 Moment.js 一类库的优化插件 ContextReplacementPlugin. 此外, 使用 babel-preset-env & browserlist 可以避免编译现代浏览器已经支持的功能. 部分更高级的开发者可能会细心分析 Webpack bundles 来帮助确定不必要的依赖.

  • 通过缓存来优化网络传输. 通过 max-age 和 Etag 等方式来缓存脚本, 减少字节的传输. Service Worker 缓存技术能使你的应用具备网络弹性, 并且能使用像 V8 code cache 一样的特性. 同时, 也可以了解下通过 文件哈希名 实现长久缓存.

解析/编译

脚本下载之后, JavaScript 最消耗时间的地方就是 JS 引擎对代码的解析/编译. 在 Chrome DevTools 的性能面板中, JS 的解析和编译是 Scripting time 中的黄色部分.

从 Bottom-Up/Call Tree 可以看到更精确的解析/编译时间.

但是, 为什么会这样呢?

花费很长时间去解析/编译代码会严重延迟用户在网站上的可交互时间. 传输的脚本越多, 在网站可交互之前, 就会花费更多的时间去解析/编译代码.

和脚本相比, 浏览器也会花费很多时间来处理同等大小的图片(图片仍需要被解码). 但是在大多数移动设备上, JS 更有可能对页面的交互性产生负面影响.

当我们谈论脚本的解析和编译很慢时, 上下文是很重要的--我们说的是普通的手机设备. 普通用户的手机是配置低配的 CPU 和 GPU, 可能由于手机内存的限制, 也没有 L2/L3 级缓存设置.

在 JavaScript 性能 一文中, 我注意到在低配手机和高配手机上解析约 1M 被解压后的脚本文件所用的时间是不同的. 对于市面上解析最快的手机和普通手机之间, 大约有 2~5x 的时间差异.

那么不同配置的手机访问 CNN.com 又会是怎么样的呢?

与普通手机(Moto G4) 需要花费约 13s 来解析/编译 CNN 网站的 JS 相比, 高配 iPhone 8 仅需要约 4s 时间.这可以显著地影响用户与该站点完全交互的速度.

这突出了测试普通手机设备(如 Moto G4)的重要性而不仅仅是你口袋里的手机设备. 然而, 上下文关系也很重要: 优化网站用户的硬件设备和网络环境.

深入分析真实用户访问你的网站所使用的移动设备类型, 这样才可能明白他们真实的 CPU/GPU 等硬件约束.

另一方面, 也需要反思我们是否真的传输了太多的脚本?

通过 HTTP Archive 分析约前 500K 网站在移动设备上传输的脚本大小, 可以发现 50% 的网站需要占据 14s, 用户才可以与网站交互, 但是这些网站仅用 4s 时间来解析和编译 JS.

在获取和处理 JS 以及其他资源所需的时间中, 用户需要在页面可交互之前等待一段时间, 这一点也不奇怪, 但我们可以在这里做得更好.

移除页面上的非关键脚本不仅能减少传输时间, 也能减少 CPU 的解析/编译时间和潜在的内存开销, 这可提高页面可交互的速度.

执行时间

不仅脚本的解析和编译需要时间, 脚本的执行也需要时间. 长时间的执行时间也会延迟用户与站点的交互速度.

如果脚本的执行时间超过 50ms, 那么可交互时间的延迟将是脚本下载、编译和执行脚本所花费时间的总和. -- Alex Russell

为减少脚本的执行时间, 可以将脚本分成小块来执行, 以避免锁住主线程. 可以考虑是否能减少脚本在执行过程中需要完成的工作量, 如果工作量很多, 就将脚本分成小块来分解工作量, 以提高页面可交互的速度.

降低 JavaScript 交付成本的模式

当你尝试着降低 JavaScript 的解析/编译和网络传输时间时, 也可以试试基于路由的代码分割或 PRPL 模式来降低 JavaScript 的交付成本.

PRPL 是一种通过代码分割和缓存来优化页面交互的模式:

通过 V8’s Runtime Call Stats, 我们可以分析一些受欢迎移动站以及 PWA 应用的加载时间. 从下图可以看出, 脚本解析所需要的时间(橙色部分)是页面加载中最耗时的一部分:

其它消耗

除上述方式外, JavaScript 还能通过如下方式影响页面性能:

  • 内存. 由于 GC(garbage collection), 页面可能会频繁的出现闪现或者卡顿. 当浏览器回收内存时, JS 的执行会被暂停, 所以 JS 被暂停执行的频率和浏览器回收内存的频率是正相关的, 因此需要避免内存泄漏和频繁的内存回收导致的 JS 执行暂停, 保持页面的流畅度.

  • 在运行期间, 长时间的脚本执行会阻塞主线程而导致页面没有响应. 将脚本的工作量分成多个小块来执行(使用 requestAnimationFrame() 或 requestIdleCallback() 进行任务调度)可以最小化响应性问题.

Progressive Bootstrapping(逐步引导)

因为优化交互性的成本比较高, 许多网站会考虑去优化内容的可见性. 当 JavaScript Bundles 很大时, 为了减少白屏时间(First paint time), 一些开发者会采用服务端渲染的方式, 当 JS 处理完成之后再将其 "升级" 为事件处理.

但这种方式也是有时间消耗的: 1) 通常会发送一个很大的 HTML 文件作为响应, 2) 在 JavaScript 完成处理之前, 页面可能只有一部分是可交互的.

因而逐步引导可能是一个更好的方式. 浏览请请求一个最小化的功能页面(仅由当前路由需要的 HTML/JS/CSS 组成), 当有更多资源请求时, 应用可以进行资源懒加载, 然后逐步解锁更多功能.

Loading code proportionate to what’s in view is the holy grail. PRPL and Progressive Bootstrapping are patterns that can help accomplish this.

参考

  • The Cost Of JavaScript

  • JavaScript Start-up Performance

  • Solving the web performance crisis

  • Can you afford it? Real-world performance budgets

  • Performance Futures

 

JavaScript 的时间消耗--摘抄的更多相关文章

  1. Java和JavaScript的时间互传

    原创文章,转载请注明:Java和JavaScript的时间互传 By Lucio.Yang 1.从JavaScript到Java JavaScript: function query(){ var s ...

  2. 在项目管理中如何保持专注,分享一个轻量的时间管理工具【Flow Mac版 - 追踪你在Mac上的时间消耗】

    在项目管理和团队作业中,经常面临的问题就是时间管理和优先级管理发生问题,项目被delay,团队工作延后,无法达到预期目标. 这个仿佛是每个人都会遇到的问题,特别是现在这么多的内容软件来分散我们的注意力 ...

  3. 测试mysqldump 压缩率和时间消耗

    测试mysqldump 压缩率和时间消耗 实验总结: 从本次实验数据可以看出,mysqldump通过|gzip参数可以将导出文件压缩53%,同时耗时也普通非压缩模式的2.3倍. 数据库环境: #[ro ...

  4. 详解JavaScript UTC时间转换方法

    这篇文章主要介绍了JavaScript UTC时间转换方法,介绍了本地时间到UTC时间的转换.UTC日期到本地日期的转换,感兴趣的小伙伴们可以参考一下 一.前言 1.UTC: Universal Ti ...

  5. JavaScript 对时间日期格式化

    JavaScript 对时间日期格式化 // 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h).分(m).秒(s).季度(q) 可以用 1-2 个占位 ...

  6. JavaScript日期时间格式化函数

    这篇文章主要介绍了JavaScript日期时间格式化函数分享,需要的朋友可以参考下 这个函数经常用到,分享给大家. 函数代码: //格式化参数说明: //y:年,M:月,d:日,h:时,m分,s:秒, ...

  7. JavaScript实现时间上一天和下一天切换

    JavaScript实现时间上一天和下一天切换 1.先获取时间戳毫秒数 var date = new Date()//实例化时间戳 var time = date.getTime()//获取当前毫秒数 ...

  8. javascript系统时间测试题

    如果系统的时间是2016年2月20日,分析下列JavaScript代码,运行后在网页上显示() var now = new Date();var year = now.getFullYear();va ...

  9. javascript实例——时间日期篇(包含5个实例)

    本来想在网上找一些js实例来练练手,结果发现一本书<突破JavaScript编程实例五十讲>,看了下内容还不错,就下了下来: 后面又下了该书籍的源码,一看才发现这本书编的日期是2002年的 ...

随机推荐

  1. Scala 的list

    9.1 使用列表 列表类型:跟数组一样,列表也是同质化的(homogeneous).即所有元素都要是同种类型. 列表结构:所有列表由两部分组成:Nil 和 ::(cons). 基本操作:主要有三个:h ...

  2. Error:(3, 32) java: 程序包org.springframework.boot不存在

     解决方案一: 找同事传一份D:\maven_repository\org\springframework\boot  ,如图所示的位置,添加进去立刻就不报红.我也可以给你发....  解决方案二: ...

  3. Windows server 2012安装oracle11g(32/64位)步骤

    Oracle官方下地址: http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html以下两网址 ...

  4. 一、numpy入门

    Array import numpy as np # create from python list list_1 = [1, 2, 3, 4] array_1 = np.array(list_1)# ...

  5. java反序列化字节转字符串工具

    https://github.com/NickstaDB/SerializationDumper SerializationDumper-v1.1.jar 用法 : java -jar Seriali ...

  6. Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]

    这是因为我把 [/WEB-INF/dispatcher-servlet.xml]的位置换成了[config/springmvc/dispatcher-servlet.xml] 因此idea在原来的位置 ...

  7. 6.python

    最早的'密码本' ascii 涵盖了英文字母大小写,特殊字符,数字.01010101ascii 只能表示256种可能,太少,创办了万国码 unicode 16表示一个字符不行,32位表示一个字符. A ...

  8. Vue的安装并在WebStorm中运行

    一.Vue的安装需要两个支持分别为:nodejs.npm Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻塞式 I/O ...

  9. 在Phonegap下实现oAuth认证

    原文:http://www.kuqin.com/mobile/20120719/322873.html 前段时间做过两次关于Phonegap的现场交流会议分享.基本上把Phonegap的一些特性和大家 ...

  10. vuejs 中 select 动态填充数据,后台的数据

           selected:"A" 对       selected:A 错.  变量不用引号. 内容一定要引号. https://jsfiddle.net/rgnuaw30/ ...