In this article you will learn:
- What is a Rollup
- CommonJS, AMD, CMD, UMD, ES6 respectively
- Differences between ES6 modules and CommonJS modules
- Tree Shaking for module evolution
- What should Tree Shaking look for
All examples of this article are available at github.com/hua1995116/…
The introduction
I ran into a problem with rollup packaging today
Error: 'Map' is not exported by node_modules/immutable/dist/immutable.js
Copy the code
typeof exports === 'object' && typeof module! = ='undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.Immutable = factory();
Copy the code
It is discovered that immutable is exposed in the form UMD. Rollup does not support CommonJS and AMD packaging. In order to successfully introduce CommonJS modules, you must load the plugin github.com/rollup/plug… Of course, not all CommonJS files are automatically supported. Only static scripts can be exported, such as pin dynamic module export, and implicit export will not be exported automatically. The above example is a dynamic approach, only when the factory function is executed to know the exported module, you need to specify manually.
commonjs({
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'immutable': ['Map']}});Copy the code
Of course, the above is just one of the reasons for me to write this article. It is because of my confusion on this piece of knowledge that I want to review this piece of knowledge again. You may not understand what I am saying at all.
What’s Rollup?
Since I was the one who introduced the concept in the first place, IT’s up to me to fill in the holes, and of course those of you who are familiar with this tool can skip it. Unfamiliar friends you just need to know, this is a package ES Module tool.
Rollup is a JavaScript module wrapper that compiles small pieces of code into large, complex pieces of code, such as libraries or applications. Rollup uses new standardized formats for code modules that are included in the ES6 version of JavaScript, rather than previous ad-hoc solutions such as CommonJS and AMD. ES6 modules allow you to use the most useful stand-alone functions in your favorite library freely and seamlessly, without having to carry around other unused code in your projects. ES6 modules will eventually be implemented natively in the browser, but the current Rollup gives you an early taste.
CommonJS
CommonJS specification
CommonJS runs primarily on the server side, and the specification states that a single file is a module. Node.js, the main practitioner, has four important environment variables that support modular implementations: Module, exports, require, and global. The require command is used to import functions provided by other modules, and the module.exports command is used to specify the module’s external interface. Exports a copy of a value that cannot be changed and is cached.
/ / module a. s
const name = 'qiufeng'
module.exports = {
name,
github: 'https://github.com/hua1995116'
}
Copy the code
/ / module b.j s
// Reference core modules or third-party package modules, no need to write the full path
const path = require('path');
// References to custom modules can omit.js
const { name, github } = require('./a');
console.log(name, github, path.basename(github));
/ / output qiufeng hua1995116 at https://github.com/hua1995116
Copy the code
Code address: github.com/hua1995116/…
CommonJS uses synchronous loading of modules, and most of the loaded file resources are on the local server, so execution speed or time is fine. However, on the browser side, it is more reasonable to use asynchronous loading for network reasons.
AMD
AMD specification
AMD stands for “Asynchronous Module Definition”. It loads modules asynchronously without affecting the execution of subsequent statements. All statements that depend on this module are defined in a callback function that will not run until the load is complete. RequireJS is the best practitioner.
Module functions Main commands: define, require, return and define. Amd. Define is a global function that defines modules. Define (ID? , dependencies? The factory). The require command is used to input functions provided by other modules, the return command is used to standardize the external interface of modules, and the define. Amd property is an object whose existence indicates that the function complies with AMD specifications.
// model1.js
define(function () {
console.log('model1 entry');
return {
getHello: function () {
return 'model1'; }}; });Copy the code
// model2.js
define(function () {
console.log('model2 entry');
return {
getHello: function () {
return 'model2'; }}; });Copy the code
// main.js
define(function (require) {
var model1 = require('./model1');
console.log(model1.getHello());
var model2 = require('./model2');
console.log(model2.getHello());
});
Copy the code
<script src="https://cdn.bootcss.com/require.js/2.3.6/require.min.js"></script>
<script>
requirejs(['main']);
</script>
Copy the code
// Output the result
// model1 entry
// model2 entry
// model1
// model2
Copy the code
Code address: github.com/hua1995116/…
Here, we use define to define the module, return to output the interface, and require to load the module, which is the official AMD recommendation.
CMD
CMD specification
CMD(Common Module Definition – Common Module Definition) specification is mainly formed in the promotion of Sea-js, a file is a Module, you can write Module code like Node.js. It runs primarily in a browser, but it can also run in Node.js.
It is similar to AMD, except that AMD relies on front-loading and up-front execution, while CMD relies on nearby and delayed execution.
Those of you who don’t know how to rely on nearby, delayed execution can compare the examples below and above.
// model1.js
define(function (require, exports, module) {
console.log('model1 entry');
exports.getHello = function () {
return 'model1'; }});Copy the code
// model2.js
define(function (require, exports, module) {
console.log('model2 entry');
exports.getHello = function () {
return 'model2'; }});Copy the code
// main.js
define(function(require, exports, module) {
var model1 = require('./model1'); // If required
console.log(model1.getHello());
var model2 = require('./model2'); // If required
console.log(model2.getHello());
});
Copy the code
<script src="https://cdn.bootcss.com/seajs/3.0.3/sea.js"></script>
<script>
seajs.use('./main.js')
</script>
Copy the code
/ / output
// model1 entry
// model1
// model2 entry
// model2
Copy the code
Github.com/hua1995116/…
Conclusion: Contrast AMD with CMD to see the difference between AMD and CMD. Although now CMD is cold. But CMD is closer to CommonJS, while AMD is closer to browser asynchronous execution.
UMD
UMD document
UMD(Universal Module Definition – Universal Module Definition) pattern, which is mainly used to solve the CommonJS and AMD pattern code is not common, and also supports the old global variable specification.
Example shows
// bundle.js
(function (global, factory) {
typeof exports === 'object' && typeof module! = ='undefined' ? module.exports = factory() :
typeof define === 'function'&& define.amd ? define(factory) : (global = global || self, global.myBundle = factory()); } (this, (function () { 'use strict';
var main = (a)= > {
return 'hello world';
};
return main;
})));
Copy the code
// index.html
<script src="bundle.js"></script>
<script>
console.log(myBundle());
</script>
Copy the code
- judge
Define as
Function, and whether it existsdefine.amd
, to determine whether it is an AMD specification, - judge
module
Is an object and existsmodule.exports
To see if it isCommonJS
specification - If neither is available, use the original code specification.
Code address: github.com/hua1995116/…
ES Modules
ES Modules document
ES Modules (ESM) is the official standardized module system for JavaScript.
- Because it is standard, many browsers will support it in the future, and it can be easily used in browsers. (Browser default loading cannot omit.js)
- It is also compatible with running in node environments.
- Module import and export, through
import
andexport
To determine. Can be mixed with Commonjs modules. - ES Modules outputs references to values, interfaces are dynamically bound, and CommonJS outputs copies of values
- ES Modules modules are executed at compile time, whereas CommonJS modules are always loaded at run time
use
// index.js
import { name, github } from './demo.js';
console.log(name(), github());
document.body.innerHTML = `<h1>${name()} ${github()}</h1>`
Copy the code
export function name() {
return 'qiufeng';
}
export function github() {
return 'https://github.com/hua1995116';
}
Copy the code
<script src="./index.js" type="module"></script>
Copy the code
Code address: github.com/hua1995116/…
For details, see the ES6 module mechanism
CommonJS value copy
// a.js
const b = require('./b');
console.log(b.age);
setTimeout((a)= > {
console.log(b.age);
console.log(require('./b').age);
}, 100);
Copy the code
// b.js
let age = 1;
setTimeout((a)= > {
age = 18;
}, 10);
module.exports = {
age
}
// Execute: node a.js
// Execution result:
/ / 1
/ / 1
/ / 1
Copy the code
CommonJS has two main features
- The position of require in the CommonJS module will affect the output and produce a copy of the value
- Modules introduced repeatedly by the CommonJS module are not executed repeatedly, and retrieving modules again only retrieves the cache of previously acquired modules
Reference to the value of ES modules
// a.js
import { age } from './b.js';
console.log(age);
setTimeout((a)= > {
console.log(age);
import('./b.js').then(({ age }) = > {
console.log(age); })},100);
// b.js
export let age = 1;
setTimeout((a)= > {
age = 2;
}, 10);
/ / open index. HTML
// Execution result:
/ / 1
/ / 2
/ / 2
Copy the code
What’s the difference between dynamically loading and statically compiling?
Here’s an example:
Dynamic loading, only when the module is running, can know what the exported module is.
var test = 'hello'
module.exports = {
[test + '1'] :'world'
}
Copy the code
Static compilation, at compile time you know what module to export.
export function hello() {return 'world'}
Copy the code
Compilation time execution of ES6 modules results in the following two characteristics:
- Import commands are statically parsed by the JavaScript engine, taking precedence over the rest of the module.
- The export command has the effect of pre-declaring variables.
Import is preferentially executed:
// a.js
console.log('a.js')
import { age } from './b.js';
// b.js
export let age = 1;
console.log('B.js executed first');
// Run index. HTML
// b.js executes first
// a.js
Copy the code
Although the order of import is lower, the promotion effect of import is carried out first.
Export variable declaration enhancement:
// a.js
import { foo } from './b.js';
console.log('a.js');
export const bar = 1;
export const bar2 = (a)= > {
console.log('bar2');
}
export function bar3() {
console.log('bar3');
}
// b.js
export let foo = 1;
import * as a from './a.js';
console.log(a);
// Run node --experimental-modules a.js
// [Module] {
// bar:
,
// bar2:
,
// bar3: [Function: bar3]
}
Copy the code
Code address: github.com/hua1995116/…
As you can see from the above example, module A references module B, and module B also references module A, and the export declaration takes precedence over other contents. Since variable and function promotions are different, I won’t go into too much detail here.
Here is an episode where MY initial execution in the browser results in:
{
bar: 1
bar2: () => { console.log('bar2'); ƒ bar3()Copy the code
I once wondered if there was any special promotion of export. Because I found that babel-node was used in the article of in-depth understanding of ES6 module mechanism, whether it was caused by different environment. Therefore, I used Node V12.16.0 to test node –experimental-modules A.js, and found that the results were consistent with those in the in-depth understanding of ES6 module mechanism. Later, I thought of the problem of console.log display. Console. log often has some asynchronous display. Later I tested it and found it was console.log
console.log(a);
-> console.log(JSON.stringify(a))
An Uncaught ReferenceError: bar is not defined occurs because the bar is not initialized. The presentation of the console will also be reported to Chromium later.
Tree shaking
After introducing the standards for each module, why shaking the Tree? Because modularity changes time and time again, our modular systems are getting better and better, and Tree shaking is the product of the development of benefit ES modules.
Rollup came up with this concept. Rollup recommends using ES2015 Modules to code Modules, so you can use tree-shaking to statically analyze code and remove useless code. Check out the REPL examples on the Rollup website for the differences before and after code was packaged. It becomes clear what tree-shaking is.
- Replace export’s module by locating import directly without using an additional module system
- Unused code is removed
A practical example of tree shaking
// main.js
import * as utils from './utils';
const array = [1.2.3.1.2.3]
console.log(utils.arrayUnique(array));
Copy the code
Code address: github.com/hua1995116/…
Tree Shaking vs. no Tree Shaking packaging.
In the absence of tree-shaking, all files in utils are packaged and the size explodes.
ES Modules are tree-shaking for the following four reasons (from You Yuxi’s answer in Zhihu) :
import
Can only be used as a top-level statement in a module, not in a function or if.import
Can only be string constants.- No matter
import
Where does the statement appear when the module is initializedimport
All must have been imported. import binding
是immutable
Similar to const. For example, you can’t import {a} from ‘./a ‘and then assign something else to a.
What should Tree Shaking look for
Side effects
Yes, side effects. So what are side effects? Here’s an example.
// effect.js
console.log(unused());
export function unused() {
console.log(1);
}
Copy the code
// index.js
import {unused} from './effect';
console.log(42);
Copy the code
In this example, console.log(unused()); Side effects. This sentence console.log is not required in index.js. Rollup does not know if this global function removal is safe. So in the packaging when. You can explicitly specify treeshake moduleSideEffects to false, can display to tell a rollup external dependencies there is no other side effects.
Package output when not specified. npx rollup index.js –file bundle.js
console.log(unused());
function unused() {
console.log(1);
}
console.log(42);
Copy the code
Specifies packaged output with no side effects. npx rollup index.js –file bundle-no-effect.js –no-treeshake.moduleSideEffects
console.log(42);
Copy the code
Code address: github.com/hua1995116/…
Of course, the above is only one kind of side effects, details of other kinds of see rollupjs.org/guide/en/
conclusion
UMD = CommonJS + AMD, ES Module is the standard specification, replacing UMD, is the general trend. Tree-shaking Remember side effects.
reference
Github.com/rollup/roll…
Github.com/rollup/plug…
www.zhihu.com/question/63…
www.yuque.com/baichuan/no…
Github.com/indutny/web…
Xbhong. Top / 2018/03/12 /…
www.douban.com/note/283566…
Blog.fundebug.com/2018/08/15/…
Huangxuan. Me/js – module – 7…
www.jianshu.com/p/6c26fb754…