概述
将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进行组合在一起。块的内部数据、实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块通信。
JS模块化的历史解决方案
- 使用命名空间(即定义在某个对象中)
var obj = {
module:'module',
test:function(){
console.log('call test function',module);
}
}
- IIFE立即执行函数 (函数式javascript唯一的localScope)。
(function(window){
let module = "module";
function test(){
console.log('call test function',module);
}
window.JQueue = {
test
}
})(Window)
- IIFE立即执行函数的增强版,引入依赖
引入jQuery依赖
(function (window, $) {
console.dir('$', $);
let module = "module";
function test() {
console.log('call test function', module);
}
window.JQueue = {
test
}
$('body').css('background', 'red')
})(Window, jQuery);
模块化的好处
- 避免命名冲突
- 更好的分离,按需加载
- 更高的复用性
- 高可维护性
一个页面需要引入多个js文件的问题
- 请求过多。
- 依赖关系模糊,我们不知道他们具体的依赖关系是什么,而且如果需要根据依赖关系来确定引入的先后顺序。
- 难以维护,由于上述原因,很可能出现牵一发而动全身的情况导致项目出现严重的问题。
所以出现了模块化的几种规范来解决上述问题。
模块化的几种规范
1. CommonJS
说明
- 每一个文件都是一个模块。
- 在服务器端:模块的加载是运行时同步加载的。
- 在浏览器端: 模块需要提前编译打包处理。(浏览器识别不了require等方法)。
基本语法
1.暴露模块
module.exports = value
exports.xxx = value
2.引入模块
require(xxx);
//xxx
//1. 第三方模块:xxx为模块名
//2. 自定义模块:xxx为模块文件路径
实现
- 服务器实现 nodejs
module1.js
module.exports = {
message:'hello world',
test(){
console.log('call module1 test function');
}
}
module2.js
module.exports = function(){
console.log('call module2 function');
}
module3.js
exports.test = function(){
console.log('test() module2');
}
exports.getMessage = function(){
console.log('call module2 getMessage function');
}
exports.arr = [1,2,3,4,5,6,7]
app.js
var module1 = require('./module1.js');
var module2 = require('./module2.js');
var module3 = require('./module3.js');
console.log('module1', module1);
console.log('module2', module2);
console.log('module3', module3);
//输出结果
//module1 { message: 'hello world', test: [Function: test] }
//module2 function(){
// console.log('call module2 function');
//}
//module3 { test: [Function],
// getMessage: [Function],
// arr: [ 1, 2, 3, 4, 5, 6, 7 ]
//}
- 浏览器端实现 Browserify(也被称为CommonJS的浏览器端的打包工 browserify官网
2. AMD
说明
专门用于浏览器端,模块的加载是异步的。
基本语法
1. 暴露模块
//无依赖
define(function () {
let name = "module1";
function getName() {
return name;
}
return {
getName
}
})
//有依赖
define(['module1'], function (module1) {
let msg = 'module2';
function showMsg() {
console.log(msg, module1.getName());
}
return {
showMsg
}
})
2. 导入模块
(function(){
requirejs.config({
paths:{
module1: './module1', //不需要加后缀名
module2: './module2' //不需要加后缀名
}
})
requirejs(['module2'],function(module2){
module2.showMsg();
})
})();
浏览器端实现
3. CMD
一个阿里人写的,使用的不太广泛。据说现在卖给老外了。这里不详细介绍了。
浏览器端实现
seajs
官网已经找不到。
4. ES6
说明
依赖模块需要编译打包处理。(因为es6中有部分语法,浏览器还不支持,所以需要转成es5语法)
基础语法
导出模块
- 批量导出
export const message = "hello world";
export const data = [1,2,3,4,5];
export const obj = {
key:1,
value: 'obj'
}
export function getName(){
console.log('module1: My Name is wangyanfei');
}
2.具名导出
function getName(){
console.log('module2: this is module2.js');
}
function getLocation(){
console.log('module2: call getLocation function');
}
export {
getName,
getLocation
}
3.默认导出
export default function getName(){
console.log('module3: call getName function');
}
引入模块
1.具名引入
import {message,getName} from './module1.js';
import {getName as getN} from './module2.js';
- 批量导入
import module3 from './module3.js';
浏览器端实现
使用babel(Babel 中文网 · Babel - 下一代 JavaScript 语法的编译器 (babeljs.cn)
.babelrc
是babel运行时的配置文件。rc是run command的缩写。
因为有的浏览器无法识别 import 和 export 语法,所以要对js进行编译成浏览器器可以识别的es5语法。可以按照下面2步执行:
- 使用babel将es6编译为es5代码(包含CommonJS语法)
- 使用 browserify 编译成浏览器可以识别的js语法。