Below, I shed light on such useful technology as RequireJS and compare it with the closest competitors. Since here on the Firebear blog we are passionate about Magento 2, I also explain how to configure and adjust RequireJS with the second version of the ecommerce platform. The article itself combines real world coding experience, notes from the official documentation, code snippets, and materials from reliable sources. You will find a complete resource list in the end of this blog post.
Such opportunities are vital if you come from Java, Python, or any other server side language. Thus, by using RequireJS, you get the ability to define dependencies for your JS files. Besides, the module loader checks if those dependencies are loaded in the correct order. As a result, the code gets all necessary variables.
There are several reasons for using modules, and all of them are based on constantly rising requirements. Since websites are getting bigger, code complexity grows; at the same time, some of them are turning into web apps. Thus, assembly gets harder, and new standards appear. For instance, discrete JS files are required, and code should be optimized in just a few HTTP calls. As a result, front-end developers need a tool that offer the following features:
support for loading nested dependencies
ease of use
optimization of work
availability of deployment
First thing you can think about is a script loading API, such as CommonJS: require(“some/module”). Since it is a synchronous call, there is a necessity for returning the module immediately, that leads to lots of issues in a browser.
Furthermore, if require() is async, your code will never work, but synchronous loading in the browser absolutely destroys performance. Thus, such approach is not a good idea.But how about using XMLHttpRequest (XHR) instead?
Unfortunately, XHR requires using eval(), utilizes script tags, and has problems with cross-domain requests. That’s why you should think about another method to load scripts.
If you are going to choose Web Workers, note that they do not offer strong cross browser support. Another problem is related to a message-passing API nature of this approach, while the scripts likely want to interact with the DOM.
In addition to aforementioned approaches to script loading, it is necessary to mention document.write(), as it provides the ability to load scripts from other domains. Besides, it allows easy debugging. Unfortunately, it is impossible to execute that script directly, since we should know its dependencies before the script is executed, and that is impossible.
Thus, creating scripts on demand can be a good idea. Unlike document.write(), such approach helps to avoid blocking page rendering. Moreover, it works after page load, but the problem of unknown dependencies is still unsolved.
That’s why the only available solution is to construct a module loading API with function wrappers – the approach utilized by RequireJS. This laconic syntax type allows the loader to use head.appendChild(script) loading. A function wrapping format used by RequireJS is Asynchronous Module Definition (AMD).
RequireJS against competitors
With Webpack, you can easily split your codebase into several chunks, which can be loaded on demand. As a result, such approach helps to reduce the initial loading time.
It supports CommonJS and AMD module styles. Besides, there is a support for the most existing libraries.
There is also the ability to use source maps. This feature leads to easier debugging.
Webpack relies on the same require() function as Node.js; therefore, you can share modules between both client and server-side.
The tool also offers a flexible plugin infrastructure, so you can easily create your plugins and loaders.
Webpack works well with npm’s modules.
User friendly dependency management with the require() function and a dependency graph.
The ability to share the same modules between client and server-side.
npm modules, CJS module format, and modules from other systems (deAMDify and debowerify features)
Browser-friendly shims of Node.js modules. this feature offer the ability to use Node event systems as well as path and URL parsing.
Easier debugging with source maps.
Closure wrapping for each module.
It is also necessary to mention that Browserify has the following problems: there is no dynamic loading and it does nothing client-side. Thus, I recommend you to pay attention to RequireJS.
Supports all desktop browsers.
Optimized for nested dependencies.
AMD implementation with the ability to implement CJS
Offers all necessary documentation and is easy to use.
Does not require a server for getting started
Offers a RequireJS optimizer designed to minify built files improving the performance of your projects.
Since REquireJS has a wide community, all its problems are fixed rapidly.
At the same time, there are some disadvantages related to RequireJS. While it is awesome for working with dependencies in an async manner, RequireJS is not the best solution to create a library for both client and server. Some other problems are discussed below.
One of the most important problems of using RequireJS is related to the AMD standard, which no longer holds a significant technical advantage over alternatives. Moreover, it has a less clear syntax and experiences problems with network effects. AMD modules, used by RequireJS, have the following look:
As you can see, we have to deal with a define function while defining a new module. There are 2 arguments. The first one is an array of dependencies. The second argument is related to a callback function. The function is passed in values exported by each dependency. It is run after the dependencies have been loaded.
Moreover, AMD provides a require function. Although the function takes the same arguments, it plays a role of the program’s initialization point, loading and executing all the dependencies when it’s run.
As for two other standards, CommonJS and the ES6 Modules syntax, they have the following structure:
import Class1 from'file1';
import Class2 from'file2';
Thus, CommonJS and ES6 standards offer some obvious advantages: they do not require an outer function for wrapping your code. Besides, CommonJS or ES6 syntaxes do not require two changes of code for every added or removed dependency. In case of AMD it is necessary to specify all dependencies as an array of strings. That result in parameters to a callback function, so it is necessary to change code in two places when you add or remove a dependency.
The asynchronous nature nature of AMD still remains an advantage, but other module loaders offer similar opportunities. For instance, Webpack provides configuration options which will help you lazy-load individual modules. As for Browserify, there is also a workaround to the problem. So, other module formats no longer offer less opportunities compared to AMD. There are still a lot of differences, but they are not crucial. Moreover, by using AMD modules you can reduce the range of tools possible in your development, especially compared to CommonJS.
RequireJS and Magento 2 Tutorial
Magento is the most popular ecommerce platform and Magento 2 is its upcoming version. The solution is aimed at creating robust ecommerce stores with all necessary capabilities. With Magento 2 you will get tons of marketing features, user friendly interface, out-of-the-box SEO, and other optimizations required for a successful web store. Besides, the new version of the platform offers top notch performance, which can be significantly improved, for instance with the help of the RequireJS library. Let’s see, how to use RequireJS within Magento 2.
How to Configure the RequireJS library
To make RequireJS library available in Magento 2, specify it along with specific configurations in layout.xml. Use the following code:
For more precise configurations, identify mapping at several levels. Note that there is a certain order for collecting and executing configurations.
The first are library configurations.
Then, the priority goes to the module level (considering dependencies between both modules and themes).
As for configurations at the theme module level, the ancestor themes are involved first. Then comes a current theme.
The same is about configurations at the theme level: the priority goes to the ancestor themes and then to the current theme.
Besides standard to RequireJS aliases, Magento uses relative paths. Therefore, you should specify the relative paths to JS resources in RequireJS configurations. For the following path app/code/Magento/Catalog/view/frontend/requirejs-config.js, utilize configurations similar to oneslisted below:
// configuration for resource 'app/code/Magento/Catalog/view/frontend/product/product.js'
./product/productis a relative path to Catalogmodule JS resources.
Please note that the baseUrlparameter is generated automatically, so you you should not specify it in the configuration file.
How to Adjust RequireJS
There are two ways to adjust RequireJS in Magento 2. Firstly, you can use the fallback mechanism. Secondly, it is possible to utilize configuration files – the mechanism is described above.