Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript性能 #13

Closed
bytemofan opened this issue Feb 24, 2017 · 0 comments
Closed

JavaScript性能 #13

bytemofan opened this issue Feb 24, 2017 · 0 comments

Comments

@bytemofan
Copy link
Owner

bytemofan commented Feb 24, 2017

作为web开发者,我们知道随着网页的膨胀很容易导致网页的启动性能低下!因此loading一个web页面比传输字节数据更为重要!一旦浏览器下载了我们的页面的脚本,它必须解析,解释和运行它们。在这篇文章中,我们将深入JavaScript的这一阶段去了解为什么它会拖慢你的应用启动速度和如何去修复它。

以往,我们并没有花很多时间优化的JavaScript解析/编译步骤。我们仅仅期待只要解析器命中标签,脚本将立即解析和执行。但事实并非如此。这里是如何工作的一个简化的V8引擎故障:

关于V8引擎如何工作的一个简化视图。这是一个我们正在为之努力的理想化的流程。

让我们把重点放在一些主要阶段上。

是什么拖慢了我们的Web应用程序启动速度?

在启动过程中,JavaScript引擎解析,编译和执行脚本的过程花费了大量的时间。这个问题也是用户需要等待多久才能和我们的网页互动。想象一下,如果他们可以看到一个按钮,需要等待几秒之后才能点击和触摸,这是一个非常不好的用户体验。

这是Chrome Canary统计的使用V8引擎的通常的一个页面的解析和编译时间。注意,移动端的解析和编译所花费的时间明显大于桌面端。

启动时间问题普遍存在。事实上,像Facebook, Wikipedia和Reddit这些顶级的网站,在使用V8引擎的Chorme里边运行时,也会花费大量的实际解析和编译JavaScript:

粉红色的区域(JavaScript)代表V8引擎和Blink内核的C++代码执行时间,而橙色和黄色则代表解析和编译时间。

从解析和编译的时间花费来看,这已经是你所使用的大量的网页和框架的性能瓶颈所在。下面是Facebook的Sebastian Markbage和Google的Rob Wormald的tweets:

Sam Saccone把js解析所用的时间花费命名为"规划性能"

随着越来越多我们投身于移动端得到怀抱,理解js的解析/编译过程所花费的时间是桌面端的2到5倍是是一件重要的事。高端手机(如iPhone或Pixel)的执行情况和一个Moto G4的执行情况有非常大的不同。这突出了我们在不能影响我们的用户体验的情况下测试代表性硬件(不仅仅是高端!)的重要性。

这是不同种类的桌面和移动端设备解析1MbJavaScript的时间

注意,当我们沿着图中走向取出移动端花费时间的平均值时,高端手机如iPhone 7的性能和MacBook Pro是如何的接近。

如果我们的APP使用了大量的bundle,我们使用一些先进的技术如code-splitting, tree-shaking和Service Worker缓存确实能够产生巨大的性能差异。也就是说,即使是一个很小的bundle,写得不好或与库选择不当,可能会导致主线程被挂在编译或函数调用上很长的一段时间。从整体上衡量和理解我们的真正的瓶颈在哪里是很重要的。

什么是一般网站解析和编译JavaScript的瓶颈?

“可问题是,我们不像Facebook”,亲爱的读者,我就知道你会这样说。你可能会问“除此之外的其它一般的站点花费在解析和编译上的时间有多少呢?”。我们用一个科学的办法统计出来了:

我花费了两个月的时间去挖掘出大量的线上网站的性能,且这些网站使用了不同的库和框架,比如React、Angular,Ember和Vue。大多数的测试都是基于最近的WebPageTest的结果,因此如果你想要挖掘更多的数据你可以很容易的重复这个过程。以下是我的一些见解:

应用程序可以交互时间,桌面(使用缓存)花费8s,移动端(Moto G4在3G网络下)花费16s

是什么导致的呢?在桌面端,大多数程序平均花费4秒启动(解析/编译/执行)。

在移动端,解析时间比桌面端高36%!

**我们都在传输巨大的JS bundle?没有我猜测的那么大,但是存在改进的余地。**有这么一个中位值,开发商将他们的网页使用gzip压缩JS到410kb。这超过了HTTPArchive的“每页的平均JS大小”报告线,也就是420kb大小。最严重的甚至发送了10MB的脚本。我的天!

HTTPArchive统计: 每个页面平均下载420kb的JavaScript。

脚本大小是重要的,但它不是一切。当脚本大小增加时,解析和编译次数不一定会线性增加。更小的JavaScript bundles通常加载得更快(不管我们的浏览器类型,设备类型和网络情况如何),因此,200kb的js !== 200kb,别人的不同的解析和编译方式可能有不同的结果。

测量解析/编译JavaScript的时间

Chrome DevTools

Timeline (Performance面板) > Bottom-Up/Call Tree/Event Log 将让我们得到解析/编译所花费的时间。为了看到更为完整的视图(如解析,预解析或延迟编译的时间),我们可以打开V8’s Runtime Call Stats功能。在Chrome Canary版本中,这在Timeline上的Experiments > V8 Runtime Call Stats里。

Chrome Tracing

about:tracing是Chorme的一个降级的追踪工具,它允许用户使用disabled-by-default-v8.runtime_stats配置去拿到V8引擎运行时所花费的更详细的信息。V8前几天发布了一个入门指南讲述如何去使用它。

WebPageTest

当我们开启功能Chrome > Capture Dev Tools Timeline的时候,WebPageTest的“处理故障”页面可以包含精确的V8编译,evaluatescript和函数调用的时间。我们现在也可以通过指定disabled-by-default-v8.runtime_stats 来开启Runtime Call Stats作为一个自定义追踪方式(WPT的Pat Meenan现在默认这样做的!)

想要看关于这个的详细信息,可以看我写的gist

用户定时

有可能通过用户定时API来测量解析时间,Nolan Lawson曾指出:

这第三点不重要,但是第一点很重要(performance.mark() 在启动之前调用)。

这个方法能够影响随后的V8引擎的解析器。这能够运行在脚本文件末尾加了一串随机字符的文件上,Nolan在他的optimize-js做了如此规范。

我使用了相同的方法去测量了Google Analytics的JavaScript解析时间的影响:

在真正的用户和设备在野外打我的页面的时候,自定义的谷歌分析维度“parse”让我测量到JavaScript解析时间。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant