javascript概述_JavaScript性能优化技巧:概述

javascript概述

在这篇文章中,有很多内容可以涵盖广阔而变化莫测的景观。 它也是涵盖每个人最喜欢的主题:本月的JS框架™。

我们将尝试坚持“工具而不是规则”的口头禅,并将JS流行语降至最低。 由于我们无法在2000字的文章中涵盖与JS性能相关的所有内容,因此请确保您已阅读参考资料,然后进行自己的研究。

但是,在深入探讨具体问题之前,让我们通过回答以下问题来更广泛地了解该问题:什么是高性能JavaScript,以及它如何适应更广泛的Web性能指标?

搭建舞台

首先,让我们排除以下问题:如果仅在台式机设备上进行测试,则您将排除超过50%的用户。

2016年11月,移动用户数量超过台式机用户数量

这种趋势只会继续增长,因为新兴市场首选的网络门户是价格低于100美元的Android设备。 桌面作为访问Internet的主要设备的时代已经过去,接下来的十亿互联网用户将主要通过移动设备访问您的站点。

在Chrome DevTools的设备模式下进行的测试不能替代在真实设备上进行的测试。 使用CPU和网络限制会有所帮助,但这是完全不同的野兽。 在真实设备上测试。

即使您在真实的移动设备上进行测试,也可能会在品牌上使用这种价格仅为600美元的旗舰手机进行测试。 问题是,这不是您的用户拥有的设备。 中值设备类似于Moto G1,即具有1GB以下RAM以及非常弱的CPU和GPU的设备。

让我们看看在解析平均JS bundle时它是如何堆叠的。

哎哟。 虽然此图像仅涵盖JS的解析和编译时间(稍后会详细介绍),而不涉及一般性能,但它具有很强的相关性,可以视为一般JS性能的指标。

用布鲁斯·劳森(Bruce Lawson)的话来说,“ 这是万维网,而不是西方的富裕网络 ”。 因此,您的网络性能目标是使设备的运行速度比MacBook或iPhone 慢25倍 。 让它沉入一点。 但情况变得更糟。 让我们看看我们真正的目标是什么。

什么是Performance JS代码?

现在我们知道目标平台是什么,我们可以回答下一个问题:什么高性能JS代码?

尽管没有定义性能代码的绝对分类,但我们确实有一个以用户为中心的性能模型,可以用作参考: RAIL模型

轨道:响应/动画/空闲工作/加载

Sam Saccone: 绩效规划:PRPL

响应

如果您的应用在100毫秒内响应用户的操作,则用户会将响应视为立即响应。 这适用于可点击的元素,但不适用于滚动或拖动。

动画化

在60Hz显示器上,我们希望在动画和滚动时以每秒60帧的恒定速度为目标。 这导致每帧约16ms。 在这16毫秒的预算中,实际上,您需要8到10毫秒来完成所有工作,其余的工作则由浏览器内部和其他差异占用。

空闲工作

如果您有一项昂贵且连续运行的任务,请确保将其切成较小的块,以使主线程对用户输入做出React。 您不应将用户输入延迟超过50毫秒。

加载

您应该在1000毫秒内定位页面加载。 一切都结束了,您的用户开始抽搐。 这是在移动设备上实现的一个非常困难的目标,因为它与页面是交互式的有关,而不仅仅是页面在屏幕上可滚动。 实际上,它甚至更少:

JavaScript 1000毫秒预算

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

默认快速设置:现代加载最佳做法(Chrome开发者峰会2017)

在实践中,争取5s的互动时间标记。 这就是Chrome浏览器在Lighthouse审核中使用的功能

现在我们知道了指标, 让我们看一些统计数据

  • 如果移动网站加载时间超过三秒钟,则53%的访问将被放弃
  • 2人中有1人希望页面加载时间少于2秒
  • 77%的移动网站在3G网络上加载所需的时间超过10秒
  • 19秒是3G网络上移动网站的平均加载时间。

还有更多,由Addy Osmani提供

  • 应用程序在台式机上(通过电缆)在8秒内变为交互式,在移动设备上(3G上的Moto G4)在16秒内变为交互式
  • 以中位数计,开发人员为其页面发送了410KB的压缩JS。

感到足够沮丧吗? 好。 让我们开始工作并修复网络。 ✊

情境就是一切

您可能已经注意到,主要瓶颈是加载网站所需的时间。 具体来说,就是JavaScript的下载,解析,编译和执行时间。 没有其他办法,只能加载更少JavaScript并更智能地加载。

但是,除了启动网站之外,您的代码还能做些实际工作吗? 那里必须有一些性能提升,对吗?

在深入优化代码之前,请考虑您要构建的内容。 您正在构建框架还是VDOM库? 您的代码是否需要每秒执行数千次操作? 您是否正在处理时间紧迫的库来处理用户输入和/或动画? 如果没有,您可能希望将时间和精力转移到更有意义的地方。

并不是说编写高效的代码无关紧要,但是这通常不会对宏大的事物产生影响,甚至不会产生任何影响,尤其是在谈论微优化时。 因此,在通过比较JSperf.com的结果进入有关.map.forEachfor循环的Stack Overflow参数之前,请确保不仅查看树木,而且查看树木。 50k ops / s听起来可能比纸上的1k ops / s好50倍,但在大多数情况下并没有什么不同。

解析,编译和执行

从根本上讲,大多数性能不佳的JS的问题不是运行代码本身,而是在代码开始执行之前必须执行的所有步骤。

我们在这里谈论抽象级别。 您计算机中的CPU运行机器代码。 您在计算机上运行的大多数代码都是已编译的二进制格式。 (考虑到如今所有的Electron应用程序 ,我说的是代码而不是程序 。)这意味着,除了所有的OS级抽象之外,它还可以在您的硬件上本地运行,无需任何准备工作。

JavaScript未预编译。 它(作为一个相对较慢的网络)以可读代码的形式到达您的浏览器中,无论出于何种目的,它都是JS程序的“操作系统”。

该代码首先需要解析-即,将其读取并转换为可用于编制索引的计算机可索引结构。 然后将其编译为字节码,最后编译为机器码,然后由设备/浏览器执行。

还要提到的另一个非常重要的事情是JavaScript是单线程的,并且在浏览器的主线程上运行。 这意味着一次只能运行一个进程。 如果您的DevTools性能时间表充满黄色峰值,并且CPU以100%的速度运行,那么您将出现长帧/掉帧,混乱滚动以及所有其他讨厌的东西。

因此,在您的JS开始工作之前,需要完成所有这些工作。 在Chrome的V8引擎中,解析和编译最多需要执行JS的总时间的50%。

移动和桌面JS解析时间的比较

艾迪·奥斯曼(Addy Osmani): JavaScript启动性能

您应该从本节中删除两件事:

  1. 尽管不一定是线性的,但JS解析时间与包的大小成比例。 您运送的JS越少越好。
  2. 您使用的每个JS框架(React,Vue,Angular,Preact…)都是另一种抽象级别(除非它是像Svelte这样的预编译框架)。 由于不直接与浏览器对话,这不仅会增加捆绑包的大小,而且还会降低代码的速度。

有多种方法可以减轻这种情况,例如,使用服务工作者在后台和另一个线程上执行作业,使用asm.js编写更易于编译为机器指令的代码,但这是另一个主题。

但是,您可以做的是避免对所有内容使用JS动画框架,并仔细阅读触发绘画和布局的内容 。 仅在绝对无法使用常规CSS过渡和动画实现动画的情况下,才使用库。

即使它们可能使用CSS过渡,复合属性和requestAnimationFrame() ,它们仍在主线程中的JS中运行。 他们基本上只是每16ms用内联样式锤击您的DOM,因为他们没有太多其他事情可以做。 您需要确保所有JS在每帧8ms内完成执行,以保持动画流畅。

另一方面,CSS动画和过渡正在主线程上运行-如果在GPU上实现得当,则不会导致重做/重排。

考虑到大多数动画在加载或用户交互过程中都在运行,因此可以为您的Web应用程序提供急需的呼吸空间。

Web Animations API是即将推出的功能集,它使您可以在主线程之外执行高效的JS动画,但是目前,请坚持使用CSS过渡和FLIP之类的技术。

捆绑包大小无所不能

今天,一切都与捆绑包有关。 在</body>标记之前,Bower和数十个<script>标记的时代已经一去不复返了。

现在,这一切都与npm install有关–可以在NPM上找到任何闪亮的新玩具,将它们与Webpack捆绑在一个巨大的单个1MB JS文件中,并在限制其数据计划的同时重击用户的浏览器。

尝试运送较少的JS。 您可能不需要项目的整个Lodash库 。 您是否绝对需要使用JS框架? 如果是,是否考虑使用React以外的其他东西,例如PreactHyperHTML ,其大小小于React的1/20? 该滚动到顶部的动画是否需要TweenMax ? npm和框架中隔离组件的便利性有一个缺点:开发人员对问题的第一React是投入更多的JS。 当您只有一把锤子时,一切看起来都像钉子。

完成修剪杂草并减少JS运输后,请尝试更智能地进行运输。 在需要时运送需要的物品。

Webpack 3具有称为代码拆分动态导入的 惊人功能。 无需将所有JS模块捆绑为一个整体的app.js捆绑包,它可以使用import()语法自动拆分代码并异步加载。

您也无需使用框架,组件和客户端路由来获得其好处。 假设您有一个复杂的代码段可以为您的.mega-widget ,该代码可以在任意数量的页面上显示。 您只需在主JS文件中编写以下内容:

if (document.querySelector('.mega-widget')) {
    import('./mega-widget');
}

如果您的应用在页面上找到小部件,它将动态加载所需的支持代码。 否则,一切都很好。

此外,Webpack需要自己的运行时才能运行,并将其注入到它生成的所有.js文件中。 如果使用commonChunks插件,则可以使用以下代码将运行时提取到其自己的块中

new webpack.optimize.CommonsChunkPlugin({
  name: 'runtime',
}),

它将所有其他块中的运行时剥离到其自己的文件中,在本例中为runtime.js 。 只要确保在主JS捆绑包之前加载它即可。 例如:

<script src="runtime.js">
<script src="main-bundle.js">

然后是转译代码和polyfill的主题。 如果您正在编写现代(ES6 +)JavaScript,则可能是使用Babel将其转换为ES5兼容代码。 由于所有的冗长性,转码不仅增加了文件大小,而且还增加了复杂性,并且与本机ES6 +代码相比,转码通常会降低性能

除此之外,您可能正在使用babel-polyfill whatwg-fetch软件包和whatwg-fetch来修补旧版浏览器中缺少的功能。 然后,如果您使用async/await编写代码,则还可以使用包含regenerator-runtime所需的regenerator-runtime器来对其进行转换……

关键是,您将近100 KB的JS包添加到JS包中,这不仅具有巨大的文件大小,而且还具有巨大的解析和执行成本,以支持较旧的浏览器。

不过,惩罚使用现代浏览器的人毫无意义。 Philip Walton在本文中介绍了我使用的一种方法,即创建两个单独的包并有条件地加载它们。 Babel使用babel-preset-env轻松实现了这一点。 例如,您有一个捆绑包用于支持IE 11,而另一个捆绑包不具有针对现代浏览器最新版本的polyfills。

一种肮脏但有效的方法是将以下内容放入内联脚本中:

(function() {
  try {
    new Function('async () => {}')();
  } catch (error) {
    // create script tag pointing to legacy-bundle.js;
    return;
  }
  // create script tag pointing to modern-bundle.js;;
})();

如果浏览器无法评估async功能,则我们假定它是旧的浏览器,并且只提供了polyfilled捆绑包。 否则,用户将获得简洁而现代的变体。

结论

我们希望您从本文中学到的是JS价格昂贵,应谨慎使用。

确保在真实网络条件下在低端设备上测试网站的性能。 您的网站应加载速度快,并应尽快进行交互。 这意味着可以减少JS的发送,并通过任何必要的方式更快地发送。 您的代码应始终最小化,分成更小的,可管理的捆绑包,并在可能的情况下异步加载。 在服务器端,请确保已启用HTTP / 2,以实现更快的并行传输和gzip / Brotli压缩,以大大减少JS的传输大小。

话虽如此,我想结束以下推文:

翻译自: https://www.sitepoint.com/javascript-performance-optimization-tips-an-overview/

javascript概述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值