Current mainstream module specifications
- UMD
- CommonJs
- es6 module
Umd module (General purpose module)
(function (global, factory) {
typeof exports === 'object' && typeof module! = ='undefined' ? module.exports = factory() :
typeof define === 'function'&& define.amd ? define(factory) : (global.libName = factory()); } (this, (function () { 'use strict'; })));Copy the code
If you see this code in the header of a JS file, this file is using the UMD specification and it’s actually a combination of amd + CommonJS + global variables and this code is a judgment on the current runtime environment, if it’s a Node environment it’s using the CommonJS specification, With UMD, our code runs on both Nodes and browsers, so now most of the front-end libraries are packaged using the UMD specification
CommonJs
The Nodejs environment uses a module system based on the CommonJs specification, which we now refer to as the Node module system
Module export
Keywords: Module. exports exports
// foo.js
// Export one by one
module.exports.age = 1
module.exports.foo = function(){}
exports.a = 'hello'
// Export as a whole
module.exports = { age: 1.a: 'hello'.foo:function(){}}// Whole exports cannot be used with 'exports.' Exports cannot be used with imports
exports = { age: 1.a: 'hello'.foo:function(){}}Copy the code
Exports = module.exports before the module starts. After that, exports loses its reference to module.exports and becomes a local variable in a module
Module import
Key words: require
const foo = require('./foo.js')
console.log(foo.age) / / 1
Copy the code
Module import rules:
Call require() for files in SRC /app/index.js
./moduleA
Start of relative path
Find the sibling directory without specifying the suffix: SRC /app/
src/app/moduleA
No file name extension as perjavascript
parsingsrc/app/moduleA.js
Js file according tojavascript
parsingsrc/app/moduleA.json
The JSON file followsjson
parsingsrc/app/moduleA.node
The node file compiles the plug-in module dlopen as loaded
If no moduleA file is found in the same directory, the SRC /app/moduleA directory will be found
src/app/moduleA/package.json
Check whether the directory existspackage.json
Files, if any are foundmain
Field definition file returns ifmain
Field indicates file does not exist ormain
The field does not exist orpackage.json
File does not exist to execute downsrc/app/moduleA/index.js
src/app/moduleA/index.json
src/app/moduleA/index.node
The end of the
/module/moduleA
Start of absolute path
Find the rule directly in the /module/moduleA directory as above
react
No path start
If the path does not start with a path, it is considered to import a package. If the path does not start with a path, it is considered to import a core module. If the path does not start with a path, it will import a core module first
/src/app/node_modules/
Find rules as above to importreact
As an exampleJson ->react. Node ->react directory ->react package.json -> index. index.json -> index.node
If you don’t find the parent directorynode_modules
In looking for/src/node_modules/
/node_modules/
Until finally can’t find the end
require wrapper
Node modules can actually be understood as code wrapped in a simple require demo in a function wrapper:
function wrapper (script) {
return '(function (exports, require, module, __filename, __dirname) {' +
script +
'\n})'
}
function require(id) {
var cachedModule = Module._cache[id];
if(cachedModule){
return cachedModule.exports;
}
const module = { exports: {}}// Add the reference to the cache
Module._cache[id] = module
// Not eval
eval(wrapper('module.exports = "123"'(a))module.exports, require.module.'filename'.'dirname')
return module.exports
}
Copy the code
From the above code we can know:
- The module calls the acquired object after executing it only once
module.exports
It’s all cache even this onejs
Not finished yet (because the module was added to the cache before it was executed) - Module export is
return
And this variable is essentially thetaa = b
The assignmentThe same,Basic types ofThe exported isvalue.Reference typesThe exported isRefer to the address exports
和module.exports
Holds the same reference because the last exported ismodule.exports
And so onexports
Performing an assignment causesexports
The operation is no longermodule.exports
A reference to the
A circular reference
// a.js
module.exports.a = 1
var b = require('./b')
console.log(b)
module.exports.a = 2
// b.js
module.exports.b = 11
var a = require('./a')
console.log(a)
module.exports.b = 22
//main.js
var a = require('./a')
console.log(a)
Copy the code
Run this code with the require demo above to analyze each step of the process:
Nodemain.js -> require(a.js)
, (node
The execution can also be understood as calling the require method, which we omitrequire(main.js)
Content)Enter the require(a) method: check the cache (none) -> initialize a module -> add module to the cache -> execute the contents of module A. js
It should be noted thatFirst,joinThe cache.After the implementationModule content)A. js: a = 1 -> b: require(b.js)
(A only executes the first line)Enter the same content as 1 -> execute module B. js in require(b)
B.js: line 1 b = 11 -> line 2 require(a.js)
Require (a) this is the second call to require -> judge cache -> cachedModule.exports -> return to b.js
(because thejs
Object reference problem at this timecachedModule.exports = { a: 1 }
)B. js: output {a: 1} -> modify the fourth line B = 22 -> Return to A. js after execution
{b: 22} -> a = 2 -> return to main.js
Main.js: get a -> second line of output {a: 2} -> Execute complete
These are the general rules for parsing and running node modules
es6 module
Before ES6, javascript didn’t have its own module specification, so the community developed the CommonJs specification, and Node borrowed ideas from the CommonJs specification and developed Node module. The AMD asynchronous module is also derived from the Commonjs specification, and then has require.js running in browsers
Es6 Module Basic syntax
export
export * from 'module'; // The redirected export does not include default in module
export{ name1, name2, ... , nameN }from 'module'; // Redirect the named export
export { import1 as name1, import2 asname2, ... , nameN }from 'module'; // Redirect and rename the export
export { name1, name2, …, nameN }; // Bind the export to the previously declared variable name
export { variable1 as name1, variable2 asName2,... , nameN };// Rename the export
export let name1 = 'name1'; // Declare a named export or var, const, function, function*, class
export default expression; // Export by default
export default function () {... }Function *, class
export default function name1() {... }Function *, class
export { name1 as default. };// Rename the default export
Copy the code
export
The rules
export * from ''
orexport {} from ''
, redirection export, redirection name is not used in this module, just to build a bridge, for example: thisa
Cannot be used in this moduleexport {}
, bound to the variable name, named exportexport Declaration
Declare, name the export,DeclarationIs this:var
.let
.const
.function
.function*
.class
A declaration of this classexport default AssignmentExpression
, default export,AssignmentExpressionThe scope of the statement is very wide and can be roughly understood as except declarationDeclaration
(There’s actually a crossover between the two),a=2
.i++
.i/4
.a===b
.obj[name]
.name in obj
.func()
.new P()
.[1, 2, 3]
.function(){}
And so on many
import
// Name the export module.js
let a = 1,b = 2
export { a, b }
export let c = 3
// Name the import main.js
import { a, b, c } from 'module'; // a: 1 b: 2 c: 3
import { a as newA, b, c as newC } from 'module'; // newA: 1 b: 2 newC: 3
// Export module.js by default
export default 1
// Import main.js by default
import defaultExport from 'module'; // defaultExport: 1
// Mix export module.js
let a = 1
export { a }
const b = 2
export { b }
export let c = 3
export default [1.2.3]
// Mix import main.js
import defaultExport, { a, b, c as newC} from 'module'; //defaultExport: [1, 2, 3] a: 1 b: 2 newC: 3
import defaultExport, * as name from 'module'; //defaultExport: [1, 2, 3] name: { a: 1, b: 2, c: 3 }
import * as name from 'module'; // name: { a: 1, b: 2, c: 3, default: [1, 2, 3] }
// module.js
Array.prototype.remove = function(){}
// Side effects run only one module
import 'module'; // Execute module without exporting value multiple calls to module.js only run once
// Dynamic import (asynchronous import)
var promise = import('module');
Copy the code
import
The rules
import { } from 'module'
, importmodule.js
theNamed after the exportimport defaultExport from 'module'
, importmodule.js
theThe default is derivedimport * as name from 'module'
That will beThe module of js
theAll exportCombined intoname
The object,key
Specifies the name of the export, the default exportkey
fordefault
import 'module'
Side effects, just runmodule
Instead of exporting content such as polyfill, the statement can only be executed onceimport('module')
Dynamic import returns onePromise
.TC39
thestage-3
Phase is proposedtc39 import
ES6 module
The characteristics of
ES6 module
Syntax is static
Imports are automatically promoted to the top level of the code
Export and import can only appear at the top level of the code
//if for while, etc
{
export let a = 1
import defaultExport from 'module'
}
true || export let a = 1
Copy the code
The import name of import cannot be a string or in a statement, the following code is wrong
import 'defaultExport' from 'module'
let name = 'Export'
import 'default' + name from 'module'
Copy the code
Static syntax means that imports and exports can be determined at compile time, and dependencies can be found more quicklylint
Tools for checking module dependencies, imports and exports plus type information for static type checking
####ES6 Module export is bound ####
Modules that are imported using import run in strict mode
Variables that are imported using import are read only, so it is understood that they default to const decorations and cannot be assigned
Variables imported with import are bound to/referenced by the original variable, which can be understood as import variables are passed by reference regardless of whether they are basic types
// The base type in js is value passing
let a = 1
let b = a
b = 2
console.log(a,b) / / 1. 2
// The reference type in js is reference pass
let obj = {name:'obj'}
let obj2 = obj
obj2.name = 'obj2'
console.log(obj.name, obj2.name) // obj2 obj2
// Base types in es6 Modules are also passed by reference
// foo.js
export let a = 1
export function count(){
a++
}
// main.js
import { a, count } from './foo'
console.log(a) / / 1
count()
console.log(a) / / 2
// Export default cannot be dynamically bound to A. This is a bit similar to CommonJs, which is a copy of the value
let a = 1;
export default a
// Dynamic binding of default can be implemented in another way
let a = 1;
export { a as default }
export function count(){
a++
}
// Same as main.js above
Copy the code
This code is the difference between CommonJs exported variables and ES6 exported variables
Es Module circular reference
// bar.js
import { foo } from './foo'
console.log(foo);
export let bar = 'bar'
// foo.js
import { bar } from './bar'
console.log(bar);
export let foo = 'foo'
// main.js
import { bar } from './bar'
console.log(bar)
Copy the code
Go to main.js -> import bar.js
Bar.js -> import foo.js
Foo. Js -> import bar.js -> bar.js has been executed directly return -> output bar -> bar is not defined, bar is not defined error
We can use function to solve this problem:
// bar.js
import { foo } from './foo'
console.log(foo());
export function bar(){
return 'bar'
}
// foo.js
import { bar } from './bar'
console.log(bar());
export function foo(){
return 'foo'
}
// main.js
import { bar } from './bar'
console.log(bar)
Copy the code
Since function declarations are prompted at the top of the file, you can call the still-executing bar method of bar.js directly from foo.js. Do not use external variables inside the function, since variables have not been declared (let,const) and assigned (var)
CommonJs and ES6 Modules
In fact, we’ve already talked about some of the differences
CommonJs
You export a copy of the variable,ES6 Module
What is exported is the binding of variables (export default
Is special)CommonJs
Is a single value export,ES6 Module
You can export multipleCommonJs
It’s dynamic syntax that can be written in judgment,ES6 Module
Static syntax can only be written at the top levelCommonJs
thethis
Is the current module,ES6 Module
thethis
是undefined
Confusing point
Module syntax and deconstruction
The module syntax is easily confused with the deconstruction syntax, for example:
import { a } from 'module'
const { a } = require('module')
Copy the code
You can’t say that two people are the same person if they wear the same clothes. Import /export {a} / {a, b} / {a as c}
let { a } = { a: 1 }
let { a = 2} = {}let { a: b } = { a: 1 }
let { a: b = 2. res } = {name:'a' }
let { a: b, obj: { name } } = { a: 1.obj: { name: '1'}}function foo({a: []}) {}
Copy the code
They are two very different things, one is module import and export, and the other is to get the syntactic sugar of the object
Export syntax and object property abbreviations
The following code is also confusing
let a = 1
export { a } // Export the syntax
export default { a } // Attribute shorthand exports {a: 1} objects
module.exports = { a } // Attribute shorthand exports {a: 1} objects
Copy the code
Export Default and Module. exports are similar
ES6 Module supports CommonJs
CommonJs is supported by ES6 Modules in various environments. How to use CommonJs in different environments
Module. exports is like export default so ES6 modules can be easily compatible with CommonJs. ES6 modules use CommonJs specifications in ES6 modules, and packaging tools vary from environment to environment
At present, we mostly use Webpack for project construction and packaging, because the front-end development environment is in the Node environment, and NPM packages are CommonJs specifications, so WebPack extends ES6 modules to support CommonJs. And support node import NPM package specification
If you use rollup, you need to download rollup-plugin-commonJS to support the Commonjs specification in ES Module. To import packages under node_modules, you need rollup-plugin-node-resolve
If you use Node, you can use ES6 in.mjs files and also support CommonJs to see nodeJS es-modules.md
CommonJs is not supported in the browser environment
Node differs from packaging tools webPack, rollup import CommonJs
// module.js
module.export.a = 1
// index.js webpack rollup
import * as a from './module'
console.log(a) // { a: 1, default: { a:1 } }
// index.mjs node
import * as a from './module'
console.log(a) // { default: { a:1 } }
Copy the code
Node uses module.exports as an export default package. Import defaultExport from ‘./foo’, Defaultexport.foo () import {foo} from ‘./foo’, foo()
Use the ES6 Module
You can get the code in the ES6Module Example repository and test it locally
Use in the browser
You need a Web server to access it. Double-click to run index.html locally does not execute the type=module tag. We can add module to the type attribute of the script tag and define two modules first
// index.js
import module from './module.js'
console.log(module) / / 123
// module.js
export default 123
Copy the code
Inline calls in HTML
<! -- index.html -->
<script type="module">
import module from './module.js'
console.log(module) / / 123
</script>
Copy the code
Referenced in HTML via script SRC
<! -- index.html -->
<script type="module" src="index.js"></script>// console 123Copy the code
Browser imports path rules
https://example.com/apples.mjs
http://example.com/apples.js
//example.com/bananas
./strawberries.mjs.cgi
../lychees
/limes.jsx
data:text/javascript,export default 'grapes';
blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f
Supplement:
- The specific file cannot be found without the suffix
- Interfaces can be modified on the back end
/getjs? name=module
That kind of thing, but the back end returnsContent-Type: application/javascript
Make sure it returnsjs
Because the browser is based onMIME type
Identification of the
Because the ES6 Module is not compatible with the browser, the compatibility table will not introduce the browser support, we generally do not use directly in the browser
Nodejs is used in
nodejs es-modules.md
ES Module is supported in Node V8.5.0 and above, requiring the.mjs extension
NOTE: DRAFT status does not mean ESM will be implemented in Node core. Instead that this is the standard, Should Node core decide to implement ESM. At which time this draft would be moved to ACCEPTED A DRAFT refers to the drafting stage.
// module.mjs
export default 123
// index.mjs
import module from './module.mjs'
console.log(module) / / 123
Copy the code
If you need to execute node — experimental-Modules index. MJS to start, it should prompt an ExperimentalWarning: The ESM Module loader is experimental. This feature is experimental (this tip does not affect execution) importing CommonJs in ES Module
// module.js
module.exports.a = 123 // module.exports is export default
// index.mjs
import module from './module.js'
console.log(module) // { a: 123 }
import * as module from './module.js'
console.log(module) // { get default: { a: 123 } }
import { default as module } from './module.js';
console.log(module) // { a: 123 }
import module from 'module'; // Import NPM package import rules are similar to require
Copy the code
Module extension.js,.mjs belongs to es Module, import form can only import file extension.mjs, module is not defined in.mjs. Module. exports, exports returns an error
The CommonJs es Module can only be imported dynamically/asynchronously using import()
// es.mjs
let foo = {name: 'foo'};
export default foo;
export let a = 1
// cjs
import('./es').then((res) = >{
console.log(res) // { get default: {name: 'foo'}, a: 1 }
});
Copy the code
Webpack used in
Webpack2 supports es Module by default, CommonJs by default, NPM package import by default
Use the rollup
Rollup focuses on ES Modules, which can be packaged as a mainstream module specification. Note the difference with WebPack. We can use Commonjs syntax in WEBPack JS, but rollup does not support it. Rollup requires plugin support, including loading the node_modules package form ‘react’
It can be seen that ES Module is not compatible with the browser and Node, and the experimental function is mostly used in the packaging tool
Tree-shaking
Tree shaking shaking is shaking the dead leaves from a Tree, Tree-shaking was first proposed by Rollup and later supported by WebPack 2. It’s all about static analysis based on the es Module feature
rollup
The following code is packaged using rollup:
// module.js
export let foo = 'foo'
export let bar = 'bar'
// index.js
import { foo } from './module'
console.log(foo) // foo
Copy the code
Running online we can modify examples and export a variety of specifications
Packing results:
let foo = 'foo';
console.log(foo); // foo
Copy the code
We can see that the rollup package is very neat and removes the unused bar support for tree-shaking the specification importing CommonJs:
// index.js
import { a } from './module'
console.log(a) / / 1
// module.js
module.exports.a = 1
module.exports.b = 2
Copy the code
Package as ES Module
var a_1 = 2;
console.log(a_1);
Copy the code
You can see that the unused B is removed
webpack
Let’s take a look at webPack support
// src/module.js
export function foo(){ return 'foo' }
export function bar(){ return 'bar' }
// src/index.js
import { foo } from './module'
console.log(foo())
Copy the code
After executing NPX webpack -p (we use webpack 4,0 configuration, -p to enable automatic compression in build mode), we did not find bar in the package file, and bar was deleted. We modified the above example:
// src/module.js
module.exports.foo = function (){ return 'foo' }
module.exports.bar = function (){ return 'bar' }
// src/index.js
import { foo } from './module'
console.log(foo())
Copy the code
Webpack does not support tree-shaking for CommonJs
pkg.module
Webpack doesn’t support Commonjs tree-shaking, but now NPM packages are Commonjs compliant. What if? If I send a new package it is the ES Module specification, but if the code is running in the Node environment, it will report an error without being packaged
There is a load on demand solution
Full path import, import specific files:
// src/index.js
import remove from 'lodash/remove'
import add from 'lodash/add'
console.log(remove(), add())
Copy the code
You can also use plugins such as babel-plugin-lodash, &lodash-webpack-plugin
But we can’t just send out a library and write our own plugins
Module-es module pkG. module {package.json} {CommonJs -> CommonJs}
Pkg. module is supported by both Webpack and rollup
Webpack can recognize our ES Module by adding the Module field, but there is still a problem with Babel
Node_modules are excluded when we use Babel, so the PKg. module is just an ES6 module that must be compiled into ES5 code because Babel won’t compile it for us. Our package must be ES5 code with the ES6 Module specification
If you use presets-env, set “presets”: [[“env”, {“modules”:false}] not to convert es module to CommonJs because it will convert our code to CommonJs
webpack
与 rollup
The difference between
webpack
Export not supportedes6 module
The specification,rollup
Supports exportes6 module
webpack
The packaged code is too redundant to look at directly,rollup
The packaged code is clean and readable, like source codewebpack
You can do code segmentation, static resource processing,HRM
.rollup
Focus on thees module
.tree-shaking
More powerful, leaner
If you are developing an application, you can use Webpack, because you can do code splitting, static resources, HRM, plug-ins. If you are developing libraries like Vue, React, etc., rollup is better, because you can make your code lean, no redundant code, faster execution, export multiple module syntax
conclusion
Commonjs and ES6 Modules, import and export syntax rules, path parsing rules, differences between them, confusion, differences in different environments, use in different environments, tree-shaking, webpack. Hopefully, after reading this article, you will have a better understanding of the modularity of the front end
Refer to the link
- ECMAScript® 2015 Language Specification sec-imports/sec-exports
- MDN import
- github nodejs lib/module
- github nodejs node-eps/002-es-modules
- nodejs docs modules
- Understanding ECMAScript 6
- ECMAScript introduction to 6
- es6-modules-final