Modularity is not new to today’s developers, but rather familiar. But if you ask JavaScript module development process, I believe that many people are not familiar with. Let’s take a look at the history of JavaScript.

There are three steps to everything: what, why, and how.

1. What is modularity?

Modularity is a kind of specification, constraint actually. This kind of formal constraint makes our code much more observable and maintainable. This approach will greatly improve our work efficiency and reduce the time of maintenance.

2. Why modularity?

  • Our front-end code is getting bigger and bigger
  • Code is getting more and more complex
  • There is too much coupling between files

Obviously these issues will be encountered as the front-end project continues to iterate and evolve. This is where we need to use modularity to manage and constrain.

3. How to achieve modularity?

1. The Beginning

The earliest JS code we wrote was function by function down…

function foo(){
    //...
}
function bar(){
    //...
}
Copy the code

Disadvantages: Write directly in the global, variables are easy to be polluted

2. Namespace mode

The Namespace pattern encapsulates an object

var test = {
    foo: function() {},
    bar: function() {}
}

test.foo()
test.bar()
Copy the code

Although this mode reduces the number of global variables, it is still an object in the global scope, which is not safe.

Anonymous closures: IIFE mode
var Module = (function(){
    var test = "test";
    var foo = function(){
        console.log(test)
    }
    return {
        foo: foo
    }
})()

Module.foo();
Module.test; // undefined
Copy the code

Each closure is a separate file that uses closures to solve the problem of variable name duplication and environmental pollution

But closures also cause a lot of problems: memory leaks, this pointing problems, and so on…

4. Introduce dependencies
var Module = (function($){ var _$body = $("body"); // we can use jQuery now! var foo = function(){ console.log(_$body); // Revelation Pattern return {foo: foo}})(jQuery) module.foo ();Copy the code

This is the modular pattern and the cornerstone of modern modular implementations

5. Labjs-script Loader

“LABjs is a dynamic script loader designed to replace ugly, underperforming apis with flexible and performance optimized alternatives

The ability to load all JavaScript files in parallel as fast as the browser allows

$lab.script ("script1.js").script("script3.js").script("script3.js") script1Func(); script2Func(); script3Func(); });Copy the code
6, YUI3 Loader

YUI’s lightweight kernel and modular architecture make it scalable, fast, and powerful

Structure is divided into four categories: seed, core, component framework, component

Biggest feature: dynamic load on demand, fine granularity design

