AJax技术

10个技巧,让你在 2017 年成为更好的Node开发者(2)

字号+ 作者:H5之家 来源:H5之家 2017-03-03 10:00 我要评论( )

WebPageTest 中 Processing Breakdown 页面在我们启用 Chrome Capture Dev Tools Timeline 时会自动记录 V8 编译、EvaluateScript以及 FunctionCall 的时间。我们同样可以通过指明disabled-by-default-v8.runtime_s

WebPageTest 中 Processing Breakdown 页面在我们启用 Chrome > Capture Dev Tools Timeline 时会自动记录 V8 编译、EvaluateScript 以及 FunctionCall 的时间。我们同样可以通过指明disabled-by-default-v8.runtime_stats的方式来启用 Runtime Call Stats。

更多使用说明参考我的gist点击预览。

User Timing

我们还可以使用 Nolan Lawson 推荐的User Timing API来评估语法解析的时间。不过这种方式可能会受 V8 预解析过程的影响,我们可以借鉴 Nolan 在 optimize-js 评测中的方式,在脚本的尾部添加随机字符串来解决这个问题。我基于 Google Analytics 使用相似的方式来评估真实用户与设备访问网站时候的解析时间。

DeviceTiming

Etsy 的 DeviceTiming 工具能够模拟某些受限环境来评估页面的语法解析与执行时间。其将本地脚本包裹在了某个仪表工具代码内从而使我们的页面能够模拟从不同的设备中访问。可以阅读 Daniel Espeset 的Benchmarking JS Parsing and Execution on Mobile Devices 一文来了解更详细的使用方式。

我们可以做些什么以降低 JavaScript 的解析时间?

减少 JavaScript 包体体积。我们在上文中也提及,更小的包体往往意味着更少的解析工作量,也就能降低浏览器在解析与编译阶段的时间消耗。

使用代码分割工具来按需传递代码与懒加载剩余模块。这可能是最佳的方式了,类似于PRPL这样的模式鼓励基于路由的分组,目前被 Flipkart, Housing.com 与 Twitter 广泛使用。

Script streaming: 过去 V8 鼓励开发者使用async/defer来基于script streaming实现 10-20% 的性能提升。这个技术会允许 HTML 解析器将相应的脚本加载任务分配给专门的 script streaming 线程,从而避免阻塞文档解析。V8 推荐尽早加载较大的模块,毕竟我们只有一个 streamer 线程。

评估我们依赖的解析消耗。我们应该尽可能地选择具有相同功能但是加载地更快的依赖,譬如使用 Preact 或者 Inferno 来代替 React,二者相较于 React 体积更小具有更少的语法解析与编译时间。Paul Lewis 在最近的一篇文章中也讨论了框架启动的代价,与 Sebastian Markbage 的说法不谋而合:最好地评测某个框架启动消耗的方式就是先渲染一个界面,然后删除,最后进行重新渲染。第一次渲染的过程会包含了分析与编译,通过对比就能发现该框架的启动消耗。

如果你的 JavaScript 框架支持 AOT(ahead-of-time)编译模式,那么能够有效地减少解析与编译的时间。Angular 应用就受益于这种模式:

现代浏览器是如何提高解析与编译速度的?

不用灰心,你并不是唯一纠结于如何提升启动时间的人,我们 V8 团队也一直在努力。我们发现之前的某个评测工具 Octane 是个不错的对于真实场景的模拟,它在微型框架与冷启动方面很符合真实的用户习惯。而基于这些工具,V8 团队在过去的工作中也实现了大约 25% 的启动性能提升:

本部分我们就会对过去几年中我们使用的提升语法解析与编译时间的技巧进行阐述。

代码缓存

Chrome 42 开始引入了所谓的代码缓存的概念,为我们提供了一种存放编译后的代码副本的机制,从而当用户二次访问该页面时可以避免脚本抓取、解析与编译这些步骤。除以之外,我们还发现在重复访问的时候这种机制还能避免 40% 左右的编译时间,这里我会深入介绍一些内容:

代码缓存会对于那些在 72 小时之内重复执行的脚本起作用。

对于 Service Worker 中的脚本,代码缓存同样对 72 小时之内的脚本起作用。

对于利用 Service Worker 缓存在 Cache Storage 中的脚本,代码缓存能在脚本首次执行的时候起作用。

总而言之,对于主动缓存的 JavaScript 代码,最多在第三次调用的时候其能够跳过语法分析与编译的步骤。我们可以通过chrome://flags/#v8-cache-strategies-for-cache-storage来查看其中的差异,也可以设置?js-flags=profile-deserialization运行 Chrome 来查看代码是否加载自代码缓存。不过需要注意的是,代码缓存机制仅会缓存那些经过编译的代码,主要是指那些顶层的往往用于设置全局变量的代码。而对于类似于函数定义这样懒编译的代码并不会被缓存,不过 IIFE 同样被包含在了 V8 中,因此这些函数也是可以被缓存的。

Script Streaming

Script Streaming允许在后台线程中对异步脚本执行解析操作,可以对于页面加载时间有大概 10% 的提升。上文也提到过,这个机制同样会对同步脚本起作用。

这个特性倒是第一次提及,因此 V8 会允许所有的脚本,即使阻塞型的<script src=''>脚本也可以由后台线程进行解析。不过缺陷就是目前仅有一个 streaming 后台线程存在,因此我们建议首先解析大的、关键性的脚本。在实践中,我们建议将<script defer>添加到<head>块内,这样浏览器引擎就能够尽早地发现需要解析的脚本,然后将其分配给后台线程进行处理。我们也可以查看 DevTools Timeline 来确定脚本是否被后台解析,特别是当你存在某个关键性脚本需要解析的时候,更需要确定该脚本是由 streaming 线程解析的。

语法解析 & 编译优化

我们同样致力于打造更轻量级、更快的解析器,目前 V8 主线程中最大的瓶颈在于所谓的非线性解析消耗。譬如我们有如下的代码片:

(function (global, module) { … })(this, function module() { my functions })

V8 并不知道我们编译主脚本的时候是否需要module这个模块,因此我们会暂时放弃编译它。而当我们打算编译module时,我们需要重分析所有的内部函数。这也就是所谓的 V8 解析时间非线性的原因,任何一个处于 N 层深度的函数都有可能被重新分析 N 次。V8 已经能够在首次编译的时候搜集所有内部函数的信息,因此在未来的编译过程中 V8 会忽略所有的内部函数。对于上面这种module形式的函数会是很大的性能提升,建议阅读The V8 Parser(s)?—?Design, Challenges, and Parsing JavaScript Better来获取更多内容。V8 同样在寻找合适的分流机制以保证启动时能在后台线程中执行 JavaScript 编译过程。

预编译 JavaScript?

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • CSS 其他技巧

    CSS 其他技巧

    2017-03-02 13:00

  • 编写高效的jQuery代码技巧总结

    编写高效的jQuery代码技巧总结

    2017-03-01 13:04

  • 几个 ASP.NET 小技巧

    几个 ASP.NET 小技巧

    2017-02-27 14:02

  • 3款实用的在线JS代码工具(国外)

    3款实用的在线JS代码工具(国外)

    2017-02-25 11:04

网友点评
-