In this article, we’ll review how your choice of module formatting systems affect bundling size, issues with CommonJS, how ES modules address the issues, and how you can implement ES modules when using Wijmo.
In June 2020, Angular 10 released with a host of new features and fixes. One of the new features implemented is a warning that will be shown when building an application that has dependencies packaged using CommonJS modules:
The reason for this warning is that using CommonJS modules can result in larger bundle sizes when performing a production build. The bundle size increase is due to how CommonJS was designed, but we’ll touch more on that later. If you’re concerned about the performance of your application and are using CommonJS modules, you do have another option: ES modules (EcmaScript modules).
In this article, we’ll review how your choice of module formatting systems affect bundling size, issues with CommonJS, how ES modules address the issues, and how you can implement ES modules when using Wijmo.
Modules and Bundle Sizes
Modules are a way for developers to better organize variables and functions through the module scope. The module scope can be thought of like a global scope; variables and functions that are made available in the module scope can be accessed by other modules that also have access to the module scope.
When something is made available to other modules, it’s called an export. Once you have created an export, other modules can import that class, explicitly stating that they depend on that variable, class, or function.
Once your application can import and export variables and functions between modules, you can more easily organize your application by breaking it up into smaller chunks that get imported for their functionality.
Since variables and functions are now broken up across multiple files, your application will now need to bundle everything together when performing a production build. How your module system handles importing and exporting functionality, as well as what the module system decides needed to be imported, affect the size of your bundles.
Because modules are so useful, there have been multiple attempts to bring module/module functionality to JavaScript. The two most popular module systems are CommonJS and ES modules.
CommonJS and ES Modules
CommonJS is a module formatting system created for JavaScript that was released back in 2009. Since then, it has become the standard for structuring and organizing JavaScript code. Originally designed for server-side applications, the standard has heavily influenced how NodeJS organizes its module management.
With CommonJS, you can define modules, export functionality from them, and import them in other modules using the require
function.
For example, if you have a function to verify user input made available to be exported from its module, you can use the require
function to give another module access to it:
exports.verifyInput = (userInput) =>
const { verifyInput } = require(‘./verify.js’);
…
const validData = verifyInput(userInput);
Since the verifyInput
function was made available as an export, we’re now able to use the require
method inside of the index.js file to import that functionality and make use of it.
Because CommonJS was initially created to be used on server-side applications, running on client-side applications came with performance costs that were inconsequential on server-side applications. However, because there was a lack of standardized module systems available in browsers when CommonJS was created, it became a popular module format system for JavaScript client-side libraries as well.
Due to these performance and optimization issues for client-side applications, ES modules were created with the browser in mind. Importing and exporting functionality is very similar to that of CommonJS; instead of using require
to import variables and functions, you’ll use an import statement:
exports.verifyInput =>
import verifyInput from ‘./verify.js’;
…
const validData = verifyInput(userInput);
The big difference between ES modules and CommonJS is how the modules are loaded. CommonJS is more dynamic, while ES modules are more static.
To give you an example of what I mean, when importing functionality from another module, CommonJS allows you to pass a variable into the path, whereas ES modules require it to be a string literal:
const { verifyinput } = require(‘./${path}/verify.js’);
import verifyInput from (‘./${path}/verify.js’);
import verifyInput from (‘./login/verify.js);
CommonJS can take this dynamic approach because of how the dependencies are loaded. When CommonJS needs to load modules, it is done at runtime. The modules are loaded, instantiated, and evaluated all at once. This is what allows you to do things like pass variables into file paths (though that is just a simple example). Since this is done at runtime, it makes it harder for applications to perform tree-shaking.
Tree-shaking is when your application can remove any unused functionality from files to make your application smaller. These are things like removing unused functions, comments, and redundant whitespace.
Because CommonJS loads, instantiates, and evaluates all at runtime, it makes it harder for tree-shaking to occur, as well as be less efficient when it happens. The reason that tree-shaking could take place was because of webpack’s ability to statically (at build time) understand what we were importing from and what is exported.
ES modules don’t have this same problem. Because ES modules are more static than CommonJS, they’re more statically analyzable. The loading, instantiating, and evaluating that takes place is broken up into three different phases that can be done separately, instead of together like with CommonJS. This allows for more efficient tree-shaking.
Using ES Modules with Wijmo
With the release of Wijmo 2020v2 comes support for using ES modules. The use of ES modules is enabled by default, meaning that you won’t need to take any action to use ES modules.
From our tests, you can expect to see a decrease in bundle size for those including Wijmo components of roughly 30%.
You can see a breakdown of bundle size decreases when using ES modules versus CommonJS with Wijmo below:
Do note that: If you wish to continue using CommonJS modules with Wijmo, you can do so by running the following command:
npm run wijmo-esm -- -disable
If you have any issues using wijmo-esm, you can also run the following command for more information:
npm run wijmo-esm -- -help
To build your application in production mode, you’ll need to add the following command to the "scripts" section of your package.json file:
"prod": "ng build --prod",
You’ll then be able to build your application by running npm run –prod
and be able to see the bundled files inside of the created dist folder.