Reduce JavaScript payloads with code splitting 2

78 阅读4分钟

大多数网页和应用程序是由许多不同的部分组成的。与在加载第一个页面时立即发送组成应用程序的所有JavaScript不同,将JavaScript拆分为多个块可以提高页面性能。

这个代码实验室展示了如何使用代码拆分来提高对三个数字进行排序的简单应用程序的性能。

代码地址:glitch.com/search?q=co…

措施

由于此应用程序中使用了 webpack,因此对代码所做的任何更改都将触发新的构建,这可能需要几秒钟。 完成后,您应该会看到您的更改反映在应用程序中。

像往常一样,在尝试添加任何优化之前,首先衡量一个网站的性能是很重要的。

  • 要预览网站,按查看应用程序。
  • 然后按全屏全屏。 
  • 按“Control+Shift+J”(或Mac上的“Command+Option+J”)打开DevTools。 单击“网络”页签。 
  • 选择“禁用缓存”复选框。 
  • 重新加载应用程序。

71.2 KB的JavaScript代码只用于在一个简单的应用程序中对一些数字进行排序。到底发生了什么事?

在源代码(src/index.js)中,导入了lodash库并在该应用程序中使用。Lodash提供了许多有用的实用程序函数,但这里只使用了包中的一个方法。在只使用了一小部分的情况下安装和导入整个第三方依赖是一个常见的错误。

优化

有几种方法可以减少bundle的大小:

  • 编写自定义排序方法,而不是导入第三方库
  • 使用内置的Array.prototype.sort()方法进行数值排序
  • 只从lodash导入sortBy方法,而不是整个库
  • 只在用户单击按钮时下载排序代码

选项1和2是减少bundle大小的非常合适的方法(对于真正的应用程序可能最有意义)。但是,为了教学😈,本教程中不使用这些。

在单独的指南中更详细地探讨了删除未使用代码的概念。(separate guide.)

选项 3 和 4 都有助于提高此应用程序的性能。 本 Codelab 的接下来几节将介绍这些步骤。 像任何编码教程一样,始终尝试自己编写代码,而不是复制和粘贴。

github 地址:codelab-code-splitting

仅仅导入你需要的

需要修改一些文件以仅从 lodash.import 导入单个方法。 首先,在 package.json 中替换此依赖项:

"lodash": "^4.7.0",

使用

"lodash.sortby": "^4.7.0",

现在在 src/index.js 中,导入这个特定的模块:

import "./style.css";// import _ from "lodash";import sortBy from "lodash.sortby";

并更新值的排序方式::

form.addEventListener("submit", e => {
    e.preventDefault();  
    const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
    const sortedValues = _.sortBy(values);  
    const sortedValues = sortBy(values);  
    results.innerHTML = `<h2>${sortedValues}</h2>`
});

重新加载应用程序,打开 DevTools,然后再次查看 Network 面板。

对于这个应用程序,捆绑包的大小减少了 4 倍以上,而且工作量很小,但仍有很大的改进空间。

代码分割

Webpack是当今最流行的开源模块捆绑器之一。简而言之,它将构成web应用程序的所有JavaScript模块(以及其他资源)捆绑到可以被浏览器读取的静态文件中。

此应用程序中使用的单个捆绑包可以分成两个单独的块:

  • 负责构成我们初始路由的代码 
  • 包含我们的排序代码的次要块

通过使用动态导入,可以延迟加载辅助块,或按需加载。 在这个应用程序中,组成块的代码只有在用户按下按钮时才能被加载。

首先删除 src/index.js 中排序方法的顶级导入:

// import sortBy from "lodash.sortby";

并将其导入到按下按钮时触发的事件侦听器中:

form.addEventListener("submit", e => {
    e.preventDefault();
    import('lodash.sortby')
        .then(module => module.default)
        .then(sortInput())    
        .catch(err => { alert(err) });
});

import() 功能是提案的一部分(目前处于 TC39 流程的第 3 阶段),包括动态导入模块的功能。 webpack 已经包含了对相同语法的支持,并遵循提案中的规定。

在此 Web 更新文章(Web Updates article)中阅读有关动态导入如何工作的更多信息。

警告:  在生产应用程序中,始终适当地处理动态导入错误。 与此处使用的类似的简单警报消息可能无法提供最佳的用户体验来让用户知道某些事情发生了故障。

您可能会看到一个 linting 错误,上面写着:bash Parsing error: 'import' and 'export' may only appear at the top level。 这是因为动态导入语法仍处于提案阶段,尚未最终确定。 尽管 webpack 已经支持它,但 Glitch 使用的 ESLint(一个 JavaScript linting 实用程序)的设置尚未更新以包含此语法,但它仍然有效!

需要做的最后一件事是在文件末尾编写 sortInput 方法。 这需要是一个函数,该函数返回一个函数,该函数接受从 lodash.sortBy 导入的方法。 然后嵌套函数可以对三个输入值进行排序并更新 DOM。

const sortInput = () => {  
    return (sortBy) => {    
        const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];    
        const sortedValues = sortBy(values);    
        results.innerHTML = `<h2>${sortedValues}</h2>`
    };
}

检测

最后一次重新加载应用程序并再次密切关注网络面板。 应用程序加载后,只会下载一个小的初始捆绑包。

按下按钮对输入数字进行排序后,将获取并执行包含排序代码的块。

注意这些数字是如何排序的!

结论

代码分割和延迟加载是非常有用的技术,可以减少应用程序的初始包大小,这可以直接导致更快的页面加载时间。但是,在您的应用程序中包含这种优化之前,有一些重要的事情需要考虑。

延迟加载UI

当惰性加载特定的代码模块时,重要的是要考虑网络连接较弱的用户的体验。当用户提交操作时,拆分并加载非常大的代码块会使应用程序看起来像是停止了工作,因此可以考虑显示某种类型的加载指示器。

延迟加载第三方节点模块

许多使用webpack的流行框架和库提供了抽象,使惰性加载比在应用程序中间使用动态导入更容易。

尽管了解动态导入的工作原理很有用,但请始终使用您的框架/库推荐的方法来延迟加载特定模块。

预加载和预取

在可能的情况下,利用浏览器提示,如或,以便尝试并更快地加载关键模块。Webpack通过在导入语句中使用神奇注释来支持这两种提示。这在Preload关键块指南中有更详细的解释。(Preload critical chunks)

图像是应用程序的重要组成部分。延迟加载那些在折叠之下,或在设备视口之外的内容,可以加快网站的速度。在 Lazysizes 指南中阅读更多相关内容。