模块化
- 将一个复杂的程序根据一定的规则封装成几个块,并组合在一起。块的内部数据/实现是私有的,只是向外部暴露一些借口与外部其他模块进行通信。
模块化的进化史
//全局函数模式:将不同的功能封装成不同的全局函数。
//Global被污染,容易命名冲突。
function foo(){
...
}
function bar(){
...
}
//Namespace模式:简单对象封装。
//减少Global上的变量数目;本质是对象,仍可以操作修改它,不安全。
let MYAPP = {
foo: function(){
...
},
bar: function(){
...
}
};
MYAPP.foo();
//IIFE模式:匿名函数自调用(闭包)。
(function(window){
let msg = 'module';
function foo(){
...
}
window.module = {
foo
};
})(window);
module.foo();
//IIFE模式增强:引入依赖。
(function(window, $){
var _private = "a";
var foo = function(){
console.log(_private);
};
window.Module = {
foo
};
})(window, jQuery);
Module.foo();//"a"
Module._private;// undefined
模块化的好处
- 避免命名冲突(减少命名空间污染)。
- 更好的分离,按需加载。
- 高复用性。
- 高可维护性。
CommonJS
模块
核心模块:由node引擎提供的模块。
核心模块的标识就是模块的名字。例如:var fs = require(‘fs’);文件模块:由用户自己创建的模块。
文件模块的标识就是文件的路径。
//服务器端
//一个模块就是一个js文件。当执行模块中的代码时,会在代码包裹在函数里,同时传递进5个参数。
/*
expots:该对象用来将变量或函数暴露到外部。是module的属性。
require:用来引入外部模块的函数。
module:当前模块本身。
__filename:当前模块的完整路径。
__dirname:当前模块所在文件夹的完整路径。
*/
function (exports, require, module, __filename, __dirname){
...
}
//暴露模块
//1、分别暴露:将需要暴露的变量或方法设置为exports的变量或方法。
exports.x = 10;
//等同于(因为exports是module的属性)
module.exports.x = 10;
//2、统一暴露
//不能写成以下(因为exports指向module.exports,如果写成下面则把exports指向另外一个对象了)
exports = {
...
}
//正确写法
module.exports = {
...
};
//引入模块
//require(filePath):使用相对路径,必须使用./或者../开头,.js可以省略。返回一个对象。
//app.js
var module = require('./module');
module.x // 10
module.foo();
包规范
- 允许我们将一组相关的模块组合在一起,形成一组完整的工具。
- 由包结构和包描述文件两个部分组成。
包结构:用于组织包中的各种文件。
包描述文件:描述包的相关信息,以供外部读取分析。
//服务器端
|- package.json 描述文件(必须;放在根目录下;json文件里不能写注释)
|- bin 可执行二进制文件
|- lib js代码
|- doc 文档
|- test 单元测试
//浏览器端
|- js
|- dist(build) 打包生成文件的目录
|- src 源码所在的目录
|- module1.js
|- module2.js
|- module3.js
|- app.js 应用主源文件
|- index.html
|- package.json
//Browserify:编译工具,通过它可以在浏览器环境下像nodejs一样使用遵守commonjs规范的模块化编程。
browserify js/src/app.js -o js/dist/bundle.js
//index.html
<script type="text/javascript" src="./js/dist/bundle"></script>
AMD
Asynchronous Module Definition异步模块定义
- 模块的加载是异步的。
//浏览器端
//下载require.js,放入项目目录中
|- js
|- libs
|- require.js
|- modules
|- module1.js
|- module2.js
|- main.js
|- index.html
//暴露模块
//1、暴露没有依赖的模块
//module1.js
define(function(){
let name = 'a';
function foo(){
...
}
return {foo};
})
//2、暴露有依赖的模块
//module2.js
define(['module1'], function(module1){
let msg = 'b';
function showmsg(){
...
}
return {showmsg};
});
//引入模块
//main.js
(function(){
requirejs.config({
baseUrl: 'js/'
paths:{
//默认不加.js
module1: './modules/module1',
module2: './modules/module2',
jquery: './libs/jquery'
}
});
requirejs(['module2'], function(module2){
module2.showmsg();
});
})();
//index.html
<script data-main="js/main.js" src="js/libs/require.js"></script>
CMD
Common Module Definition通用模块定义
- 模块的加载是异步的。
//浏览器端
//下载sea.js,放入项目目录中
|- js
|- libs
|- sea.js
|- modules
|- module1.js
|- module2.js
|- main.js
|- index.html
//暴露模块
//1、暴露没有依赖的模块
define(function(require, exports, module){
//分别暴露
exports.x = 10;
//统一暴露
module.exports = {
...
}
});
//2、暴露有依赖的模块
define(function(require, exports, module){
//引入依赖模块(同步)
var module2 = require('./module2');
//引入依赖模块(异步)
require.async('./module3',function(m3){
...
})
//分别暴露
exports.x = 10;
//统一暴露
module.exports = {
...
}
});
//引入模块
//main.js
define(function(require){
var m1 = require('./module1');
m1.show();
});
//index.html
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
seajs.use('./js/modules/main.js');
</script>
ES6
//浏览器端
|- js
|- build
|- src
|- main.js
|- module1.js
|- module2.js
|- index.html
|- .babelrc
|- package.json
|- package-lock.json
//定义package.json
//安装babel-cli(-g),babel-preset(--save-dev)
//设置.babelrc
//使用babel将ES6转换为ES5(转换后包含commonjs语法):babel js/src -d js/build
//再用Browserfy打包:browserify js/build/main.js -o js/dist/bundle.js
//index.html
<script type="text/javascript" src="./js/dist/bundle"></script>
//export命令:规定模块的对外接口。
//1、分别暴露:直接在要暴露的变量前面加export。
//m1.js
export let s = 's';
export function f(){
...
}
//2、统一暴露
//m2.js
let s = 's';
function f(){
...
}
export {s, f};
//3、默认暴露
export default{
s: 's',
f: function(){
...
}
}
//import命令:输入其他模块提供的功能。
//1、在文件里直接引入
<script type="module">
//1、通用导入
import * as m1 from './src/js/m1.js';
console.log(m1.s);
import * as m2 from './src/js/m2.js';
console.log(m2.s);
import * as m3 from './src/js/m3.js';
console.log(m3.default.s);
//2、解构赋值
import {s, f} from './src/js/m1.js';
consoel.log(s);
import {s as s_plus, f as f_plus} from './src/js/m2.js';
console.log(s_plus);
import {default as m3} from './src/js/m3.js';
console.log(m3.s);
//3、简便:针对默认暴露
import m3 from "./src/js/m3.js";
console.log(m3.s);
</script>
//2、利用标签引入:用的ES6,兼容性不佳。
<script src="./src/js/app.js" type="module"></script>
//app.js
import * as m1 from './src/js/m1.js';
console.log(m1.s);
import * as m2 from './src/js/m2.js';
console.log(m2.s);
import * as m3 from './src/js/m3.js';
console.log(m3.default.s);
//导入npm包语法
import $ from 'jquery';
//3、babel
<script src="dist/bundle.js"></script>