“This is the third day of my participation in the August More Text Challenge.

As the front-end application gets more complex, we want to break up the code into modules that are easy to reuse, load on demand, and so on.

Require and import are statements that introduce modules under different module specifications, and the differences between the two approaches are described below.

The origin of

year provenance
require/exports 2009 CommonJS
import/export 2015 ECMAScript2015 (ES6)

The limit

require/exports import/export
Node.js All versions Node 9.0+ (enable with flag –experimental-modules) Node 13.2+ (enable directly)
Chrome Does not support 61 +
Firefox Does not support 60 +
Safari Does not support 10.1 +
Edge Does not support 16 +

CommonJS modular solution Require/Exports is designed for server-side development. The server module system synchronously reads the content of the module file, and obtains the module interface after compilation and execution. Node.js is an implementation of the CommonJS specification.

On the browser side, the CommonJS specification does not load properly due to its asynchronous loading of script files. So there are RequireJS, SeaJS and other modular solutions designed for browsers. It wasn’t until the ES6 specification that browsers got their own modular scheme, import/export.

The two schemes have their own limitations, and the following points need to be noted:

  • Native browsers do not support require/exports. You can use Browsersify, Webpack and other packaging tools that support the CommonJS module specification and convert require/exports into code that can be used in the browser.
  • Import /export cannot be used directly in the browser, we need to import the module’s
  • Although node.js 13.2+ can support ES6 module import/export by changing the file suffix to.mjs, node.js is not recommended for use in official environments. ES6 module systems can now be compiled to CommonJS specifications using Babel (note: the syntax is the same, but the implementation is still require/exports).

Resolve differences

Require is dynamically loaded at runtime and import is statically compiled.

  1. Require is a CommonJS syntax. The loaded module is an object (the module.exports property) that is generated only after the script has run and must look for the object property when entering.

    / / CommonJS module
    let { stat, exists, readFile } = require('fs');
    
    / / is equivalent to
    let _fs = require('fs');
    let stat = _fs.stat;
    let exists = _fs.exists;
    let readfile = _fs.readfile;
    Copy the code

    Load the fs module as a whole (that is, load all the methods of FS), generate an object “_fs”, and then read three methods from that object. This is called “runtime load”, because this object is only available at runtime and cannot be static at compile time.

  2. The ES6 import module is not an object, and its external interface is a static definition that allows the dependencies of the module, as well as the input and output variables, to be determined at compile time (the code static parsing phase).

    import { stat, exists, readFile } from 'fs';
    Copy the code

    The ES6 module is not an object, but displays the specified output code through the export command, followed by input through import.

    The stat, exists, and readFile methods are loaded from fs. The other methods are not loaded

The output difference

The import/export module exports a copy of the value. The require/exports module exports a copy of the value That is, once a value is printed, changes within the module do not affect that value.Copy the code
  1. The import/export module outputs references to values. When the JS engine statically analyzes a script, it generates a read-only reference to the module load command import. When the script is actually executed, it will be evaluated in the loaded module based on the read-only reference.

  2. If the module value referenced by the file changes, the module value introduced by require does not change, while the module value introduced by import changes.

Require related usage

const fs = require('fs')
exports.fs = fs
module.exports = fs
Copy the code

Exports is a reference to module.exports

exports = module.exports = {};
Copy the code
/ / CommonJS module
let { stat, exists, readFile } = require('fs');

/ / is equivalent to
const _fs = require('fs');
const stat = _fs.stat;
const exists = _fs.exists;
const readfile = _fs.readfile;
Copy the code

There is no difference between using exports and module.exports without changing the direction of exports; If exports points to another object, the exports change does not change the module output value. The following is an example:

//utils.js let a = 100; exports.a = 200; console.log(module.exports) //{a : 200} exports = {a:300}; //exports to other memory modules //test.js var a = require('./utils'); Console. log(a) // Prints as {a: 200}Copy the code

The import/export usage

Module consists of two commands, import and export. The export command defines the external interface of the Module, and the import command is used to input functions provided by other modules.

