基于JavaScript 性能优化技巧心得(分享)


Posted in Javascript onDecember 11, 2017

JavaScript 作为当前最为常见的直译式脚本语言,已经广泛应用于 Web 应用开发中。为了提高Web应用的性能,从 JavaScript 的性能优化方向入手,会是一个很好的选择。

本文从加载、上下文、解析、编译、执行和捆绑等多个方面来讲解 JavaScript 的性能优化技巧,以便让更多的前端开发人员掌握这方面知识。

什么是高性能的 JavaScript 代码?

尽管目前没有高性能代码的绝对定义,但却存在一个以用户为中心的性能模型,可以用作参考:RAIL模型。

基于JavaScript 性能优化技巧心得(分享)

响应

如果你的应用程序能在100毫秒内响应用户的操作,那么用户会认为该响应为即时的。这适用于可点击的元素,不适用于滚动或拖动操作。

动画

在60Hz的显示器上,我们希望动画和滚动时每秒有60帧,这种情况下每帧大约为16ms。在这16ms的时间内,实际上只有8-10ms来完成所有工作,其余时间则由浏览器的内部和其它差异占据。

空闲工作

如果你有一个耗时很久,需要持续运行的任务时,请确保把它分成很小的块,以便允许主线程对用户的输入操作做出反应。不应该出现一个任务延迟超过50ms的用户输入。

加载

页面加载应该在1000毫秒内完成。在移动设备上,这是一个很难达到的目标,因为它涉及到页面的互动,而不仅仅是在屏幕上渲染和滚动。

基于JavaScript 性能优化技巧心得(分享)

现代加载最佳实践(Chrome Dev Summit 2017)

如果移动网站的加载时间超过三秒,则会有53%的用户放弃访问

50%的用户希望在不到2秒的时间内完成页面加载

77%的移动网站需要10秒以上的时间来加载3G网络

19秒是3G网络上移动站点的平均加载时间

代码内容

你可能已经注意到了,最大的瓶颈是加载网站所需的时间。具体来说就是 JavaScript 的下载、解析、编译和执行时间。除了加载更少的 JavaScript 文件或者加载的更加灵活以外,看起来没有其它办法。

除去启动网站之外,JavaScript 代码又是如何实际工作的呢?

在进行代码优化之前,请考虑你当前正在构建的内容。你正在建立的是一个框架还是一个 VDOM 库?你的代码是否需要每秒执行数千次操作?你是否正在做一个对时间要求较为严格的库来处理用户输入和/或动画?如果没有,你需要把时间和精力转移到更有影响力的地方。

编写高性能代码并不是那么重要,因为对于宏观计划通常没有什么影响。50k ops/s 听起来好于 1k ops/s,但在大多数情况下整体时间并不会有所改变。

解析、编译和执行

从根本上说,大多数 JavaScript 的性能问题,并不在于运行代码本身,而是在代码开始执行之前必须采取的一系列步骤。

我们在这里讨论抽象层次的问题。计算机上运行的大多数代码都是编译后的二进制格式。意思是说,除了所有的操作系​​统级别的抽象外,代码都可以在硬件上本地运行,不需要准备工作。

JavaScript 代码不是预编译的,它在浏览器上是可读的。

JavaScript 代码首先会被解析,也就是读取并转换成可用于编译的计算机索引的结构,然后再被编译成字节码,最后被编译成机器码,用于设备/浏览器执行。

另一个非常重要的方面是:JavaScript 是单线程的,并且在浏览器的主线程上运行。这意味着一次只能运行一个进程。如果你的 DevTools 性能时间线充满黄色峰值,同时 CPU 占用率达到100%,则将出现丢帧的情况。这是滚动操作常出现的,也是很讨厌的一种情况。

基于JavaScript 性能优化技巧心得(分享)

在 JavaScript 代码运行之前,需要完成所有的这些解析、编译和执行工作。在 ChromeV8 引擎中,解析和编译占 JavaScript 执行总时间的50%左右。

基于JavaScript 性能优化技巧心得(分享)

所以在这部分中,应该了解两件事情:

1. 虽然 JavaScript 解析的时间长度和包的大小不是完全线性的,但是需要处理的 JavaScript 越少,则所花时间越少。

