Advanced JavaScript bundling
2021/02/26 AM
捆绑JavaScript模块以获得更好的性能是为了减少两件事:
-
服务器请求的数量
-
这些服务器请求的大小。
在一个模块化的应用程序中,服务器请求的数量可以达到数百个。例如,下面的截图只显示了一个干净的Magento安装的主页上加载的JavaScript模块列表的开始。
Magento merging and bundling
开箱即用,Magento提供了两种方法来减少服务器请求的数量:合并和捆绑。这些设置默认是关闭的。您可以在Magento的管理界面中的商店>设置>配置>高级>开发人员>JavaScript设置中打开它们,或者通过命令行打开。
Basic bundling
To enable Magento’s built-in bundling from the command line:
php -f bin/magento config:set dev/js/enable_js_bundling 1
This is a native Magento mechanism that combines all assets present in the system and distributes them among same-sized bundles (bundle_0.js, bundle_1.js … bundle_x.js):
更好,但浏览器仍然会加载所有的JavaScript捆绑包,而不仅仅是需要的那些。
Magento捆绑减少了每个页面的连接数,但对于每个页面请求,它都会加载所有的捆绑包,甚至当请求的页面可能只依赖于其中一个或两个捆绑包中的文件时。浏览器缓存捆绑包后,性能会有所提高。但是,由于浏览器同步加载这些捆绑包,用户第一次访问Magento店面可能需要一段时间来渲染,损害用户体验。
Basic merging
To enable Magento’s built-in merging from the command line:
php -f bin/magento config:set dev/js/merge_files 1
这个命令将所有同步的JavaScript文件合并成一个文件。启用合并而不启用捆绑是没有用的,因为Magento使用了RequireJS。如果你不启用捆绑,Magento只会合并RequireJS及其配置。当你同时启用捆绑和合并时,Magento会创建一个单一的JavaScript文件。
真实世界的渲染时间 之前的捆绑和合并的加载时间在开发环境中看起来很不错。但在现实世界中,很多事情都会拖慢渲染速度:连接速度慢、连接阈值大、网络有限。此外,移动设备的渲染速度不如台式机。
为了测试并为现实世界的店面部署做好准备,我们建议您使用Chrome的原生节流配置文件 "慢速3G "进行测试。使用慢速3G,我们之前的捆绑输出时间现在反映了许多用户的连接实际情况。
在慢速3G连接的情况下,加载一个干净的Magento安装的主页的所有捆绑包需要大约44秒。
当把捆绑包合并成一个文件时也是如此。用户仍然可以等待约42秒的初始页面加载,如图所示。
通过更先进的JavaScript捆绑方法,我们可以改善这些加载时间。
高级捆绑
请记住,JavaScript捆绑的目标是减少浏览器中加载的每个页面的请求资产的数量和大小。要做到这一点,我们希望构建我们的捆绑包,以便我们商店中的每个页面只需要为每个访问的页面下载一个通用的捆绑包和一个特定页面的捆绑包。
实现这一目标的一种方法是按页面类型定义你的捆绑包。你可以将Magento的页面分为几种页面类型,包括分类、产品、CMS、客户、购物车和结账。每一个被归入这些页面类型的页面都有一组不同的RequireJS模块依赖关系。当您按页面类型捆绑您的RequireJS模块时,您最终将只得到少量的捆绑包,这些捆绑包涵盖了您商店中任何页面的依赖关系。
例如,你可能最终会有一个捆绑包用于所有页面的依赖性,一个捆绑包用于CMS专用页面,一个捆绑包用于目录专用页面,另一个捆绑包用于搜索专用页面,还有一个捆绑包用于结账页面。
你也可以按目的创建捆绑包:通用功能、产品相关功能、运输功能、结账功能、税收和表单验证。如何定义您的捆绑包取决于您和您的Magento商店的结构。你可能会发现,一些捆绑策略会比其他策略更有效。
一个干净的Magento安装可以通过按页面类型拆分捆绑来达到足够好的性能,但一些定制可能需要更深入的分析和其他资产分布。
Required tools
The following steps require you to install and have familiarity with the following tools:
Sample code
Full versions of the sample code used in this article are available here:
第一部分
1. 新建 build.js 文件
({
optimize: 'none',
inlineText: true
})
Later, we will change the optimize:
setting from_ none
to uglify2
to minify bundle output. But for now, during development, you can leave it set to none
to ensure faster builds.
2. 新建 Add RequireJS dependencies, shims, paths, and map
({
optimize: 'none',
inlineText: true,
deps: [],
shim: {},
paths: {},
map: { "*": {} },
})
3. Aggregate the requirejs-config.js instance values
在这一步中,您需要将您的商店的requirejs-config.js文件中的所有多个deps、shim、路径和map配置节点聚合到您的build.js文件中的相应节点中。要做到这一点,您可以在浏览器的开发者工具面板中打开网络选项卡,并导航到您的商店中的任何页面,例如主页。在 "网络 "选项卡中,你会看到你的商店的requirejs-config.js文件的实例靠近顶部,这里高亮显示。
在这个文件中,你会发现每个配置节点(deps、shim、paths、map)的多个条目。你需要将这些多个节点值汇总到你的build.js文件的单个配置节点中。例如,如果您的商店的 requirejs-config.js 实例有 15 个独立的 map 节点的条目,您需要将所有 15 个节点的条目合并到您的 build.js 文件的单个 map 节点中。同样的情况也适用于deps、shim和paths节点。如果没有脚本来自动完成这个过程,可能会花费一些时间。
你需要将路径配置节点中的mage/requirejs/text改为requirejs/text,如下所示。
({
//...
paths: {
//...
"text": "requirejs/text"
},
})
4. 增加一个 modules node
在build.js文件的末尾,添加modules[]数组,作为您稍后将为您的店面定义的bundles的占位符。
({
optimize: 'none',
inlineText: true,
deps: [],
shim: {},
paths: {},
map: { "*": {} },
modules: [],
})
5. 获取 RequireJS 依赖
6. Identify unique and common bundles
其目标是创建一个所有页面所需的JavaScript文件的通用捆绑包。这样一来,浏览器只需要在加载一个或多个特定页面类型的同时加载共同的捆绑包。
在Magento根目录下打开一个终端,使用下面的命令来验证你是否有可以分割成单独捆绑的依赖关系。
sort bundle/*.txt |uniq -c |sort -n
这条命令合并了 bundle/*.txt 文件中的依赖关系并进行了排序。输出结果还显示了包含每个依赖关系的文件数量。
1 buildTools,
1 jquery/jquery.parsequery,
1 jsbuild,
2 jquery/jquery.metadata,
2 jquery/validate,
2 mage/bootstrap,
3 jquery
3 jquery/ui
3 knockoutjs/knockout
...
这个输出显示 buildTools 只在 bundle/*.txt 文件中的一个文件中是一个依赖关系。jquery/jquery.metadata 依赖在两(2)个文件中,es6-collections 在三(3)个文件中。
我们的输出只显示了三种页面类型(主页、分类和产品),这告诉我们。
三个依赖关系只在一个页面类型中出现(数字1显示)。 另外三个依赖关系出现在两个页面类型上(由数字2显示)。 最后三个依赖关系是我们所有三种页面类型所共有的(由数字3显示)。 这告诉我们,一旦我们知道哪些页面类型需要哪些依赖关系,我们就有可能通过将我们的依赖关系拆分成不同的bundle来提高我们商店的页面加载速度。
7. Create a dependencies distribution file
要找出哪些页面类型需要哪些依赖关系,在Magento根目录下创建一个名为deps-map.sh的新文件,并复制以下代码。
awk 'END {
for (R in rec) {
n = split(rec[R], t, "/")
if (n > 1)
dup[n] = dup[n] ? dup[n] RS sprintf("\t%-20s -->\t%s", rec[R], R) : \
sprintf("\t%-20s -->\t%s", rec[R], R)
}
for (D in dup) {
printf "records found in %d files:\n\n", D
printf "%s\n\n", dup[D]
}
}
{
rec[$0] = rec[$0] ? rec[$0] "/" FILENAME : FILENAME
}' bundle/*.txt