优化 JavaScript
切换到 HTTP/2
异步与延迟
代码拆分
明智地进口
节流和去抖
所以呢
最近我有机会在NDC 悉尼发表关于网络性能的演讲,并获得了热烈的反响。
这启发了我针对那次演讲中涉及的每个主题撰写一系列帖子,谁知道呢,也许有一天这些帖子都会成为他们自己的演讲😃。
所有其他部分:
第 2 部分使用 Preload/Prefetch 来提升加载时间
是时候看看我们能为我们的老朋友 JavaScript 做些什么了。那就开始吧。
切换到 HTTP/2
随着越来越多的主机提供商支持HTTP/2,现在正是切换到该协议并受益于其多路复用特性的好时机。就性能而言,这意味着我们无需将所有 JavaScript 打包成大包来减少对服务器的调用次数。
HTTP/2旨在处理大量请求,因此您现在可以增加渲染页面所需的文件数量。但不要太多:
好事太多反而会变成坏事。
异步与延迟
正如我之前提到的,JavaScript 和 CSS 一样,都是渲染阻塞元素。这意味着浏览器需要等待 JavaScript 加载并执行完毕后才能解析HTML
文档的其余部分。
这极大地增加了我们的“首次有意义的痛苦”。为了解决这个问题,我们可以使用两个很少人使用但非常有效的功能。
正常执行
当你使用<script>
加载 JavaScript 文件时,它会中断文档的解析。浏览器会获取资源,执行此操作,然后继续解析:
属性Async
该Async
属性用于指示此资源可以异步执行。解析不需要暂停,可以在资源从网络获取并准备就绪后立即执行。
<script async src="script.js">
此属性只能用于外部 JavaScript 文件。该文件将并行下载,下载完成后,解析将暂停,以便执行脚本:
属性Defer
该Defer
属性用于告诉浏览器在解析整个文档后执行此脚本。
<script defer src="script.js">
就像Async
这个文件被并行下载,但只有当整个文档被解析时才会执行HTML
:
最后请记住将所有script
标签放在末尾,body
以防止解析时出现更多延迟HTML
。
至于浏览器支持,幸运的是,所有主要浏览器都完全支持这些属性。
代码拆分
大多数现代网站都会将所有 JavaScript 捆绑在一起,从而增加加载时间并影响加载性能。
代码拆分允许你将应用程序代码拆分成单独的块,并在需要时进行延迟加载。这也意味着客户端所需的代码量最少,从而缩短了页面加载时间。
您可以将代码分为三个区域:
- 供应商代码
- 入口点
- 动态分裂
供应商代码
Angular、React、moment 等供应商代码可以与主代码分离。Webpack完全支持此方法和其他方法。此技术让您能够更好地控制 bundles 的缓存失效,无论应用程序或供应商代码是否独立更改。
这是每个应用程序都应该做的事情。
入口点
这种技术通过应用中的入口点来分离代码。像 webpack 这样的打包工具在构建应用的依赖关系树时,就是从这些入口点开始的。
这是迄今为止最简单的代码分割方法,但它是手动的并且存在一些缺陷:
- 如果入口点之间有任何重复的模块,它们将被捆绑在两者中。
- 它不够灵活,不能用于动态地拆分应用程序逻辑的代码。
当您有客户端路由或混合服务器端渲染和单页应用程序时,此技术不适用。
动态分裂
使用动态代码时,请单独编写代码import
。这对于单页应用来说是最佳选择。例如,在 SPA 中为不同的路由设置不同的模块。
我是否真的需要代码分割?
这是我经常说“视情况而定”的其中一种(毕竟我是个顾问😉)。如果你的应用有很多功能独立的路由,并且大量使用了框架和库,那么答案很可能是“是”。
但是,是否需要它取决于您对应用程序结构和代码的理解。
明智地进口
如果您使用npm
或其他包管理系统来管理您的依赖项,那么您的 buid 文件夹中将会有很多额外的和不需要的文件。
当使用框架或库时,请确保调查它们是否有可以导入的单独模块,如果有,则只导入您需要的模块。
例如,假设你使用下划线,但只使用groupBy
、shuffle
和partition
。大多数人会像这样导入整个库:
import * as _ from 'underscore'
除了这个之外,您还可以导入您需要的内容:
import {
groupBy,
shuffle,
partition,
} from 'underscore'
这样,您只需携带所需的内容,打包工具会帮您处理其余部分。您的整体软件包大小以及页面加载时间都会减少。
节流和去抖
好的,关于尺寸已经说得够多了,让我们看看还有什么地方可以提高我们的性能。
很多时候,你需要添加一个事件监听器来执行某些操作,比如监听页面滚动。然而,我们却忘记了监听器会在每次触发事件时触发。
window.addEventListener('scroll', function() {
console.log('page scrolled')
})
在上面的例子中,每当你滚动屏幕时,控制台都会打印出这条消息。想象一下,如果你在这个回调函数中有一些繁重的操作,这将会成为一个很大的性能瓶颈。
如果您无法删除该事件监听器并使用其他方法,那么您可以使用debounce
或throttle
来缓解这种情况。
去抖动
此功能强制函数调用在上次调用后经过一段时间后才会发生。例如,如果距离上次调用已过去 100 毫秒,则调用该函数。
从下划线来看这个实现:
const debounce = (func, delay) => {
let inDebounce
return function() {
const context = this
const args = arguments
clearTimeout(inDebounce)
inDebounce = setTimeout(
() => func.apply(context, args),
delay
)
}
}
现在我们可以每 100 毫秒对事件监听器进行一次去抖动:
var efficientScrollListener = debounce(
function() {
console.log('page scrolled')
},
100
)
window.addEventListener(
'scroll',
efficientScrollListener
)
风门
节流与去抖动类似,但不同之处在于它会强制限制一段时间内函数的最大调用次数。例如,每 100 毫秒执行一次此函数。
以下是一个简单的实现:
const throttle = (func, limit) => {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(
() => (inThrottle = false),
limit
)
}
}
}
现在我们可以限制滚动事件监听器:
var efficientScrollListener = throttle(
function() {
console.log('page scrolled')
},
100
)
window.addEventListener(
'scroll',
efficientScrollListener
)
所以呢
我希望我已经提供了足够的信息,涵盖了在使用 JavaScript 时可以关注的一些方面,以提高应用程序的性能。如果您想了解其他主题,请在下方评论,我会在这里或其他文章中添加。
和往常一样,别忘了分享❤️。
鏂囩珷鏉ユ簮锛�https://dev.to/yashints/optimising-javascript-1f8b