2. 你使用的每一个 JavaScript 框架(React,Vue,Angular,Preact ...)都是另一个抽象层次(除非它是一个预编译的)。这不仅会增加你的包的大小,而且会让你的代码变慢,因为你不是直接与浏览器通信的。

有些方法可以缓解这种情况,比如使用 service workers 在后台的另一个线程中执行部分工作,或者使用 asm.js 编写更容易编译机器指令的代码。

我们所能做的,就是避免使用 JavaScript 动画库。只有在使用常规的 CSS 转换和动画完全无法实现时,才去使用这些库。

即使这些 JavaScript 动画库使用 CSS 转换,合成属性和 requestAnimationFrame( ),但是它们仍然运行在 JavaScript 的主线程上。基本上这些库会使用内联样式每16ms访问一次 DOM。你需要确保所有的 JavaScript 都在每帧8ms以内完成,才能保持动画的平滑性。

另一方面,CSS 动画和转换会在主线程中运行,如果能够高效执行,则能避免重新布局/重排的情况出现。

考虑到大多数动画都在加载或用户交互的过程中运行,这可以为你的 web 应用程序提供非常重要的调整空间。

web Animations API 是一个即将到来的功能集,它能够脱离主线程执行高性能的 JavaScript 动画。但就目前而言,还需要继续使用 CSS 转换等技术。

捆绑尺寸非常重要

现在已经不再是在 </body> 结束标签之前包含有多个 <script> 的时代了。现在,可以在 npm 上找到各式各样的工具包,并且可以将这些工具包和 Webpack 捆绑在一个单个的 1MB 大小的 JavaScript 文件中,在完成数据计划时,提醒用户的浏览器进行爬取。

这样可以使用更少量的 JavaScript,这也意味着你的项目可能不再需要整个Lodash库。如果必须使用 JavaScript 库,也可以考虑使用 React 以外的东西,比如 Preact 或者 HyperHTML,它们只是 React 的1/20大小。

Webpack 3 有着神奇的功能,被称作代码分割和动态导入。它不会将所有 JavaScript 模块捆绑到一个 app.js 整包中,而是使用 import( ) 语法自动分割代码并且进行异步加载。

你不需要使用框架、组件和客户端路由,就能获得这些好处。你只需要简单地在主 JavaScript 文件中写入以下内容:

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

如果你的应用程序需要在页面上用到这个小部件,它将动态加载所需的支持代码。

另外,Webpack 需要运行时间来工作,并将其注入到它生成的所有 .js 文件中。如果使用该 commonChunks 插件,则可以使用以下内容将运行时抽取到 Chunk 中:

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

确保 Webpack 在主 JavaScript 包之前已完成加载,那么所有其它 chunk 中的运行时间会剥离到各自的文件中,这种情况也被成为 runtime.js。例如:

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

然后是编译代码和 polyfills 的部分。如果你正在编写现代 JavaScript 代码(ES6 +),则可以使用 Babel 将其转换为 ES5 兼容的代码。与原生 ES6+ 代码相比,编译不仅增加了文件的大小,还增加了复杂性,并且经常会出现性能下降的情况。

除此之外,你还很可能使用 babel-polyfill 软件包和 whatwg-fetch,来修复旧版本浏览器中的缺失功能。因此如果你正在编写 async/await,你还需要使用包 regenerator-runtime 的生成器来进行编译。

问题是,你为 JavaScript 软件包添加了近 100KB 的内容,这不仅是一个巨大的文件,而且预示着巨大的解析和执行花费,以便能够支持旧版本的浏览器。

一种方法是创建两个独立的 bundle,并根据实际条件来加载它们。Babel 转换编译器在 babel-preset-env 的帮助下,会使同时面临新旧两种浏览器的情况更加容易处理。

一个并不规范但行之有效的方法,是将以下内容放在一个内联脚本中:

