模块化发展进程
一:function方式
优缺点
- 缺点:无安全性、污染Global(模块内变量声明为window属性)。
代码示例
- module1
var data = 'module_data';
//let data_ = 'module_data';
function fn(){
console.log(data);
//console.log(data_);
}
- test.html
<script type='text/javascript' src='module1.js'></script>
<script type='text/javascript'>
var data = 'window_data';
//let data_ = 'window_data';// let方式声明的与模块中的同名变量,运行报错重复声明(IDE检测不会爆红提示)。
fn();
</script>
- 运行结果
二:namespace方式
优缺点
- 优点:模块内变量声明为自定义对象属性,不污染全局变量。
- 缺点:不能私有变量、无安全性(模块外仍可以直接修改模块内变量值)。
代码示例
- module2
let obj = {
data:"module_data",
fn(){
console.log(this.data);
}
};
- test.html
<script type='text/javascript' src='module2.js'></script>
<script type='text/javascript'>
obj.fn();
obj.data = 'window_data';
obj.fn();
</script>
- 运行结果
三:IIFE模式
IIFE : immediately-invoked function expression(立即调用函数表达式)
优缺点
- 优点:可以私有变量、选择性暴露。
- 缺点:不够强大,缺少模块引用功能。
代码示例
- module3
(function(window){
let publicData = 'public_data';
let privateData = 'private_data';
function publicFn(){
console.log(privateData);
}
window.module3 ={publicData,publicFn}
})(window);
- test.html
<script type='text/javascript' src='module3.js'></script>
<script type='text/javascript'>
console.log(module3.publicData);
module3.publicFn();
</script>
- 运行结果
四;IIFE模式增强(现代模块实现的基石)
优缺点
- 优点:私有变量、选择性暴露、引用模块。
- 缺点:网页加载变慢(js文件增多导致http请求增多)、模块间的引用模糊难以维护。
代码示例
- module4
(function(window,$){
let publicData = 'public_data';
let privateData = 'private_data';
function publicFn(){
console.log(privateData);
}
function jqueryTest(){
$('body').css('background', 'red')
}
window.module4 ={publicData,publicFn,jqueryTest}
})(window,$);
- test.html
<script type='text/javascript' src='jquery-1.10.1.js'></script>
<!--被引用的模块必须保证先被引入-->
<script type='text/javascript' src='module4.js'></script>
<script type='text/javascript'>
console.log(module4.publicData);
module4.publicFn();
module4.jqueryTest();
</script>
- 运行结果
模块化规范
可以解决由于模块化而带来的js请求过多以及模块的依赖管理问题。
CommonJS
服务器端(node)
浏览器端
- 编译打包:模块需要提前编译打包处理(合并模块、转译commonJS语法)。
- src/dist:编译打包前和编译打包后的代码文件夹(代码修改后需再次编译打包);
特点
- 代码简洁:使用第三方工具一次编译打包即可,无不相关业务代码。
- 无需配置
- 修改麻烦:每次修改后都要再次编译打包
代码示例
- 目录结构
- js/src/moduleAttr.js
/**
* 函数向模块内传递的形参有require、module、exports。
* 使用exports.xxx = value向外暴露一个对象。
*/
let privateData = 'private_data';
exports.publicData = 'public_data';
exports.publicFn = function(){
console.log('attr_'+privateData);
};
- js/src/moduleObj.js
/**
* 函数向模块内传递的形参有require、module、exports。
* 使用module.exports = value向外暴露一个对象
*/
let privateData = 'private_data';
module.exports = {
publicData:'public_data',
publicFn(){
console.log('obj_'+privateData);
}
};
- js/src/moduleFun.js
/**
* 函数向模块内传递的形参有require、module、exports。
* 使用module.exports = value向外暴露一个函数
*/
let privateData = 'private_data';
module.exports = function(){
console.log('fun_'+privateData) ;
};
- js/src/index.js
// 安装开发依赖broserify
npm install browserify -g
// 使用browserify编译打包index.js入口文件
browserify js/src/index.js -o js/dist/bundle.js
let moduleAttr = require('./moduleAttr');
let moduleObj = require('./moduleObj');
let moduleFun = require('./moduleFun');
console.log(moduleAttr.publicData);
console.log(moduleAttr.privateData);
moduleAttr.publicFn();
console.log('------------------------');
console.log(moduleObj.publicData);
console.log(moduleObj.privateData);
moduleObj.publicFn();
console.log('------------------------');
moduleFun();
- index.html
<!--引入编译打包后的js文件-->
<script type="text/javascript" src="js/dist/bundle.js"></script>
- 运行结果
AMD
浏览器端
- 专门用于浏览器端,模块的加载是异步的。
特点
- 少量配置代码。
- 模块异步加载。
- 模块按需加载。
代码示例
- 目录结构
- 工具库:js/libs/require.js
- 入口/配置文件:js/main.js
(function () {
//配置
require.config({
//基本路径
baseUrl: 'js/',
//映射: 模块标识名: 路径
paths: {
//自定义模块
'module1': 'src/module1',
'module2': 'src/module2',
//库模块
'jquery': 'libs/jquery-1.10.1'
}
//配置不兼容AMD的模块
});
//引入模块使用
require(['module2'], function (module2) {
module2.module2Fn();
})
})();
-
第三方模块:js/libs/jquery.js(jQuery的实现支持AMD规范)
-
自定义无依赖模块:module1
/*
定义没有依赖的模块
*/
define(function () {
let module1_data = 'module1_data';
function module1Fn() {
return module1_data
}
return {module1Fn}
});
- 自定义有依赖模块:module2
/*
定义有依赖的模块
*/
define(['module1', 'jquery'], function (module1, $) {
let module2_data = 'module2_data';
function module2Fn() {
$('body').css('background', 'red');
console.log(module1.module1Fn() + '--' + module2_data);
}
return {module2Fn}
});
- index.html
<!--引入require.js并指定js主文件的入口-->
<script type="text/javascript" src="js/libs/require.js" data-main="js/main.js"></script>
- 运行结果
CMD
ES6
浏览器端
- ES6->ES5:使用Babel将ES6转译为ES5代码(但包含CommonJS语法)。
- ES5->Bundle:使用Browserify将转译后的ES5代码编译打包成js。
代码示例
- 目录结构
- js/src/module1.js
/**
* 分别暴露
*/
let privateData = 'module1_private_data';
export let publicData = 'module1_public_data';
export function module1Fn1(){
console.log(privateData);
}
- js/src/module2.js
/**
* 统一暴露
*/
let privateData = 'module2_private_data';
let publicData = 'module2_public_data';
function module2Fn1(){
console.log(privateData);
}
export {publicData,module2Fn1}
- js/src/module3.js
/**
* 默认暴露
*/
let privateData = 'module3_private_data';
let publicData = 'module3_public_data';
function module3Fn1(){
console.log(privateData);
}
// 可暴露一个函数/对象/值
export default{
publicData,
module3Fn1
}
- js/src/index.js
// 常规暴露使用解构赋值引入
import {publicData1,module1Fn1} from './module1';
import {publicData2,module2Fn1} from './module2';
// 默认暴露使用对象接收
import module3 from './module3';
// 引入第三方库
import $ from 'jquery';
console.log(publicData1);
console.log(publicData2);
console.log(module3.publicData);
module1Fn1();
module2Fn1();
module3.module3Fn1();
$("body").css("background","red");
- js/build文件夹
npm install babel-cli -g
npm install babel-preset-es2015 --save-dev
// 已配置好.babelrc,并确认已开发依赖babel-preset-es2015
babel js/src -d js/build
- .babelrc(json):babel-cli运行所读取的配置文件
{
"presets": ["es2015"]
}
- package.josn
{
"name": "moduleEs6Test",
"version": "1.0.0",
"devDependencies": {
"babel-preset-es2015": "^6.24.1"
},
"dependencies": {
"jquery": "^1.12.4"
}
}
- js/dist文件夹
npm install babel-cli browserify -g
// 指定入口文件和打包后的文件名。
browserify js/build/index.js -o js/dist/bundle.js
- index.html
<script type='text/javascript' src='./js/dist/bundle.js'></script>
- 运行结果