Magento 2 JS loading. What can we do for optimization?
Introduction.
One of the important factors for e-commerce is its performance. One of the big problems for the Magento 2 store is its large number of loaded JS files on the page. The Magento recommendation and built-in mechanism don’t give good results, aren’t flexible, and sometimes break on missing files. There are several ways to improve JS file loading in Magento. All of these recommendations can be implemented concurrently and positively impact performance. Let’s see these ways below:
1. Fix Jquery Compat
The browser console identifies this problem as a notification:
Fallback to JQueryUI Compat activated. Your store is missing a dependency for a jQueryUI widget. Identifying and addressing the dependency will drastically improve the performance of your site. Compat.js
The issue is caused by certain JS or PHTML files calling jquery/ui as a dependency like this:
define([
...
'jquery/ui'
...
],
That means that Magento is basically loading all jQuery UI modules because it can’t identify which one(s) the custom code needs. Here is a list of all jQuery UI modules (you can see them when you open compat.js in the console warning message):
'jquery-ui-modules/core',
'jquery-ui-modules/accordion',
'jquery-ui-modules/autocomplete',
'jquery-ui-modules/button',
'jquery-ui-modules/datepicker',
'jquery-ui-modules/dialog',
'jquery-ui-modules/draggable',
'jquery-ui-modules/droppable',
'jquery-ui-modules/effect-blind',
'jquery-ui-modules/effect-bounce',
'jquery-ui-modules/effect-clip',
'jquery-ui-modules/effect-drop',
'jquery-ui-modules/effect-explode',
'jquery-ui-modules/effect-fade',
'jquery-ui-modules/effect-fold',
'jquery-ui-modules/effect-highlight',
'jquery-ui-modules/effect-scale',
'jquery-ui-modules/effect-pulsate',
'jquery-ui-modules/effect-shake',
'jquery-ui-modules/effect-slide',
'jquery-ui-modules/effect-transfer',
'jquery-ui-modules/effect',
'jquery-ui-modules/menu',
'jquery-ui-modules/mouse',
'jquery-ui-modules/position',
'jquery-ui-modules/progressbar',
'jquery-ui-modules/resizable',
'jquery-ui-modules/selectable',
'jquery-ui-modules/slider',
'jquery-ui-modules/sortable',
'jquery-ui-modules/spinner',
'jquery-ui-modules/tabs',
'jquery-ui-modules/timepicker',
'jquery-ui-modules/tooltip',
'jquery-ui-modules/widget'
So to deal with the issue and decrease the size of the loading JS, you need to figure out which module of jquery/ui is needed in each particular instance and replace jquery/ui with them in all files located in app/code and app/design/frontend/{vendor}/{theme}/
Example:
define([
'jquery',
'mage/template',
- 'jquery/ui'
+ 'jquery-ui-modules/widget'
], function ($, mageTemplate) {
'use strict';
var editTriggerPrototype;
$.widget('mage.editTrigger', {
...
We replaced ‘jquery/ui’ with the ‘jquery-ui-modules/widget’ because of the widget module present in the JS.
2. Reduce JS size
Some Magento core modules, custom extensions, or themes use unnecessary dependencies that increase page load time. We can fix this problem and not load large JS files during initialization. We can change the way of calling large JS only during the call. Some of the large JS are spectrum, tinycolor, moment, jarallax, jarallaxVideo, etc.
Origin file:
define([
'jquery',
'jarallax',
'jarallaxVideo',
'vimeoWrapper'
], function ($) {
'use strict';
return function (config, element) {
var $element = $(element),
parallaxSpeed = $element.data('enableParallax') !== 1 ? 1 : parseFloat($element.data('parallaxSpeed'));
if ($element.data('background-type') !== 'video') {
return;
}
$element.addClass('jarallax');
$element.attr('data-jarallax', '');
window.jarallax($element[0], {
imgSrc: $element.data('videoFallbackSrc'),
speed: !isNaN(parallaxSpeed) ? parallaxSpeed : 0.5,
videoLoop: $element.data('videoLoop'),
videoPlayOnlyVisible: $element.data('videoPlayOnlyVisible'),
videoLazyLoading: $element.data('videoLazyLoad'),
disableVideo: false,
elementInViewport: $element.data('elementInViewport') &&
$element[0].querySelector($element.data('elementInViewport'))
});
$element[0].jarallax.video && $element[0].jarallax.video.on('started', function () {
if ($element[0].jarallax.$video) {
$element[0].jarallax.$video.style.visibility = 'visible';
}
});
};
});
Remove large JS from the define to prevent all of these JS from being loaded with the main file, and change the call of these JS to get them during the function call:
define([
'jquery'
], function ($) {
'use strict';
return function (config, element) {
var $element = $(element),
parallaxSpeed = $element.data('enableParallax') !== 1 ? 1 : parseFloat($element.data('parallaxSpeed'));
if ($element.data('background-type') !== 'video') {
return;
}
require(['jarallax', 'jarallaxVideo', 'vimeoWrapper'], function () {
$element.addClass('jarallax');
$element.attr('data-jarallax', '');
window.jarallax($element[0], {
imgSrc: $element.data('videoFallbackSrc'),
speed: !isNaN(parallaxSpeed) ? parallaxSpeed : 0.5,
videoLoop: $element.data('videoLoop'),
videoPlayOnlyVisible: $element.data('videoPlayOnlyVisible'),
videoLazyLoading: $element.data('videoLazyLoad'),
disableVideo: false,
elementInViewport: $element.data('elementInViewport') &&
$element[0].querySelector($element.data('elementInViewport'))
});
$element[0].jarallax.video && $element[0].jarallax.video.on('started', function () {
if ($element[0].jarallax.$video) {
$element[0].jarallax.$video.style.visibility = 'visible';
}
});
});
};
});
These changes can be applied to the core files with the patch and improve performance for some important JS libraries.
3. Advanced JS bundling
The goal of JavaScript bundling is to reduce the number and size of requested assets for each page loaded in the browser. This allows us to improve performance.
We will use the MagePack. MagePack does this by building the bundles in such a way that each page in our store will need to download a common bundle and a page-specific bundle for each page accessed.
A common way to achieve this is by defining bundles by page type. This allows us to create separate bundles for the dependencies common to different types of pages: a bundle for Category-only pages, a bundle for CMS-only pages, a bundle for Checkout pages, and so on. Each page categorized into one of these page types has a different set of RequireJS module dependencies. When you bundle your RequireJS modules by page type, you will end up with only a handful of bundles that cover the dependencies of any page in your store.
In our article, we will see how we work with MagePack, instruments, how to analyze JS and the logic of building the configuration file for MagePack.
We have to install NPM and Node to start working with MagePack. After that, we can install MagePack in the project folder. Copy the file package.json.sample to the project folder as a package.json and run the install command:
npm install magepack
After that, we have to install Magento MagePack extension:
composer require creativestyle/magesuite-magepack
Create the configuration file for MagePack in the project root folder:
magepack/magepack.config.js
module.exports = [
{
url: [],
name: "common",
modules: {},
},
{
url: [],
name: "cms",
modules: {},
},
{
url: [],
name: "category",
modules: {},
},
{
url: [],
name: "product",
modules: {},
},
{
url: [],
name: "checkout",
modules: {},
},
]
Install the extension for getting HTML templates applied on the page and add them to the configuration file.
└── {Vendor}/{Name}
├── Controller
│ └── Clean
│ └──── Test.php
├── etc
│ ├── frontend
│ │ └── routes.xml
│ └── module.xml
├── view
│ └── frontend
│ ├── layout
│ │ ├── default_head_blocks.xml
│ │ └── magepack_test_clean_test.xml
│ └── templates
│ ├── clear.phtml
│ └── html_request_lists.phtml
└── registration.php
The file html_request_lists.phtml contains the script that returns information about all HTML templates applied to the page:
{Vendor}/{Name}/view/frontend/templates/html_request_lists.phtml
<script>
window.nodesString = '';
XMLHttpRequest.prototype.realSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(value) {
this.addEventListener("progress", function() {
var nodesString = this.responseURL.replace(require.toUrl(''), "" );
nodesString = '"text!' + nodesString + '":"' + nodesString + '",';
window.nodesString = window.nodesString + nodesString;
}, false);
this.realSend(value);
};
setTimeout(function () {
var nodesString = window.nodesString.replace(/\"\,\"/g, '",\n"')
console.log(nodesString);
}, 3000);
</script>
Include this script on all pages:
{Vendor}/{Name}/view/frontend/layout/default_head_blocks.xml
...
<body>
<referenceBlock name="head.additional">
<block name="magepack_test_templates" template="{Vendor}_{Name}::html_request_lists.phtml"/>
</referenceBlock>
</body>
...
Let’s start forming the configuration file from the common section. Create an empty page for getting the HTML templates loaded on every page.
{Vendor}/{Name}/Controller/Clean/Test.php
<?php
declare(strict_types=1);
namespace {Vendor}\{Name}\Controller\Clean;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\View\Result\Page;
class Test implements ActionInterface, HttpGetActionInterface
{
/**
* @var ResultFactory
*/
private ResultFactory $resultFactory;
/**
* @param ResultFactory $resultFactory
*/
public function __construct(
ResultFactory $resultFactory
) {
$this->resultFactory = $resultFactory;
}
/**
* @return Page
*/
public function execute()
{
return $this->resultFactory->create(ResultFactory::TYPE_PAGE);
}
}
{Vendor}/{Name}/etc/frontend/routes.xml
...
<router id="standard">
<route id="magepack_test" frontName="magepack_test">
<module name="{Vendor}_{Name}"/>
</route>
</router>
...
Create a page layout without a header and footer to get a clear page.
{Vendor}/{Name}/view/frontend/layout/magepack_test_clean_test.xml
...
<body>
<referenceContainer name="header.container" remove="true"/>
<referenceContainer name="footer-container" remove="true"/>
<referenceBlock name="navigation.sections" remove="true"/>
<referenceContainer name="main.content">
<block name="magepack_test" template="{Vendor}_{Name}::clear.phtml"/>
</referenceContainer>
</body>
...
{Vendor}/{Name}/view/frontend/templates/clear.phtml
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut interdum pharetra vestibulum. Maecenas a mauris
scelerisque, placerat neque aliquam, pretium diam. Duis sed augue non arcu sagittis volutpat in a metus. Suspendisse
potenti. Sed turpis ex, ornare a iaculis ut, dictum ut diam. Etiam felis orci, aliquam a eros ac, sagittis imperdiet
dui. Phasellus vitae pretium erat.
</p>
As a result, you will see the following information in the browser console by URL {base_url}/magepack_test/clean/test:
"text!Magento_Ui/templates/tooltip/tooltip.html":"Magento_Ui/templates/tooltip/tooltip.html",
"text!Magento_Ui/templates/block-loader.html":"Magento_Ui/templates/block-loader.html",
"text!Magento_Customer/template/show-password.html":"Magento_Customer/template/show-password.html",
"text!Magento_Customer/template/authentication-popup.html":"Magento_Customer/template/authentication-popup.html",
"text!Magento_Ui/template/messages.html":"Magento_Ui/template/messages.html",
"text!Magento_Captcha/template/checkout/captcha.html":"Magento_Captcha/template/checkout/captcha.html",
Now we can fill in these templates in the configuration file:
magepack/magepack.config.js
...
{
url: [],
name: "common",
modules: {
"text!Magento_Ui/templates/tooltip/tooltip.html": "Magento_Ui/templates/tooltip/tooltip.html",
"text!Magento_Ui/templates/block-loader.html": "Magento_Ui/templates/block-loader.html",
"text!Magento_Customer/template/show-password.html": "Magento_Customer/template/show-password.html",
"text!Magento_Customer/template/authentication-popup.html": "Magento_Customer/template/authentication-popup.html",
"text!Magento_Ui/template/messages.html": "Magento_Ui/template/messages.html",
"text!Magento_Captcha/template/checkout/captcha.html": "Magento_Captcha/template/checkout/captcha.html",
},
},
...
To get all JS that loading on the page, we use the following script in the browser console:
nodes = document.querySelectorAll('[data-requiremodule]');
nodesString = '';
i = 0;
nodes.forEach(function(elem) {
elem = jQuery(elem);
nodesString = nodesString + '"' + elem.attr('data-requiremodule') + '":"' + elem.attr('src') + '",';
})
nodesString = nodesString.replaceAll( require.toUrl(''), "" );
nodesString = nodesString.replace(/\.js\"\,\"/g, '",\n"');
nodesString = nodesString.replace(/\.[^/.]+$/, '",');
console.log(nodesString);
As a result, we can see all the JS applied on the page. Add these JS to the config file in the common section:
{
url: [],
name: "common",
modules: {
"text!Magento_Ui/templates/tooltip/tooltip.html": "Magento_Ui/templates/tooltip/tooltip.html",
"text!Magento_Ui/templates/block-loader.html": "Magento_Ui/templates/block-loader.html",
"text!Magento_Customer/template/show-password.html": "Magento_Customer/template/show-password.html",
"text!Magento_Customer/template/authentication-popup.html": "Magento_Customer/template/authentication-popup.html",
"text!Magento_Ui/template/messages.html": "Magento_Ui/template/messages.html",
"text!Magento_Captcha/template/checkout/captcha.html": "Magento_Captcha/template/checkout/captcha.html",
"mage/common":"mage/common",
"mage/dataPost":"mage/dataPost",
"mage/bootstrap":"mage/bootstrap",
"Magento_Ui/js/core/app":"Magento_Ui/js/core/app",
"Magento_PageCache/js/form-key-provider": "Magento_PageCache/js/form-key-provider",
"Magento_Translation/js/mage-translation-dictionary": "Magento_Translation/js/mage-translation-dictionary",
"Magento_ConfigurableProduct/js/configurable": "Magento_ConfigurableProduct/js/configurable",
"Magento_Theme/js/theme":"Magento_Theme/js/theme",
"js/mage/header":"js/mage/header",
"js/searchMutation":"js/searchMutation",
"js-cookie/cookie-wrapper":"js-cookie/cookie-wrapper",
"Magento_Ui/js/modal/alert":"Magento_Ui/js/modal/alert",
"Magento_Customer/js/customer-data": "Magento_Customer/js/customer-data",
"Magento_Persistent/js/view/customer-data-mixin": "Magento_Persistent/js/view/customer-data-mixin",
"Magento_ReCaptchaWebapiUi/js/jquery-mixin": "Magento_ReCaptchaWebapiUi/js/jquery-mixin",
"jquery/jquery-migrate":"jquery/jquery-migrate",
"domReady":"requirejs/domReady",
"mage/template":"mage/template",
"Magento_Ui/js/modal/confirm": "Magento_Ui/js/modal/confirm",
"jquery/ui-modules/widget": "jquery/ui-modules/widget",
"mage/apply/main":"mage/apply/main",
"Magento_Ui/js/lib/knockout/bootstrap": "Magento_Ui/js/lib/knockout/bootstrap",
"text":"mage/requirejs/text",
"underscore":"underscore",
"mage/translate":"mage/translate",
"Magento_Catalog/js/price-utils": "Magento_Catalog/js/price-utils",
"jquery/jquery.parsequery": "jquery/jquery.parsequery",
"mage/smart-keyboard-handler": "mage/smart-keyboard-handler",
"mage/mage":"mage/mage",
"mage/ie-class-fixer": "mage/ie-class-fixer",
},
},
Notes:
- Exclude large JS, such as “jquery,” “jquery-ui-modules/timepicker,” and so on.
- Add jquery widgets and mage JS to the common section.
- Add the following HTML templates to the common section because these templates have a specific call:
"text!ui/template/block-loader.html": "Magento_Ui/templates/block-loader.html",
"text!ui/template/collection.html": "Magento_Ui/templates/collection.html",
"text!ui/template/modal/modal-custom.html": "Magento_Ui/templates/modal/modal-custom.html",
"text!ui/template/modal/modal-popup.html": "Magento_Ui/templates/modal/modal-popup.html",
"text!ui/template/modal/modal-slide.html": "Magento_Ui/templates/modal/modal-slide.html",
"text!ui/template/tooltip/tooltip.html": "Magento_Ui/templates/tooltip/tooltip.html"
After preparing the config file with the common section, we can create the common bundled JS and check the result. Following the execution of Magento Deploy, we have to generate a bundle JS with MagePack by issuing the command:
'./node_modules/magepack/cli.js' bundle -c './magepack/magepack.config.js' -m -g 'pub/static/frontend/Magento/luma/en_US/'
‘./node_modules/magepack/cli.js’ – MagePack CLI;
bundle – option for bundle JS files using a given configuration file;
‘./magepack/magepack.config.js’ – configuration file;
-m – minify bundle using Terser irrespective of Magento 2 minification setting;
-g – glob pattern of themes to bundle;
‘pub/static/frontend/Magento/luma/en_US/’ – active theme folder;
In the same logic, we are processing the CMS, PLP, and PDP. The common JS was generated, and we won’t see bundled JS and HTML templates on these pages during the analysis. That means the CMS, PLP, and PDP bundle sections will present files loaded only on these pages.
The checkout section that includes JS and HTML templates from the checkout and cart pages doesn’t include the common section. That means that the checkout bundle will contain all JS and HTML templates from these pages. As a result, the checkout bundle can be large. To resolve this problem, the MagePack uses prefetch for bundles (https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/prefetch).
Do one more optimization in the MagePack. Add the logic for including bundles’ RequireJS configuration on the page. Create the extension with the next structure:
└──{Vendor}/{Name}
├── Block
│ └── BundlesLoader.php
├── etc
│ └── module.xml
├── view
│ └── frontend
│ └── layout
│ ├── checkout_cart_index.xml
│ ├── checkout_index_index.xml
│ └── default.xml
└── registration.php
Create bundles loader by including bundles RequireJS config files:
{Vendor}/{Name}/Block/BundlesLoader.php
<?php
declare(strict_types=1);
namespace {Vendor}\{Name}\Block;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Filesystem\DirectoryList;
use Magento\Framework\RequireJs\Config as RequireJsConfig;
use Magento\Framework\View\Asset\Minification;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use Magento\Framework\View\Page\Config as PageConfig;
use Magento\Store\Model\ScopeInterface;
use MageSuite\Magepack\Model\FileManager;
/**
* Block needed to handle JS bundles in layout.
*/
class BundlesLoader extends Template
{
private const XML_PATH_ENABLE_MAGEPACK_BUNDLING = 'dev/js/enable_magepack_js_bundling';
/**
* @var PageConfig
*/
protected $pageConfig;
/**
* @var string
*/
protected $_template = 'MageSuite_Magepack::bundles-loader.phtml';
/**
* @var DirectoryList
*/
private DirectoryList $dir;
/**
* @var FileManager
*/
private FileManager $fileManager;
/**
* @var ScopeConfigInterface
*/
private ScopeConfigInterface $scopeConfig;
/**
* @var RequireJsConfig
*/
private RequireJsConfig $requireJsConfig;
/**
* @var Minification
*/
private Minification $minification;
/**
* @param Context $context
* @param DirectoryList $dir
* @param FileManager $fileManager
* @param PageConfig $pageConfig
* @param RequireJsConfig $requireJsConfig
* @param ScopeConfigInterface $scopeConfig
* @param Minification $minification
* @param array $data
*/
public function __construct(
Context $context,
DirectoryList $dir,
FileManager $fileManager,
PageConfig $pageConfig,
RequireJsConfig $requireJsConfig,
ScopeConfigInterface $scopeConfig,
Minification $minification,
array $data=[]
) {
$this->dir = $dir;
$this->fileManager = $fileManager;
$this->pageConfig = $pageConfig;
$this->scopeConfig = $scopeConfig;
$this->requireJsConfig = $requireJsConfig;
$this->minification = $minification;
parent::__construct($context, $data);
}
/**
* @return boolean
*/
public function isEnabled(): bool
{
return (bool)$this->scopeConfig->isSetFlag(
self::XML_PATH_ENABLE_MAGEPACK_BUNDLING,
ScopeInterface::SCOPE_STORE
);
}
/**
* @return string
*/
public function getCommonBundleUrl(): string
{
$commonBundle = $this->getData('common_bundle');
if ($commonBundle && $this->isEnabled()) {
return $this->getViewFileUrl($commonBundle['bundle_path']);
}
return '';
}
/**
* @return string[] List of page bundles URLs.
*/
public function getPageBundlesUrls(): array
{
$pageBundles = $this->getData('page_bundles');
if (!empty($pageBundles) && $this->isEnabled()) {
return array_map(
function ($pageBundle) {
return $this->getViewFileUrl($pageBundle['bundle_path']);
},
$pageBundles
);
}
return [];
}
/**
* @return string[] List of bundles URLs to prefetch when browser is idle.
*/
public function getPrefetchBundlesUrls(): array
{
$prefetchBundles = $this->getData('prefetch_bundles');
if (!empty($prefetchBundles) && $this->isEnabled()) {
return array_values(
array_map(
function ($prefetchBundle) {
return $this->getViewFileUrl($prefetchBundle);
},
$prefetchBundles
)
);
}
return [];
}
/**
* Adjust layout to include all bundles configuration files.
*
* @return BundlesLoader
* @throws FileSystemException
*/
protected function _prepareLayout(): BundlesLoader
{
if ($this->isEnabled()) {
foreach ($this->getBundlesConfigPaths() as $bundleConfigPath) {
$this->addBundleConfig($bundleConfigPath);
}
}
return parent::_prepareLayout();
}
/**
* Adds given bundle configuration to the head scripts.
*
* @param $bundleConfigPath
* @return void
* @throws FileSystemException
*/
private function addBundleConfig($bundleConfigPath): void
{
if (!$bundleConfigPath) {
return;
}
$assetCollection = $this->pageConfig->getAssetCollection();
$bundleConfigAsset = $this->fileManager->createRequireJsConfigAsset($bundleConfigPath);
$bundleConfigRelPath = $bundleConfigAsset->getFilePath();
$staticDir = $this->dir->getPath('static');
$bundleConfigAbsPath = $staticDir.'/'.$bundleConfigRelPath;
/*
* Add bundle config before main requirejs-config.js file to make sure all modules are loaded from them.
*/
if (file_exists($bundleConfigAbsPath)) {
$assetCollection->insert(
$bundleConfigRelPath,
$bundleConfigAsset,
$this->requireJsConfig->getMixinsFileRelativePath()
);
}
}
/**
* Prepares and returns a list of all defined bundles configurations paths.
*/
private function getBundlesConfigPaths(): array
{
$configPaths = [];
$commonBundle = $this->getCommonBundle();
if ($commonBundle) {
$configPaths[] = $this->minification->addMinifiedSign($commonBundle['config_path']);
}
$pageBundles = ($this->getPageBundles() ?? []);
foreach ($pageBundles as $pageBundle) {
$configPaths[] = $this->minification->addMinifiedSign($pageBundle['config_path']);
}
$requireJsConfigUrls = ($this->getBundlesRequirejsConfig() ?? []);
foreach ($requireJsConfigUrls as $requireJsConfigUrl) {
$configPaths[] = $this->minification->addMinifiedSign($requireJsConfigUrl['config_path']);
}
return array_reverse($configPaths);
}
}
Change blocks for the MagePack in the layouts:
{Vendor}/{Name}/view/frontend/layout/checkout_cart_index.xml
<body>
<referenceBlock name="magepack.bundles" remove="true"/>
<referenceBlock name="head.additional">
<block class="MageSuite\Magepack\Block\BundlesLoader" name="magepack.bundles.cart">
<arguments>
<argument name="page_bundles" xsi:type="array">
<item name="product" xsi:type="array">
<item name="config_path" xsi:type="string">
magepack/requirejs-config-checkout.js
</item>
<item name="bundle_path" xsi:type="string">
magepack/bundle-checkout.js
</item>
</item>
</argument>
</arguments>
</block>
</referenceBlock>
</body>
{Vendor}/{Name}/view/frontend/layout/checkout_index_index.xml
<body>
<referenceBlock name="magepack.bundles" remove="true"/>
<referenceBlock name="head.additional">
<block class="MageSuite\Magepack\Block\BundlesLoader" name="magepack.bundles.checkout">
<arguments>
<argument name="page_bundles" xsi:type="array">
<item name="product" xsi:type="array">
<item name="config_path" xsi:type="string">
magepack/requirejs-config-checkout.js
</item>
<item name="bundle_path" xsi:type="string">
magepack/bundle-checkout.js
</item>
</item>
</argument>
</arguments>
</block>
</referenceBlock>
</body>
{Vendor}/{Name}/view/frontend/layout/default.xml
<referenceBlock name="magepack.bundles" class="{Vendor}\{Name}\Block\BundlesLoader">
<arguments>
<argument name="page_bundles" xsi:type="array"/>
<argument name="bundles_requirejs_config" xsi:type="array">
<item name="category" xsi:type="array">
<item name="config_path" xsi:type="string">
magepack/requirejs-config-category.js
</item>
</item>
<item name="product" xsi:type="array">
<item name="config_path" xsi:type="string">
magepack/requirejs-config-product.js
</item>
</item>
</argument>
<argument name="prefetch_bundles" xsi:type="array">
<item name="checkout_bundle" xsi:type="string">
magepack/requirejs-config-checkout.js
</item>
<item name="checkout_config" xsi:type="string">
magepack/bundle-checkout.js
</item>
</argument>
</arguments>
</referenceBlock>
We can create a custom section for generating bundle JS files. This functionality is used for creating bundle JS for some specific pages, separating large bundle JS or creating more flexible use of the bundled files. In our case, we join duplicates from the category and product page to the new section catalog.
Create a new section in the config file and put duplicates there:
magepack/magepack.config.js
{
url: [],
name: "catalog",
modules: {
...
"Magento_Catalog/js/catalog-add-to-cart": "Magento_Catalog/js/catalog-add-to-cart",
"Magento_Catalog/js/price-box": "Magento_Catalog/js/price-box",
"text!Magento_Catalog/template/product/image_with_borders.html": "Magento_Catalog/template/product/image_with_borders.html",
...
}
}
After that, add a new config patch to bundles_requirejs_config, and the file will be generated automatically:
{Vendor}/{Name}/view/frontend/layout/default.xml
...
<argument name="bundles_requirejs_config" xsi:type="array">
<item name="catalog" xsi:type="array">
<item name="config_path" xsi:type="string">
magepack/requirejs-config-catalog.js
</item>
</item>
</argument>
...
After that, we just generate bundle JS files after the deployment process finishes:
'./node_modules/magepack/cli.js' bundle -c './magepack/magepack.config.js' -m -g 'pub/static/frontend/Magento/luma/en_US/'
Compare the results of the different JS optimization builds in Magento with MagePack.
Home Page
Magento JS merge and minify:
Magento JS bundling:
MagePack bundling:
Category Page
Magento JS merge and minify:
Magento JS bundling:
MagePack bundling:
Sum up.
We have identified the main problems in the Magento 2 JS. Determine how these issues can be resolved. We got acquainted with the MagePack and how we can work with it. We compared and got information that MagePack gives better improvement results for the loading of JS in Magento 2.