JS Modularization
Modular understanding
- What is a module?
- Encapsulate a complex program into several blocks (files) according to certain rules (specifications) and combine them together.
- The internal data/implementation of the block is private, but exposes some interfaces (methods) to the outside to communicate with other external modules;
- Composition of a module
- Data - > Internal attributes;
- Behavior of manipulating data - > Internal functions;
- Modularization refers to the process of dividing a system into modules from top to bottom when solving a complex problem, with multiple attributes reflecting its internal characteristics.
- Modular encoding: When encoding, each is encoded according to the module, and the whole project is a modular project;
Non-modular issues
- Problems with loading multiple js on a page:
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript" src="module3.js"></script>
<script type="text/javascript" src="module4.js"></script>
- A problem occurred:
- Difficult to maintain;
- Dependent on ambiguity;
- Too many requests;
- Therefore, these problems can be solved by modern modular coding and project building;
Advantages of modularization
-
1. Better separation;
- Avoid placing multiple script tags on a single page, and simply load the entire module you need, which is good for separating HTML from JavaScript.
-
2. Better code organization;
- Favor better maintenance code later;
-
3. Load on demand
- Improve usage performance, download speed, and load required modules on demand
-
4. Avoid naming conflicts
- JavaScript itself has no namespace, and there are often naming conflicts. Modularization allows any form of naming within a module to no longer conflict with other modules.
-
5. Better dependency handling
- With modularization, you only need to declare the dependencies within the module, add and delete to modify the module directly, and call it without regard to which other modules the module depends on.
The Development of Modularization
Original Writing
- Simply put different functions together, even a module;
function fun1(){
//...
}
function fun2(){
//...
}
//The functions fun1 and fun2 above make up a module. When using them, call a function directly.
- Disadvantages:
- "Pollution" is a global variable and there is no guarantee that variable names will not conflict with other modules;
- There is no direct relationship between module members.
Object Writing
- To solve the problem of polluting global variables, a module can be written as an object in which all module members are placed.
var module1 = new Object({
count : 0,
fun1 : function (){
//...
},
fun2 : function (){
//...
}
});
//This inside fun1 and fun2 are encapsulated in a gambler Ning, which can be invoked in the form of object.method;
module1.fun1();
- Advantage:
- Reduces the number of variables globally;
- Disadvantages:
- The object is essentially an object that exposes all module members and the internal state can be overridden externally.
Execute Function Now (IIFE)
- Avoid exposing private members, so use immediate execution functions (auto-tuning functions, IIFE);
- Role: Data is private and external can only be manipulated by exposure
var module1 = (function(){
var count = 0;
var fun1 = function(){
//...
}
var fun2 = function(){
//...
}
//Place the content you want to expose in an object byreturnReturn to global scope.
return{
fun1:fun1,
fun2:fun2
}
})()
//This way, fun1 and fun2 can only be read in the global scope, but the variable count cannot be read or modified.
//Question: What if this module currently depends on another module?
Enhancement of IIFE (introducing dependencies)
- If a module is large, it must be divided into several parts, or if one module needs to inherit another, then it is necessary to use Enhanced Mode.
- IIFE mode enhancements: introducing dependencies;
- This is the cornerstone of modern module implementation;
var module1 = (function (mod){
mod.fun3 = function () {
//...
};
return mod;
})(module1);
//A new method, fun3(), was added to the module 1, and the new module 1 was returned.
//Introducing jquery into the project;
var Module = (function($){
var _$body = $("body"); // we can use jQuery now!
var foo = function(){
console.log(_$body); // privileged method
}
// Revelation Pattern
return {
foo: foo
}
})(jQuery)
Module.foo();
js modularization needs to address those issues:
- 1. How can I safely package the code of a module?(No code outside the module is polluted)
- 2. How do I uniquely identify a module?
- 3. How to elegantly leak out the API of the module?(Global variables cannot be added)
- 4. How to use the dependent modules conveniently?
Modular specification
CommonJS
- Node.js: Server Side
- Browserify: Browser-side packaging tool also known as js
- Basic grammar:
- Define Exposure Module: exports
exports.xxx = value
module.exports = value
- Introduction module: require
var module = require('module name/module relative path')
- Define Exposure Module: exports
- When did the introduction of the module happen?
- Node: Dynamic synchronization is introduced at runtime;
- Browserify: Processing the compilation/translation/packaging of a module before running (a dependent module has already been included), running a js generated by packaging, and running without the need to remotely introduce a dependent module;
- Basic grammar:
AMD: Browser side
- require.js
- Basic Grammar
- Define exposed modules: define([dependent module name], function(){return module object})
- Introducing modules: require(['Module 1','Module 2','Module 3'], function(m1, m2) {//Use module object})
- To configure:
- Basic Grammar
require.config({
//Basic Path
baseUrl : 'js/',
//Identification name-path mapping
paths : {
'Module 1' : 'modules/Module 1',
'Module 2' : 'modules/Module 2',
'angular' : 'libs/angular',
'angular-messages' : 'libs/angular-messages'
},
//wrongAMDModule
shim : {
'angular' : {
exports : 'angular'
},
'angular-messages' : {
exports : 'angular-messages',
deps : ['angular']
}
}
})
CMD: Browser side
- sea.js
- Basic Grammar
- Define Exposure Modules:
define(function(require, module, exports){
//Introduce dependent modules through require
//Expose modules through module/exports
exports.xxx = value
})
- Using module seajs.use(['Module 1','Module 2'])
- Define Exposure Modules:
- Basic Grammar
- ES6
- ES6 has a built-in modular implementation
- Basic Grammar
- Define Exposure Module: export
- Expose an object:
- Default exposure, expose any data type, expose what data type, receive what data type
export default object
- Exposing multiple:
- General exposure, which is essentially an object, receives values only in the form of a deconstructive assignment of the object
export var xxx = value1
export let yyy = value2
//
var xxx = value1
let yyy = value2
export {xxx, yyy}
- Introduce usage module: import
- default module:
Import xxx from'module path/module name'
- Other modules
import {xxx, yyy} from'module path/module name'
Import * as module 1 from'module path/module name'
- default module:
- Define Exposure Module: export
- Problem: All browsers do not recognize ES6 modular syntax directly yet
- Solve:
- Using Babel to ES6 ->ES5 (using CommonJS) - browsers cannot execute directly yet;
- Use Browserify - > Packaging - - Browser can run
Modularization scheme | Advantage | shortcoming |
---|---|---|
commonJS | Strong reusability; Easy to use; Simple implementation; |
There are many ready-to-use modules, which are ecologically good. Synchronous loading is not suitable for browsers, browser requests are asynchronous loading; Multiple modules cannot be loaded in parallel. |
AMD | Asynchronous loading for browsers | Multiple modules can be loaded in parallel; Module definition is not elegant and does not conform to standard modularity |
ES6 | Static analysis, compilation ahead of time | Standards for the future; Browsers are typically compiled to ES5 because of poor native compatibility; There are few ready-to-use modules and the ecology is poor. |
CommonJS Universal Module Specification (Synchronization)
- The Modules/1.0 specification contains:
- 1. Rules to follow for module identification (Writing Specification)
- 2. Define the global function require, introduce other modules by passing in the module identity, and the result of execution is the API exposed by the other modules;
- 3. If the modules introduced by the require function also contain dependencies, load them in turn;
- 4. If the introduction of a module fails, the require function should report an exception;
- 5. Module exposes API through variable exports, exports can only be an object, and the exposed API must be a property of this object.
commonJS Modular Tutorial in nodejs
- 1. Install node.js;
-
2. Create project structure
//The structure is as follows |-modules |-module1.js//Module 1 to be introduced |-module2.js//Module 2 to be introduced |-module3.js//Module 3 to be introduced |-app.js//Main module |-package.json { "name": "commonjsnode", "version": "1.0.0" }
-
3. Download third-party module: uniq
npm i uniq --save
4. Modular encoding
//module1 Usemodule.exports = value Expose an object
module.exports = {
foo(){
console.log('module1 foo()');
}
}
//module2 Usemodule.exports = value Exposing a function to the outside
module.exports = function () {
console.log('module2()');
}
//module3 Useexports.xxx = value Expose an object
exports.foo = function () {
console.log('module3 foo()');
} ;
exports.bar = function () {
console.log('module3 bar()');
}
//app.js
/*
1. Define Exposure Modules:
module.exports = value;
exports.xxx = value;
2. Introducing modules:
var module = require(Module name or module path;
*/
var uniq = require('uniq');
//Reference Module
let module1 = require('./modules/module1');
let module2 = require('./modules/module2');
let module3 = require('./modules/module3');
//Using modules
module1.foo();
module2();
module3.foo();
module3.bar();
- 5. Run app.js through node
- Command: node.app.js
- Tools: Right-click -> Run
commonJS specification in browsers
- Browserify Modular Use Tutorial
- 1. Create project structure
|-js
|-dist //Directory to package generated files
|-src //Directory where the source is located
|-module1.js
|-module2.js
|-module3.js
|-app.js //Apply Primary Source File
|-index.html
|-package.json
{
"name": "browserify-test",
"version": "1.0.0"
}
- 2.download browserify - Overall situation: npm install browserify -g - local: npm install browserify --save-dev - 3.Define module code - module1.js ``` module.exports = { foo() { console.log('moudle1 foo()') } } ``` - module2.js ``` module.exports = function () { console.log('module2()') } ``` - module3.js ``` exports.foo = function () { console.log('module3 foo()') } exports.bar = function () { console.log('module3 bar()') } ``` - app.js (Application Master js) ``` //Reference Module let module1 = require('./module1') let module2 = require('./module2') let module3 = require('./module3') let uniq = require('uniq') //Using modules module1.foo() module2() module3.foo() module3.bar() console.log(uniq([1, 3, 1, 4, 3])) ``` - Packaging Processing js: - `browserify js/src/app.js -o js/dist/bundle.js` - Page usage introduction:
```
<script type="text/javascript" src="js/dist/bundle.js"></script>
```
.AMD: Asynchronous Module Definition Specification (Preloaded)
- AMD is short for Asynchronous Module Definition, which means Asynchronous Module Definition.
- It loads modules asynchronously, and the loading of modules does not affect the operation of the statements that follow it.All statements that depend on this module are defined in a callback function that will not run until the load is complete.
-
AMD also loads modules using the require() statement, but unlike CommonJS, it requires two parameters:
require([module], callback);
- The first parameter, [module], is an array whose members are the modules to be loaded.
- The second parameter, callback, is a callback function after successful loading.
Currently, there are two main Javascript libraries that implement the AMD specification: require.js and curl.js.
Introduction to require.js
- Advantage
- Implement asynchronous loading of js files to prevent web pages from losing responsiveness;
- Manage dependencies between modules to facilitate code writing and maintenance.
Request.js Use Tutorial
- 1. Download require.js and introduce
- Official website: http://www.requirejs.cn/
- github : https://github.com/requirejs/requirejs
- Import require.js into project: js/libs/require.js
- 2. Create project structure
|-js
|-libs
|-require.js
|-modules
|-alerter.js
|-dataService.js
|-main.js
|-index.html
-
3. Define the module code for require.js
- require.js loaded modules, using the AMD specification.That is, modules must be written in accordance with AMD.
-
Specifically, modules must be defined with a specific define() function;
-
If a module does not depend on other modules, it can be defined directly in the define() function.
define(['myLib'], function(myLib){ function foo(){ myLib.doSomething(); } return {foo : foo}; }); //When the require() function loads the module above, the myLib.js file is loaded first.
If the module also depends on other modules, the first parameter of the define() function must be an array indicating the dependency of the module;
-
-
dataService.js
define(function () { let msg = 'atguigu.com' function getMsg() { return msg.toUpperCase() } return {getMsg} })
-
alerter.js
define(['dataService', 'jquery'], function (dataService, $) { let name = 'Tom2' function showMsg() { $('body').css('background', 'gray') alert(dataService.getMsg() + ', ' + name) } return {showMsg} })
-
4. Apply Master (Entry) js: main.js
- Using the require.config() method, we can customize the loading behavior of modules.Request.config() is written at the head of the main module (main.js).A parameter is an object whose paths property specifies the loading path for each module.
(function () {
//To configure
require.config({
//Basic Path
baseUrl: "js/",
//Module Identity Name and Path Mapping
paths: {
"alerter": "modules/alerter",
"dataService": "modules/dataService",
}
})
//Introducing usage modules
require( ['alerter'], function(alerter) {
alerter.showMsg()
})
})()
- 5. Page usage module:
<script data-main="js/main" src="js/libs/require.js"></script>
-
6. Use a third-party require.js-based framework (jquery)
-
Import the jquery library file into the project:
js/libs/jquery-1.10.1.js
-
Configuring jquery paths in main.js
paths: { 'jquery': 'libs/jquery-1.10.1' }
-
Using jquery in alerter.js
define(['dataService', 'jquery'], function (dataService, \$) { var name = 'xfzhang' function showMsg() { $('body').css({background : 'red'}) alert(name + ' '+dataService.getMsg()) } return {showMsg} })
- Use third-party frameworks that are not based on require.js (angular)
- Import angular.js into the project
- js/libs/angular.js
- Popular function libraries, such as jQuery, conform to the AMD specification; more do not.Before such modules can be loaded with require(), define some of their characteristics using the require.config() method.
- require.config() accepts a configuration object that, in addition to the paths property mentioned earlier, has a shim property specifically designed to configure incompatible modules.
- Specifically, each module defines (1) the exports value (the variable name of the output), which indicates the name of the module when called externally, and (2) the deps array, which indicates the dependency of the module.
- Configuring in main.js
(function () { //To configure require.config({ //Basic Path baseUrl: "js/", //Module Identity Name and Path Mapping paths: { //Third-party libraries as modules 'jquery' : './libs/jquery-1.10.1', 'angular' : './libs/angular', //Custom Module "alerter": "./modules/alerter", "dataService": "./modules/dataService" }, /* Configure AMD-incompatible modules exports : Specify the module object corresponding to the corresponding module name */ shim: { 'angular' : { exports : 'angular' } } }) //Introducing usage modules require( ['alerter', 'angular'], function(alerter, angular) { alerter.showMsg() console.log(angular); }) })()
-
CMD
Simple use tutorial for sea.js
- Download sea.js and introduce
- Official website: http://seajs.org/
- github : https://github.com/seajs/seajs
- Import sea.js into project: js/libs/sea.js
- Download sea.js and introduce
- Create project structure
|-js
|-libs
|-sea.js
|-modules
|-module1.js
|-module2.js
|-module3.js
|-module4.js
|-main.js
|-index.html
-
Module code that defines sea.js
-
module1.js
define(function (require, exports, module) { //Internal variable data var data = 'atguigu.com' //Internal function function show() { console.log('module1 show() ' + data) } //Exposure exports.show = show })
-
module2.js
define(function (require, exports, module) { module.exports = { msg: 'I Will Back' } })
-
module3.js
define(function (require, exports, module) { const API_KEY = 'abc123' exports.API_KEY = API_KEY })
-
module4.js
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('Asynchronous introduction of dependent module 3 ' + m3.API_KEY) }) })
-
main.js: main (entry) module
define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show() })
-
- index.html:
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./js/modules/main')
</script>
ES6-Babel-Browserify
Using tutorials
- 1. Define the package.json file
{
"name" : "es6-babel-browserify",
"version" : "1.0.0"
}
- 2. Install babel-cli, babel-preset-es2015 and browserify
* npm install babel-cli browserify -g
* npm install babel-preset-es2015 --save-dev
- 3. Definition. babelrc file
{
"presets": ["es2015"]
}
-
4. Encoding
-
js/src/module1.js
export function foo() { console.log('module1 foo()'); }; export let bar = function () { console.log('module1 bar()'); }; export const DATA_ARR = [1, 3, 5, 1];
-
js/src/module2.js
let data = 'module2 data'; function fun1() { console.log('module2 fun1() ' + data); }; function fun2() { console.log('module2 fun2() ' + data); }; export {fun1, fun2};
-
js/src/module3.js
export default { name: 'Tom', setName: function (name) { this.name = name } }
-
js/src/app.js
import {foo, bar} from './module1' import {DATA_ARR} from './module1' import {fun1, fun2} from './module2' import person from './module3' import $ from 'jquery' //Introduction Completed $('body').css('background', 'red') foo() bar() console.log(DATA_ARR); fun1() fun2() person.setName('JACK') console.log(person.name);
-
-
5. Compile
- Compile ES6 into ES5 code (but with CommonJS syntax) using Babel: Babel js/src-d js/lib
- Compile JS using Browserify: browserify js/lib/app.js-o js/lib/bundle.js
- 6. Introducing tests into pages
<script type="text/javascript" src="js/lib/bundle.js"></script>
-
7. Introducing third-party modules (jQuery)
- 1). Download the jQuery module:
npm install jquery@1 --save
- 2). Introduce and use in app.js
import $ from 'jquery'
$('body').css('background', 'red')