Front end modular commonjs, AMD, CMD, ES

Time:2022-1-8

CommonJs、ADM、CMD

What is a module

  • A complex program is encapsulated into several blocks (files) according to certain rules (specifications) and combined together
  • The internal data and implementation of the block are private (closure / encapsulation), but some interfaces (Methods) are exposed externally to communicate with other external modules

Modular evolution

  • Global function mode: encapsulate different functions into different global functions

    • Code: encapsulate different functions into different global functions
    • Problem: polluting the global namespace is easy to cause naming conflicts or data insecurity, and there is no direct relationship between module members (a bit like jQuery source code)
function m1(){
  //...
}
function m2(){
  //...
}    
  • Namespace pattern: simple object encapsulation

    • Function: reduces global variables and resolves naming conflicts
    • Problem: the data is not secure (external data can be directly modified inside the module)

    Variables and methods are still encapsulated in an object (class)

let myModule = {
  data: 'www.baidu.com',
  foo() {
    console.log(`foo() ${this.data}`)
  },
  bar() {
    console.log(`bar() ${this.data}`)
  }
}
myModule. Data = 'other data' // you can directly modify the data inside the module
myModule.foo() // foo() other data

Such writing will expose all module members, and the internal state can be rewritten externally.

  • Iife mode: anonymous function self call (closure)

    • Function: data is private, and external operations can only be performed through exposed methods
    • Coding: encapsulate data and behavior into a function, and expose the interface by adding attributes to the window
    • Question: what if the current module depends on another module?
// index. HTML file
<script type="text/javascript"></script>
<script type="text/javascript">
    myModule.foo()
    myModule.bar()
    console. Log (mymodule. Data) // undefined cannot access the internal data of the module
    myModule. Data = 'XXXX' // not data inside the modified module
    myModule. Foo() // no change
</script>
// module. JS file
(function(window) {
  let data = 'www.baidu.com'
  //Functions that manipulate data
  function foo() {
    //For exposed functions
    console.log(`foo() ${data}`)
  }
  function bar() {
    //For exposed functions
    console.log(`bar() ${data}`)
    Otherfun() // internal call
  }
  function otherFun() {
    //Internal private function
    console.log('otherFun()')
  }
  //Exposure behavior
  window. Mymodule = {foo, bar} // ES6 writing method
})(window)

Benefits of modularity (decoupling)

  • Avoid naming conflicts (reduce namespace pollution)
  • Better separation, loading on demand
  • Higher reusability
  • high maintainability

Introduce multiple<script>Problems after

  • Too many requests

First, we need to rely on multiple modules, which will send multiple requests, resulting in too many requests

  • Dependency ambiguity

We don’t know what their specific dependencies are, that is, it’s easy to make an error in loading order because we don’t understand their dependencies.

  • Difficult to maintain

The above two reasons lead to difficult maintenance, which may lead to serious problems in the project. Modularity certainly has many advantages, but these problems will occur when multiple JS files need to be introduced into a page. These problems can be solved through modular specifications. The following describes the most popular commonjs, AMD, ES6 and CMD specifications in development.

ES6 modularization

The design idea of ES6 module is to be static as much as possible, so that the dependencies of the module and the input and output variables can be determined at compile time. Both commonjs and AMD modules can only determine these things at run time (load on demand). For example, the commonjs module is an object. When inputting, you must find the object properties.

(1) ES6 modular syntax

The export command is used to specify the external interface of the module, and the Import command is used to input the functions provided by other modules.

/**Define module math js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };
/**Reference module**/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

As shown in the above example, when using the Import command, the user needs to know the variable name or function name to be loaded, otherwise it cannot be loaded. In order to provide convenience for users to load modules without reading documents, you need to use the export default command to specify the default output for the module