export

A module is a separate file, and all variables inside the file cannot be accessed externally. If you want to get a variable, you have to export it,

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
Copy the code

Or better yet: use curly braces to specify a set of variables to output

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};
Copy the code

In addition to output variables, you can also output functions or classes.

export function multiply(x, y) {
  return x * y;
};
Copy the code

You can also batch output, again enclosed in braces, or rename it with as:

function v1() {... }function v2() {... }export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};
Copy the code

Note: The export command specifies the external interface and must establish a one-to-one correspondence with the internal variables of the module

/ / write one
export var m = 1;

/ / write two
var m = 1;
export {m};

/ / writing three
var n = 1;
export {n as m};


/ / an error
export 1;

/ / an error
var m = 1;
export m;
Copy the code

The reason for writing the error is that there is no external interface provided. The first type directly outputs 1, and the second type directly outputs 1 even though there is a variable M, so it cannot be deconstructed.

Similarly, the output of function and class must be written this way.

/ / an error
function f() {}
export f;

/ / right
export function f() {};

/ / right
function f() {}
export {f};
Copy the code

The interface output by the export statement is dynamically bound to its corresponding value, that is, the real-time value obtained through this interface is inside the module.

The export module can be located anywhere in the module, but it must be at the top level of the module. If it is in another scope, an error will be reported.

function foo() {
  export default 'bar' // SyntaxError
}
foo()
Copy the code

import

After export defines the external interface of a module, other JS files can load the module through import

// main.js
import {firstName, lastName, year} from './profile';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}
Copy the code

The import command accepts a pair of curly braces specifying that the name of the variable to be imported from another module must be the same as the name of the external interface of the imported module (profile.js).

If you want to rename the imported variable, use the as keyword

import { lastName as surname } from './profile';
Copy the code

The from after import can specify the path name of the module to be imported, either absolute or relative. The.js path can be omitted. If there is only the module name but no path, you need to specify it in the configuration file.

Note that the import command is promoted to the top of the module and executed first. (Is executed at compile time)

Because import is executed statically, expressions and variables cannot be used, meaning that the syntax structure of the result is available at run time (e.g. The else…).

Module overall loading

Instead of specifying an output value to load, you can use (*) to specify an object on which all variables will be loaded.

Using *, while convenient, is detrimental to modern build tools for detecting unused functions, affecting code optimization.

/ / circle. Js. Output two functions

export function area(radius) {
  return Math.PI * radius * radius;
}

export function circumference(radius) {
  return 2 * Math.PI * radius;
}


// main.js is loaded in a module

import { area, circumference } from './circle';

console.log('Circle area:' + area(4));
console.log('Circumference:' + circumference(14));

// The loading method is specified one by one.
import * as circle from './circle';

console.log('Circle area:' + circle.area(4));
console.log('Circumference:' + circle.circumference(14));
Copy the code

Note that the object to which the whole module is loaded (circle, for example) should be parsed statically, so runtime changes are not allowed.

import * as circle from './circle';

// The following two lines are disallowed
circle.foo = 'hello';
circle.area = function () {};
Copy the code

Also be aware that

  1. {} is not required for importing modules exported by Export Default, and {} is required for importing modules exported by non-Export Default.
import fileSystem, {readFile} from 'fs'
Copy the code
  1. Only one default module can be exported per file.

  2. The following error occurred while validating the code

access to script from origin 'null' has been blocked by CORS policy
Copy the code

As mentioned earlier, browsers import module

export default

In the previous example, when using import, you need to know the name of the variable or function to be loaded in the module. Users may not want to read the source code and just want to use the interface directly, so you can use the export default command to specify the output for the module

// export-default.js
export default function () {
  console.log('foo');
}
Copy the code

The import command can specify any name for the anonymous function when the module is loaded by other modules.

// import-default.js
import customName from './export-default';
customName(); // 'foo'
Copy the code

Export default can also be used in front of non-anonymous functions.

Let’s compare the default output to the normal output.

/ / the first group
export default function crc32() { / / output
  // ...
}

