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