Because early front-end standards did not anticipate the scale of the front-end industry today, there are many design legacies that cause problems when it comes to implementing modular front-end technologies. Although some problems are solved by some standards or tools today, it is worth thinking about an evolution process, which is the practice process of modularity in the front field. This process goes through the following four stages:

  • Stage 1 – File partitioning: This is the original modular system on the WEB, in which each function and its associated state data is stored in separate files and each file represents a module. When used, the module file is introduced into the page file, and each script tag corresponds to a module. Then reference the module’s global member directly in the code, which can be a variable or a function. The disadvantage of this approach is that all modules work in the global scope and there is no independent private space. As a result, all members of the module can be accessed or modified arbitrarily outside the module, thus polluting the global scope. And once more modules are easy to produce naming conflicts; In addition, there is no good way to manage the dependencies between modules; In general, this approach is entirely dependent on convention and fails once the project is scaled.
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Modular evolution stage 1</title>
</head>
<body>
  <h1>Modular Evolution (Stage 1)</h1>
  <h2>How to partition modules based on files</h2>
  <p>This is done by placing each function and its associated state data in a separate file, with the convention that each file is a separate module, and using a module is to introduce the module into the page, and then directly call the members of the module (variables/functions).</p>
  <p>The disadvantages are obvious: all modules work directly globally, there is no private space, all members can be accessed or modified outside the module, there are naming conflicts when there are many modules, and there is no way to manage dependencies between modules</p>
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    // Name conflict
    method1()
    // Module members can be modified
    name = 'foo'
  </script>
</body>
</html>
Copy the code
// module-a.js

var name = 'module-a'

function method1 () {
  console.log(name + '#method1')}function method2 () {
  console.log(name + '#method2')}// module-b.js

var name = 'module-b'

function method1 () {
  console.log(name + '#method1')}function method2 () {
  console.log(name + '#method2')}Copy the code
  • Stage 2 — Namespace: The second phase builds on the first phase by agreeing that each module exposes only one global object to which all module members are mounted. This is done by wrapping each module into a global object on the basis of the first phase. This is similar to adding namespaces for each module member, so we can reduce the possibility of naming conflicts. This way, however, there is still no private space, so module members can still be accessed and modified externally, and dependencies between modules are not resolved.
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Modular evolution stage 2</title>
</head>
<body>
  <h1>Modular Evolution (Phase 2)</h1>
  <h2>Each module exposes only one global object to which all module members are mounted</h2>
  <p>The idea is to build on the first phase by "wrapping" each module as a global object, somewhat like adding a "namespace" feel to the members of the module.</p>
  <p>The possibility of naming conflicts is reduced through "namespaces", but again there is no private space, all module members can be accessed or modified outside the module, and dependencies between modules cannot be managed.</p>
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    moduleA.method1()
    moduleB.method1()
    // Module members can be modified
    moduleA.name = 'foo'
  </script>
</body>
</html>
Copy the code
// module-a.js

var moduleA = {
  name: 'module-a'.method1: function () {
    console.log(this.name + '#method1')},method2: function () {
    console.log(this.name + '#method2')}}// module-b.js

var moduleB = {
  name: 'module-b'.method1: function () {
    console.log(this.name + '#method1')},method2: function () {
    console.log(this.name + '#method2')}}Copy the code
  • Stage 3 — IIFE: Provide private space for modules in the third phase by executing functions immediately. This is done by placing each module member in a private scope provided by a function. Members that need to be exposed can be mounted to global objects. This approach implements the concept of private members, which means that private members of a module can only be accessed inside the module through closures and can’t be used outside the module. This ensures the security of private members.
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Modular evolution stage 3</title>
</head>
<body>
  <h1>Modular Evolution (Stage 3)</h1>
  <h2>Use IIFE: Immediate-Invoked Function Expression to provide private space for the module</h2>
  <p>This is done by placing each module member in a private scope provided by a function, and by hanging members to global objects that need to be exposed</p>
  <p>With the concept of private members, private members can only be accessed through closures within module members.</p>
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    moduleA.method1()
    moduleB.method1()
    // Private members of the module cannot be accessed
    console.log(moduleA.name) // => undefined
  </script>
</body>
</html>
Copy the code
// module-a.js; (function () {
  var name = 'module-a'
  
  function method1 () {
    console.log(name + '#method1')}function method2 () {
    console.log(name + '#method2')}window.moduleA = {
    method1: method1,
    method2: method2
  }
})()

// module-b.js; (function () {
  var name = 'module-b'
  
  function method1 () {
    console.log(name + '#method1')}function method2 () {
    console.log(name + '#method2')}window.moduleB = {
    method1: method1,
    method2: method2
  }
})()

Copy the code
  • Stage 4 — IIFE dependency parameters: Arguments to self-executing functions can be used as dependency declarations, so that the dependencies between each module are obvious.
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Modular evolution stage 4</title>
</head>
<body>
  <h1>Modular Evolution (Stage 4)</h1>
  <h2>Use IIFE parameters as dependency declarations</h2>
  <p>This is done by passing module dependencies with the parameters of the immediately executed function, on top of the third phase.</p>
  <p>This makes the relationship between each module more obvious.</p>
  <script src="https://unpkg.com/jquery"></script>
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    moduleA.method1()
    moduleB.method1()
  </script>
</body>
</html>
Copy the code
// module-a.js; (function ($) {
  var name = 'module-a'
  
  function method1 () {
    console.log(name + '#method1'The $()'body').animate({ margin: '200px'})}function method2 () {
    console.log(name + '#method2')}window.moduleA = {
    method1: method1,
    method2: method2
  }
})(jQuery)

// module-b.js; (function () {
  var name = 'module-b'
  
  function method1 () {
    console.log(name + '#method1')}function method2 () {
    console.log(name + '#method2')}window.moduleB = {
    method1: method1,
    method2: method2
  }
})()

Copy the code

Summary: The above four stages are how early developers implemented modularity without tools and specifications. These approaches solve the modular problem of the front-end domain, which only solves the organizational problem but the modular loading problem.

  • Stage 5 — AMD & CMD: AMD and CMD are the two initial implementations of the modular specification.
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Modular evolution stage 5</title>
</head>
<body>
  <h1>The emergence of modular specifications</h1>
  <h2>Require.js provides the AMD modularization specification, as well as an automated module loader</h2>
  <script src="lib/require.js" data-main="main"></script>
</body>
</html>
Copy the code
// main.js
require.config({
  paths: {
    // Because jQuery is defined in an AMD module named jQuery
    // You must use the name 'jquery' to get the module
    Jquery.js is not necessarily in the same directory, so you need to specify a path
    jquery: './lib/jquery'}})require(['./modules/module1'].function (module1) {
  module1.start()
})
// ./modules/module1.js
// Because jQuery is defined in an AMD module named jQuery
// You must use the name 'jquery' to get the module
Jquery.js is not in the same directory, so you need to specify a path
define('module1'['jquery'.'./module2'].function ($, module2) {
  return {
    start: function () {$('body').animate({ margin: '200px' })
      module2()
    }
  }
})
// ./modules/module2.js
// Compatible with CMD specification (similar to CommonJS specification)
define(function (require.exports.module) {
	// introduce dependencies via require
  var$=require('jquery')
  Exports or module.exports exposes members
  module.exports = function () {
    console.log('module 2~'The $()'body').append('<p>module2</p>')}})Copy the code