import crc32 from 'crc32'; / / input

/ / the second group
export function crc32() { / / output
  // ...
};

import {crc32} from 'crc32'; / / input
Copy the code

As you can see, when export Default is used, the import statement does not use curly braces.

The import () function

Import and export commands can only be at the top level of a module, not in a code block. Otherwise, syntax errors will be reported.

Such a design can improve compiler efficiency, but there is no way to implement runtime loading.

Since require is loaded at runtime, the import command has no way of replacing require’s dynamic loading capabilities.

So the import() function was introduced. Dynamic loading is complete.

import(specifier)
Copy the code

Specifier is used to specify the location of the module to load. What arguments can import take? Import () can take the same arguments.

The import() expression loads the module and returns a promise that resolves to a module object containing all its exports.

We can use it dynamically anywhere in our code. Such as:

const main = document.querySelector('main');

import(`./section-modules/${someVariable}.js`)
  .then(module => {
    module.loadPageInto(main);
  })
  .catch(err => {
    main.textContent = err.message;
  });
Copy the code

Suggestion: Do not abuse dynamic import import() (only when necessary). Static frameworks are better at initializing dependencies, and are better for static analysis tools and tree shaking to work

The import() function applies

According to the need to load

button.addEventListener('click'.event= > {
  import('./dialogBox.js')
  .then(dialogBox= > {
    dialogBox.open();
  })
  .catch(error= > {
    /* Error handling */})});Copy the code

The mport module is loaded in the event listener function only when the user clicks the button.

Conditions of loading

Import () can be placed in if… Else statement, implement conditional loading.

Copy the code

if (condition) { import(‘moduleA’).then(…) ; } else { import(‘moduleB’).then(…) ; }

Copy the code

Cannot reassign/define

When I try to reassign the import module

import {e1} from './webUtils.js';
  e1=234;
Copy the code

Browser display

Uncaught TypeError: Assignment to constant variable.

When I redefine the referenced module

import {e1} from './webUtils.js';
 var e1=1;
Copy the code

Browser display

(index):17 Uncaught SyntaxError: Identifier ‘e1’ has already been declared

Require vs. import

Reference and Use

ES6 modules can use modules before import reference statements, whereas CommonJS requires reference before use

ES6 module

//webUtils.js
export var e='export';
console.log(e) //export
  import {e} from './webUtils.js';
  console.log(e) //export
Copy the code

CommonJS

//utils.js
exports.e = 'export';
console.log(a) 
a = require('./utils');
console.log(a)
Copy the code

Application error

ReferenceError: a is not defined

Scope difference

Import /export can only be used at the top level of the module, and cannot be referenced in code blocks such as functions and judgment statements. Can the require/exports.

   import fs from  './webUtils.js';
   function a(){
    import {e1} from './webUtils.js';
    console.log(e1)
   }
   a();
   console.log(fs())
Copy the code

Application error

Uncaught SyntaxError: Unexpected token ‘{‘

As mentioned earlier, import/export is generated during the static parsing phase of the code without parsing import/export inside the code block, so the program reports syntactic errors rather than runtime errors.

Strict mode or not

Strict mode is a way to adopt restrictive JavaScript variants

Import /export exported modules invoke strict mode by default. In ES6 modules, the top-level this refers to undefined, meaning that this should not be used in top-level code.

var fun=()=>{ mistypedVaraible = 17; MistypedVaraible is not defined}; export default fun;Copy the code

Require /exports does not use strict mode by default. You can specify whether to use strict mode. For example,

exports.fun = ()=>{ mistypedVaraible = 17; // No call to strict mode, no error};Copy the code

Reference article:

  1. Juejin. Cn/post / 684490…
  2. Juejin. Cn/post / 684490…
  3. Useful. Javascript. The info/modules – dyn…
  4. www.zhangxinxu.com/wordpress/2…
  5. zhuanlan.zhihu.com/p/75980415
  6. Juejin. Cn/post / 684490…
  7. Es6.ruanyifeng.com/#docs/modul…