Add ('dom', function(Y) {y.dot = {... Use ('dom', function(Y) {y.dom.dosomething (); // use some methods DOM attach to Y })Copy the code

Create a custom module

// hello.js YUI.add('hello', function(Y){ Y.sayHello = function(msg){ Y.DOM.set(el, 'innerHTML', 'Hello! '); }} '3.0.0, {the requires: [' dom]}) / / the main js YUI (). Use (' hello', function (Y) {Y.s ayHello (" hey YUI loader "); })Copy the code

You don’t have to include script tags in a fixed order; loading and execution are separate

script(src="/path/to/yui-min.js")       // YUI seed
script(src="/path/to/my/module1.js")    // add('module1')
script(src="/path/to/my/module2.js")    // add('module2')
script(src="/path/to/my/module3.js")    // add('module3')

YUI().use('module1', 'module2', 'module3', function(Y) {
    // you can use all this module now
});
Copy the code

But there is a problem with this: Too much HTTP calls

YUI combination

Process multiple files in a single request, but requires server support (Alibaba/nginx-HTTP-concat)

Script (SRC = "http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js") Script (SRC = "http://yui.yahooapis.com/3.0.0/build/dom/dom-min.js") ⬇ ️ script (SRC = "http://yui.yahooapis.com/combo? 3.0.0 / build/yui, yui - min. Js & 3.0.0 / build/dom dom - min. / js ")Copy the code
7, CommonJS

Features:

  • All code runs with module scope and does not contaminate global scope
  • Modules can be loaded multiple times, but only run once on the first load, and then the results are cached. To run the module again, you need to clear the cache
  • Modules are loaded in the order they appear in the code

Module. exports property and exports variables

The module.exports property represents the interface that the current module exports. Other files that load the module actually read the module.exports variable. Node provides an exports variable (an object, so to speak) for each module, pointing to module.exports. Exports = module.exports var exports = module.exportsCopy the code

Module definition and reference

// math.js exports.add = function(a, b){ return a + b; } // main.js var math = require('math') // ./math in node console.log(math.add(1, 2)); / / 3Copy the code
AMD/CMD
RequireJS – AMD

The AMD specification is asynchronously loaded modules, allowing you to specify callback functions

define

define(['package/lib'],function(lib){ function foo(){ lib.log('hello world! ') } return{foo:foo} })Copy the code

Async asynchronous

//CommonJS Syntax var Employee = require("types/Employee"); function Programmer (){ //do something } Programmer.prototype = new Employee(); // If require call is asynchronous, then error // because the Employee module will not be loaded before executing this statementCopy the code

Function Wrapping

//AMD Wrapper define( ["types/Employee"], Function (Employee){function(Programmer){function(){//do something}; Programmer.prototype = new Employee(); return Programmer; //return Constructor } )Copy the code

AMD vs CommonJS

1. Similarities

It's all about modularityCopy the code

2. Differences

On the load

The AMD specification loads modules asynchronously, allowing the specified callback function CommonJS to load modules synchronouslyCopy the code

writing

// Module/1.0 var a = require("./a"); // rely on nearby a.dosomething (); var b = require("./b") b.doSomething(); // AMD recommended Style define(["a", "b"], function(a, b){AMD recommended style define(["a", "b"], function(a, b){ b.doSomething(); })Copy the code

Execution time

// Module/1.0 var a = require("./a"); // AMD with CommonJS sugar define(["require"], function(require){ Var a = require("./a")})Copy the code
SeaJS – CMD

SeaJS follows the CMD specification modularized development, relies on auto-loading, and configuration profiles are clear

Very much like CommonJS format

Seajs.use ('./main', function(main) {main.init(); seajs.use('./main'); Seajs.use (['./a', './b'], function(a, b) {a.init(); seajs.use(['./a', './b'], function(a, b) {a.init(); b.init(); } // define(function(require, exports, module) {var $= require('jquery'); var Spinning = require('./spinning'); // exports provides interface through exports. DoSomething =... Module. exports = // exports = // exports = // exports = // exports = // exports = // exports = // exports = // exports // The module is wrapped with define() and then the required dependency file (module) is introduced internally through the require() method. You can also import.css files.Copy the code

AMD vs CMD

1, AMD is early execution, CMD is delayed execution

2, CMD advocate rely on nearby, AMD advocate rely on the front

// CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // ... Var b = require('./b'); }) / / AMD define (['. / a ', '/' b], function (a, b) {/ / dependence must start write a. d. oSomething () / /... b.doSomething() ... })Copy the code

3, AMD API default is a when multiple use, CMD API strictly distinguished, praise single responsibility

9. Browserify/Webpack

1, Browserify

Browserify is a tool for compiling node-style CommonJS modules for browsers.

You can use Browserify to organize code and use third-party libraries, even if you don’t use Node itself in any other capacity than using NPM to bundle and install packages.

Browserify uses the same modular system as the node, so the packages published to NPM were originally intended to be used in the node, but not in the browser, and will work in the browser as well.

2, Webpack

A quote from Webpack’s website reads: “Webpack is a static Module bundler for modern JavaScript applications. When WebPack works on an application, it recursively builds a Dependency graph of every module the application needs, and then packages all of those modules into one or more bundles.”

A simple CLI

# make sure your directory contains webpack.config.js

# Development: debug + devtool + source-map + pathinfo
webpack main.js bundle.js -d

# Production: minimize + occurence-order
webpack main.js bundle.js -p

# Watch Mode
webpack main.js bundle.js --watch
Copy the code

Specific Webpack knowledge can be found here

10, ES6 Module

The ES6 module is designed to be as static as possible, so that the dependencies of the module, as well as the input and output variables, can be determined at compile time. Both CommonJS and AMD modules can only determine these things at runtime. For example, a CommonJS module is an object, and you have to look for object properties when you enter it. To learn more about ES6 Modules, click here

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

An ES6 module is not an object. Instead, it explicitly specifies the output code through the export command, which is then input through the import command.

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

In addition to the benefits of static loading, ES6 modules have the following benefits.

  • The UMD module format is no longer needed and the ES6 module format will be supported by both servers and browsers in the future. This is already being done through various tool libraries.
  • In the future, the browser’s new apis can be provided in module format, without the need to be global variables or properties of navigator objects.
  • Instead of requiring objects as namespaces (such as Math objects), these capabilities can be provided through modules in the future.

Strict mode

ES6 modules automatically adopt strict mode, regardless of whether you add “Use strict” to the module header.

Strict mode has the following major limitations.

  • Variables must be declared before being used
  • Function arguments cannot have attributes of the same name; otherwise, an error is reported
  • The with statement cannot be used
  • Cannot assign a value to a read-only attribute, otherwise an error is reported
  • .

The export command

The function of the module consists of two commands: export and import. The export command is used to specify the external interface of a module, and the import command is used to input functions provided by other modules.

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

So there’s another way to write export

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

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

4. Sum up and think

This article focuses on the evolution of JavaScript modules and briefly lists some small examples of modular solutions from time to time.

All the different modularity approaches are actually for

  • To make future code more iterative and maintainable
  • Solve file dependency loading problems
  • More convenient to serve our daily business development…

Now in the ES6 Module, the modularity development and specification is more and more complete than before, but it doesn’t stop there. With the development and iteration of business, modular management solutions more consistent with business development and maintenance will definitely continue to emerge.

I personally feel: there is no more perfect, only more consistent

reference

Webpack website

ES6 module

JavaScript modular seven days talk

Discussion on modular Development

Quick start seaJS – Easy to use SeaJS