// export-default.js
export default function () {
  console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'

The module outputs by default. When other modules load the module, the Import command can specify any name for the anonymous function.

(2) Differences between ES6 module and commonjs module

There are two major differences:

① The commonjs module outputs a copy of the value, and the ES6 module outputs a reference to the value

② Commonjs module is loaded at run time, and ES6 module is the output interface at compile time

The second difference is that commonjs loads an object (that is, the module. Exports attribute), which will not be generated until the script runs. The ES6 module is not an object. Its external interface is only a static definition, which will be generated in the static code parsing stage.

The first difference is mainly explained below. Let’s take the loading mechanism of the commonjs module above as an example:

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

The running mechanism of ES6 module is different from commonjs.ES6 modules are dynamically referenced and do not cache values. Variables in the module are bound to the module in which they are located

(3) ES6 Babel browserify tutorial

In a word:Use Babel to compile ES6 into Es5 code, and use browserify to compile and package JS

① Define package JSON file
 {
   "name" : "es6-babel-browserify",
   "version" : "1.0.0"
 }
② Install Babel cli, Babel preset es2015 and browse
  • npm install babel-cli browserify -g
  • npm install babel-preset-es2015 –save-dev
  • Preset (package all plug-ins that convert ES6 to Es5)
Definition Babelrc file
  {
    "presets": ["es2015"]
  }
④ Define module code
//module1. JS file
//Separate exposure
export function foo() {
  console.log('foo() module1')
}
export function bar() {
  console.log('bar() module1')
}
//module2. JS file
//Unified exposure
function fun1() {
  console.log('fun1() module2')
}
function fun2() {
  console.log('fun2() module2')
}
export { fun1, fun2 }
//module3. JS file
//The default exposure can expose any data class item. What data is exposed is what data is received
export default () => {
  console. Log ('default exposure ')
}
// app. JS file
import { foo, bar } from './module1'
import { fun1, fun2 } from './module2'
import module3 from './module3'
foo()
bar()
fun1()
fun2()
module3()
⑤ Compile and in index HTML
  • Compile ES6 into Es5 code using Babel (but include commonjs syntax):babel js/src -d js/lib
  • Compile JS using browserify:browserify js/lib/app.js -o js/lib/bundle.js

Then in index HTML file

<script type="text/javascript"></script>

Finally, the following results are obtained:

Front end modular commonjs, AMD, CMD, ES

In addition, how to introduce a third-party library (taking jQuery as an example)? Install dependencies firstnpm install [email protected]Then in app JS file

//app. JS file
import { foo, bar } from './module1'
import { fun1, fun2 } from './module2'
import module3 from './module3'
import $ from 'jquery'

foo()
bar()
fun1()
fun2()
module3()
$('body').css('background', 'green')

Commonjs (dependency pre)

(1) Overview

The node application is composed of modules and adopts the commonjs module specification. Each file is a module with its own scope. Variables, functions and classes defined in one file are private and invisible to other files.On the server side, modules are loaded synchronously at runtime; On the browser side, the module needs to be compiled and packaged in advance. (before dependency, load all dependencies before running the program)

(2) Characteristics

  • All code runs in the module scope and does not pollute the global scope.
  • The module can be loaded multiple times, but it will only run once when it is loaded for the first time, and then the running results will be cached. When it is loaded later, the cached results will be read directly. In order for the module to run again, the cache must be cleared.
  • Modules are loaded in the order they appear in the code.

(3) Basic grammar

  • Exposed modules:module.exports = valueorexports.xxx = value
  • Lead in module:require(xxx), if it is a third-party module, XXX is the module name; If it is a custom module, XXX is the module file path

Here we have a question:What are the modules exposed by commonjs?The commonjs specification specifies that within each module, the module variable represents the current module. This variable is an object, and its exports attribute (i.e. module. Exports) is an external interface.Loading a module is actually loading the module of the module Exports property

// example.js
var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

The above code is passed through module Exports outputs variable x and function addx.

var example = require('./example.js');// If the parameter string starts with '. /', it means that a relative path is loaded
console.log(example.x); // 5
console.log(example.addX(1)); // 6

The require command loads the module file.The basic function of the require command is to read in and execute a JavaScript file, and then return the exports object of the module. If the specified module is not found, an error will be reported

(4) Module loading mechanism

The loading mechanism of commonjs module is that the input is a copy of the output value. That is, once a value is output, changes within the module will not affect the value。 This is significantly different from ES6 modularization (described below). Please see the following example:

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

The above code outputs the internal variable counter and the internal method inccounter that overwrites this variable.

// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;

console.log(counter);  // 3
incCounter();
console.log(counter); // 3

The above code shows that after counter is output, lib Changes within the JS module will not affect the counter.This is because counter is a primitive value and will be cached. Unless you write it as a function, you can get the value after internal change

(5) Server side implementation

① Download and install node js
② Create project structure

Note: use NPM init to automatically generate package JSON, package name cannot have Chinese and uppercase characters

|-modules
  |-module1.js
  |-module2.js
  |-module3.js
|-app.js
|-package.json
  {
    "name": "commonJS-node",
    "version": "1.0.0"
  }
③ Download third party modules
NPM install uniq -- save // used for array de duplication
④ Define module code
//module1.js
module.exports = {
  msg: 'module1',
  foo() {
    console.log(this.msg)
  }
}
//module2.js
module.exports = function() {
  console.log('module2')
}
//module3.js
exports.foo = function() {
  console.log('foo() module3')
}
exports.arr = [1, 2, 3, 3, 2]
// app. JS file
//The introduction of third-party libraries should be placed first
let uniq = require('uniq')
let module1 = require('./modules/module1')
let module2 = require('./modules/module2')
let module3 = require('./modules/module3')

module1.foo() //module1
module2() //module2
module3.foo() //foo() module3
console.log(uniq(module3.arr)) //[ 1, 2, 3 ]
⑤ Run app. From node js

Command line inputnode app.js, run JS file

(6) Browser side implementation (with browserify)
① Create project structure
|-js
  |-Dist // directory of package generated files
  |-Src // the directory where the source code is located
    |-module1.js
    |-module2.js
    |-module3.js
    |-app. JS // application main source file
|-index. HTML // run on the browser
|-package.json
  {
    "name": "browserify-test",
    "version": "1.0.0"
  }
② Download browse
  • Global: NPM install browse – G
  • Local: NPM install browse — save dev
③ Define module code (same as server side)

be careful:index.htmlIn order to run the file on the browser, you need to browse it with the help of browseapp.jsFile packaging and compilation, if directly inindex.htmlintroduceapp.jsWill report an error!

④ Package processing JS

Run under the root directorybrowserify js/src/app.js -o js/dist/bundle.js

⑤ Page usage import

In index HTML file<script type="text/javascript"></script>

Amd (dependent on Post)

The loading module of the commonjs specification is synchronized, that is, the following operations can be performed only after the loading is completed. Amd specification is an asynchronous loading module, which allows you to specify callback functions. Due to node JS is mainly used for server programming. Generally, module files already exist on the local hard disk, so they can be loaded quickly without considering the way of asynchronous loading, so the commonjs specification is more applicable. But,In the browser environment, if you want to load modules from the server, you must use the asynchronous mode. Therefore, the browser generally adopts amd specification。 In addition, the AMD specification was implemented earlier than the commonjs specification on the browser side.

Define exposure module:

//Define modules without dependencies
define(function(){
   Return module
})
//Define dependent modules
define(['module1', 'module2'], function(m1, m2){
   Return module
})

Introduction and use module:

require(['module1', 'module2'], function(m1, m2){
   Use M1 / m2
})
Amd specification not used and require js

By comparing the implementation methods of the two, the benefits of using amd specification are illustrated.

  • Amd specification not used
// dataService. JS file
(function (window) {
  let msg = 'www.baidu.com'
  function getMsg() {
    return msg.toUpperCase()
  }
  window.dataService = {getMsg}
})(window)
// alerter. JS file
(function (window, dataService) {
  let name = 'Tom'
  function showMsg() {
    alert(dataService.getMsg() + ', ' + name)
  }
  window.alerter = {showMsg}
})(window, dataService)
// main. JS file
(function (alerter) {
  alerter.showMsg()
})(alerter)
// index. HTML file
< div > < H1 > modular Demo 1: AMD (require. JS) is not used < / H1 > < / div >
<script type="text/javascript"></script>
<script type="text/javascript"></script>
<script type="text/javascript"></script>

Finally, the following results are obtained:

Front end modular commonjs, AMD, CMD, ES

The disadvantages of this method are obvious:Firstly, multiple requests will be sent. Secondly, the order of the imported JS files cannot be mistaken, otherwise an error will be reported!

  • Use require js

Requirejs is a tool library, which is mainly used for module management on the client side. Its module management complies with AMD specifications,The basic idea of requirejs is to define the code as a module through the define method; The module loading of code is realized through the require method。 Next, the steps of implementing amd specification in browser are introduced:

① Download require JS and introduce

  • Official website:http://www.requirejs.cn/
  • github : https://github.com/requirejs/requirejs

Then set require JS import project: JS / LIBS / require js

② Create project structure

|-js
  |-libs
    |-require.js
  |-modules
    |-alerter.js
    |-dataService.js
  |-main.js
|-index.html

③ Define require JS module code

// dataService. JS file
//Define modules without dependencies
define(function() {
  let msg = 'www.baidu.com'
  function getMsg() {
    return msg.toUpperCase()
  }
  Return {getmsg} // expose module
})
//alerter. JS file
//Define dependent modules
define(['dataService'], function(dataService) {
  let name = 'Tom'
  function showMsg() {
    alert(dataService.getMsg() + ', ' + name)
  }
  //Exposure module
  return { showMsg }
})
// main. JS file
(function() {
  require.config({
    Baseurl: 'JS /' // the starting point of the basic path is in the root directory
    paths: {
      //Mapping: module ID: Path
      alerter: './ Modules / Alert ', // it cannot be written as alert JS, an error will be reported
      dataService: './modules/dataService'
    }
  })
  require(['alerter'], function(alerter) {
    alerter.showMsg()
  })
})()
// index. HTML file
<!DOCTYPE html>
<html>
  <head>
    <title>Modular Demo</title>
  </head>
  <body>
    <!--  Introduce require JS and specify the entry of the JS main file -- >
    <script data-main="js/main"></script>
  </body>
</html>

④ The page introduces require JS module:

In index HTML import<script data-main="js/main"></script>

In addition, how to introduce third-party libraries into the project? Just make a slight modification on the basis of the above code:

// alerter. JS file
define(['dataService', 'jquery'], function(dataService, $) {
  let name = 'Tom'
  function showMsg() {
    alert(dataService.getMsg() + ', ' + name)
  }
  $('body').css('background', 'green')
  //Exposure module
  return { showMsg }
})
// main. JS file
(function() {
  require.config({
    Baseurl: 'JS /' // the starting point of the basic path is in the root directory
    paths: {
      //Custom module
      alerter: './ Modules / Alert ', // it cannot be written as alert JS, an error will be reported
      dataService: './modules/dataService',
      //Third party library module
      jquery: './ LIBS / jquery-1.10.1 '// Note: if it is written as jQuery, an error will be reported
    }
  })
  require(['alerter'], function(alerter) {
    alerter.showMsg()
  })
})()

The above example is in alert JS file, the jQuery third-party library, main JS file should also have corresponding path configuration.Summary: through the comparison between the two, it can be concluded thatThe method of AMD module definition is very clear, will not pollute the global environment, and can clearly show dependencies。 Amd mode can be used in browser environment, and allows asynchronous loading of modules, or dynamic loading of modules as needed.

CMD (asynchronous loading)

CMD specification is specially used on the browser side. Module loading is asynchronous and will be loaded and executed only when the module is used. CMD specification integrates the characteristics of commonjs and AMD specifications. At sea JS, all JavaScript modules follow the CMD module definition specification.

(1) CMD specification basic syntax

Define exposure modules:

//Define modules without dependencies
define(function(require, exports, module){
  exports.xxx = value
  module.exports = value
})
//Define dependent modules
define(function(require, exports, module){
  //Introducing dependent modules (synchronization)
  var module2 = require('./module2')
  //Introducing dependent modules (asynchronous)
    require.async('./module3', function (m3) {
    })
  //Exposure module
  exports.xxx = value
})

Introduction and use of modules:

define(function (require) {
  var m1 = require('./module1')
  var m4 = require('./module4')
  m1.show()
  m4.show()
})
(2)sea. JS simple use tutorial
① Download sea JS and introduce

Then set sea JS import project: JS / LIBS / sea js

② Create project structure
|-js
  |-libs
    |-sea.js
  |-modules
    |-module1.js
    |-module2.js
    |-module3.js
    |-module4.js
    |-main.js
|-index.html
③ Define sea JS module code
// module1. JS file
define(function (require, exports, module) {
  //Internal variable data
  var data = 'atguigu.com'
  //Internal function
  function show() {
    console.log('module1 show() ' + data)
  }
  //Outward exposure
  exports.show = show
})
// module2. JS file
define(function (require, exports, module) {
  module.exports = {
    msg: 'I Will Back'
  }
})
// module3. JS file
define(function(require, exports, module) {
  const API_KEY = 'abc123'
  exports.API_KEY = API_KEY
})
// module4. JS file
define(function (require, exports, module) {
  //Introducing dependent modules (synchronization)
  var module2 = require('./module2')
  function show() {
    console.log('module4 show() ' + module2.msg)
  }
  exports.show = show
  //Introducing dependent modules (asynchronous)
  require.async('./module3', function (m3) {
    console. Log ('asynchronously import dependent module 3 '+ m3. Api_key)
  })
})
// main. JS file
define(function (require) {
  var m1 = require('./module1')
  var m4 = require('./module4')
  m1.show()
  m4.show()
})
④ In index HTML
<script type="text/javascript"></script>
<script type="text/javascript">
  seajs.use('./js/modules/main')
</script>

The final results are as follows:

Front end modular commonjs, AMD, CMD, ES

3、 Summary

Commonjs specification is mainly used for server-side programming. The loading module is synchronous, which is not suitable for the browser environment, because synchronization means blocking loading, and browser resources are loaded asynchronously. Therefore, amd CMD solution is available.

Amd specification loads modules asynchronously in the browser environment, and multiple modules can be loaded in parallel. However, the development cost of AMD specification is high, the reading and writing of code is difficult, and the semantics of module definition is not smooth.

The CMD specification is very similar to the AMD specification. Both are used for browser programming. They rely on proximity and delay execution. They can be easily implemented in node JS. However, depending on SPM packaging, the module loading logic is biased

At the level of language standard, ES6 realizes module functions, and the implementation is quite simple. It can completely replace commonjs and AMD specifications and become a general module solution for browsers and servers

Recommended Today

Proper memory alignment in go language

problem type Part1 struct { a bool b int32 c int8 d int64 e byte } Before we start, I want you to calculatePart1What is the total occupancy size? func main() { fmt.Printf(“bool size: %d\n”, unsafe.Sizeof(bool(true))) fmt.Printf(“int32 size: %d\n”, unsafe.Sizeof(int32(0))) fmt.Printf(“int8 size: %d\n”, unsafe.Sizeof(int8(0))) fmt.Printf(“int64 size: %d\n”, unsafe.Sizeof(int64(0))) fmt.Printf(“byte size: %d\n”, unsafe.Sizeof(byte(0))) fmt.Printf(“string size: %d\n”, […]