While working at the current job I see many Magento stores are affected by performance issues. Design and a lot of different functionalities can look great, but all that does not matter if you cannot deliver fast rendering to the end-customer who is, by the way, the potential buyer. There are so many different vendors focusing on proper JavaScript bundling, but recently I've tested https://github.com/magento/baler, and I wanted here to share my insights.
In short, baler is an AMD (https://requirejs.org/) module bundler and preloader for Magento 2 stores.
How Does it Work?
baler has two different strategies for optimizing JavaScript delivery
Core Bundle
The "core" bundle contains the code that baler can determine is critical to the first render of the page. This includes the Magento core libraries, along with some theme-specific and feature-specific code.
Graph Preloading
The JavaScript included in the "core" bundle only includes dependencies that can be statically-analyzed from a theme's requirejs-config.js. However, many parts of the Magento front-end are controlled by widgets that are specified using a declarative notation in .phtml files.
This is where Graph Preloading comes in. When baler is run, it crawls the file system and determines which .phtml are eligible to be used in a specific area (frontend/adminhtml/base). These templates are then analyzed for any AMD module dependencies.
When a graph of dependencies has been collected for each .phtml file, the lists are flattened and deduped against the "core" bundle. Then, when a shopper requests a page of your store, preload tags are injected that instruct the browser to immediately begin fetching necessary dependencies.
I've tested on latest Magento 2.3.3 CE version, and you can check page load results here https://magentocommand.ml
It does what it says in manual, creating one core-bundle.js containing all possible aspects from core side which can be bundled safely.
What is JavaScript bundling?
Javascript bundling is a technique that groups separate files in order to reduce the number of HTTP requests that are required to load a page. Bundling is commonly used in today’s “module-based” development where some functionalities are basically split into Modules (roughly explained).
So let me show today how I did it on Fresh Magento 2.3.3 CE version with default Luma theme.
1) We need to clone current Baler repository:
git clone https://github.com/magento/baler.git
2) You can cd into baler folder and install it using npm
npm install
3) Optionally, if you don't wish to get anything installed on your local server there is already ready made binary file within bin/ folder named baler.
4) Make sure that you have node.js version >= 10.12.0 installed.
~/baler/bin$ ./baler
baler requires a version of node.js >= 10.12.0. You're currently using v8.16.0
This can be fixed easily by downloading latest Node version:
wget -c https://nodejs.org/dist/v10.14.1/node-v10.14.1-linux-x64.tar.gz
Then let's unpack archive:
tar -xvf node-v10.14.1-linux-x64.tar.gz
Once extracted we can export active PATH to new Node installation:
export PATH=/srv/baler/node-v10.14.1-linux-x64/bin:$PATH
Mine direct path was /srv/baler/node-v10.14.1-linux-x64/bin but you can use pwd command to get real path and configure it.
In case you receive the following error:
./baler --help
Error: Cannot find module '../dist/cli/initializeCLI'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
at Function.Module._load (internal/modules/cjs/loader.js:507:25)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (internal/modules/cjs/helpers.js:22:18)
at Object.<anonymous> (/srv/baler/bin/baler:39:1)
at Module._compile (internal/modules/cjs/loader.js:689:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
Fix is to run:
npm run build
To test once this is installed:
~/baler/bin$ ./baler --help
Usage
$ baler <command> [options]
Commands
build --theme Vendor/name
graph --theme Vendor/name
Examples
Optimize all eligible themes
$ baler build
Optimize multiple themes
$ baler build --theme Magento/foo --theme Magento/bar
Generate Dependency Graph
$ baler graph --theme Magento/luma
Next jump to the Magento 2 docroot and run it:
:~/public_html$ /srv/baler/bin/baler
✔ Collected theme/module data 155ms
✔ (Magento/luma) Created core bundle file 1.2s
✔ (Magento/luma) Minified core bundle and RequireJS 17.6s
Optimization is done, but stats have not been implemented in the CLI yet
Clear Magento and Redis (if installed) cache:
~/public_html$ n98-magerun2 cache:clean
config cache cleaned
layout cache cleaned
block_html cache cleaned
collections cache cleaned
reflection cache cleaned
db_ddl cache cleaned
compiled_config cache cleaned
eav cache cleaned
customer_notification cache cleaned
config_integration cache cleaned
config_integration_api cache cleaned
google_product cache cleaned
full_page cache cleaned
config_webservice cache cleaned
translate cache cleaned
vertex cache cleaned
~/public_html$ n98-magerun2 cache:flush
redis-cli flushconfig cache flushed
layout cache flushed
block_html cache flushed
collections cache flushed
reflection cache flushed
db_ddl cache flushed
compiled_config cache flushed
eav cache flushed
customer_notification cache flushed
config_integration cache flushed
config_integration_api cache flushed
google_product cache flushed
full_page cache flushed
config_webservice cache flushed
translate cache flushed
vertex cache flushed
~/public_html$ redis-cli flushall
OK
(this one is optional if Redis used)
Next, we need to install Magento 2 module which is going to handle bundlings:
composer config repositories.baler vcs https://github.com/adifucan/m2-baler
Then run:
composer require magento/module-baler:dev-master
Activate module:
n98-magerun2 module:enable Magento_Baler
Run deployment process (setup:upgrade ; setup:di:compile and setup:static-content:deploy)
n98-magerun2 setup:upgrade
n98-magerun2 setup:di:compile
n98-magerun2 setup:static-content:deploy
Clear Magento/Redis cache like we did it upper and then push the following:
php bin/magento config:set dev/js/enable_baler_js_bundling 1
Run the baler once again:
$ /srv/baler/bin/baler build
✔ Collected theme/module data 156ms
✔ (Magento/luma) Created core bundle file 1.2s
✔ (Magento/luma) Minified core bundle and RequireJS 17.8s
Optimization is done, but stats have not been implemented in the CLI yet
Then we need to disable Magento 2 built in JavaScript Minify/Merge/Bundling:
php bin/magento config:set dev/js/minify_files 0
php bin/magento config:set dev/js/enable_js_bundling 0
php bin/magento config:set dev/js/merge_files 0
Clear Magento/Redis cache once again (yeah I know boring...), load frontpage and test...
Addon:
While this is still in Alpha testing, there are certain things that needs to be patched. You can read them here https://github.com/DrewML/baler/issues/6
If you just want to apply the fixes, see the patch in core here https://github.com/magento/magento2/commit/db43c11c6830465b764ede32abb7262258e5f574
That's it! Most pages in your storefront should now be faster than they were before.
Hope this article helps. I strongly encourage you before optimizing your store to check https://drewml.com/posts/improving-javascript-delivery-in-magento2/ excellent blog post about what is Baler and what is the current project state.