(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 函数,则会被认为是旧版本的浏览器,此时就会用到 polyfill 包。如果能识别,用户则将得到现代浏览器的处理。

结论

想要提高网站的运行速度,就需要确保网站能快速的加载 JavaScript 文件,以实现快速的互动。你的 JavaScript 代码应该被分成更小的、可管理的 bundle,同时尽可能地进行异步加载。在服务器端,请确保启用了 HTTP 2.0,以便实现更快的并行传输和 gzip/Brotli 压缩,从而大大减少了 JavaScript 的传输大小。

以上这篇基于JavaScript 性能优化技巧心得(分享)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript 冒号 使用说明
Jun 06 Javascript
extjs3 combobox取value和text案例详解
Feb 06 Javascript
JS简单的轮播的图片滚动实例
Jun 17 Javascript
jquery仿QQ商城带左右按钮控制焦点图片切换滚动效果
Jun 27 Javascript
javascript中Array数组的迭代方法实例分析
Feb 04 Javascript
vue2.0实现前端星星评分功能组件实例代码
Feb 12 Javascript
iview日期控件,双向绑定日期格式的方法
Mar 15 Javascript
在ES5与ES6环境下处理函数默认参数的实现方法
May 13 Javascript
解决vuejs项目里css引用背景图片不能显示的问题
Sep 13 Javascript
Electron-vue脚手架改造vue项目的方法
Oct 22 Javascript
JavaScript监听一个DOM元素大小变化
Apr 26 Javascript
Vue时间轴 vue-light-timeline的用法说明
Oct 29 Javascript
JavaScript时间戳与时间日期间相互转换
Dec 11 #Javascript
vue项目优化之通过keep-alive数据缓存的方法
Dec 11 #Javascript
微信小程序使用input组件实现密码框功能【附源码下载】
Dec 11 #Javascript
浅谈vue项目优化之页面的按需加载(vue+webpack)
Dec 11 #Javascript
微信小程序使用radio显示单选项功能【附源码下载】
Dec 11 #Javascript
详解webpack编译多页面vue项目的配置问题
Dec 11 #Javascript
微信小程序使用checkbox显示多项选择框功能【附源码下载】
Dec 11 #Javascript
You might like
经典PHP加密解密函数Authcode()修复版代码
2015/04/05 PHP
Thinkphp实现自动验证和自动完成
2015/12/19 PHP
php设计模式之策略模式实例分析【星际争霸游戏案例】
2020/03/26 PHP
jquer之ajaxQueue简单实现代码
2011/09/15 Javascript
JavaScript全排列的六种算法 具体实现
2013/06/29 Javascript
javascript操作excel生成报表示例
2014/05/08 Javascript
使用原生JS实现弹出层特效
2014/12/22 Javascript
JS运动框架之分享侧边栏动画实例
2015/03/03 Javascript
使用JQuery实现Ctrl+Enter提交表单的方法
2015/10/22 Javascript
javascript history对象详解
2017/02/09 Javascript
使用JavaScriptCore实现OC和JS交互详解
2017/03/28 Javascript
jQuery遮罩层实例讲解
2017/05/11 jQuery
BootStrap Select清除选中的状态恢复默认状态
2017/06/20 Javascript
Node.js动手撸一个静态资源服务器的方法
2019/03/09 Javascript
原生js实现可兼容PC和移动端的拖动滑块功能详解【测试可用】
2019/08/15 Javascript
vue实现在v-html的html字符串中绑定事件
2019/10/28 Javascript
vue实现移动端input上传视频、音频
2020/08/18 Javascript
pyv8学习python和javascript变量进行交互
2013/12/04 Python
python实现忽略大小写对字符串列表排序的方法
2014/09/25 Python
python中as用法实例分析
2015/04/30 Python
Python实现的简单计算器功能详解
2018/08/25 Python
NumPy 数组使用大全
2019/04/25 Python
python爬虫使用scrapy注意事项
2020/11/23 Python
Python字节单位转换(将字节转换为K M G T)
2021/03/02 Python
Sofft鞋官网:世界知名鞋类品牌
2017/03/28 全球购物
如何在C# winform中异步调用web services
2015/09/21 面试题
计算机大学生的自我评价
2013/10/15 职场文书
库房主管岗位职责
2013/12/31 职场文书
外联部演讲稿
2014/05/24 职场文书
2014财务年度工作总结
2014/11/11 职场文书
2015年119消防宣传日活动总结
2015/03/24 职场文书
2016年小学“我们的节日·中秋节”活动总结
2016/04/05 职场文书
启迪人心的励志语录:脾气永远不要大于本事
2020/01/02 职场文书
MySQL的安装与配置详细教程
2021/06/26 MySQL
HTML基本元素标签介绍
2022/02/28 HTML / CSS
铁头也玩根德 YachtBoy YB-230......
2022/04/05 无线电