文章目录

  • 一、模块化
  • 1、模块化的最初
  • 2、模块化解决什么问题
  • 3、IIFE立即执行函数
  • 4、插件化
  • 5、JS引擎遇到script标签做了什么
  • 6、NodeJS带来的模块化体验
  • CommonJS特点
  • require
  • 使用
  • 7、客户端的CommonJS(AMD)
  • 8、通用模块定义Common Module Definition(CMD)
  • 9、ES官方推出:ES6模块化
  • 10、CommonJS跟ES6模块化的区别


一、模块化

1、模块化的最初

  1. IE6之前,是没有JS引擎的
  2. JS代码都是写在script标签中
  3. 随着JavaScript的发展,慢慢的就将JS通过外部脚本引入

2、模块化解决什么问题

  • 将JavaScript架构跟逻辑分离开
  1. 抽离公共JS模块,然后引入
  2. 不能光通过以页面为基础,来分程序块,分JS文件

模块化解决的问题:

  1. 加载顺序
  2. 污染全局 -> 数据类型的变量

3、IIFE立即执行函数

解决变量污染全局跟依赖的问题,不会解决加载顺序的问题

  • 立即执行函数 -> 闭包 -> 模块的注入

约定俗成在立即执行函数前面加上分号

  • 函数有自己的作用域和执行期上下文,所以我们可以通过立即执行函数创建模块的独立作用域

一个模块一般都是将对象的方式抛出去,他才是可拓展的

var moduleA = (function() {
	var a = [1, 2, 3];

	return { // 这里形成了闭包,函数被销毁,但是该作用域还存在
		a: a
	}
})();


// 模块注入:模块独立,并且可以相互依赖
var moduleB = (function(moduleA) {
	var b = moduleA.a.concat([4, 5, 6]);

	return {
		b: b
	}
})(moduleA);


// 使用
(function (moduleA, moduleB) {
	console.log(moduleA.a);
	console.log(moduleB.b);
})(moduleA, moduleB);

4、插件化

插件化是由开发者通过一遍又一遍的试验来定义的:立即执行函数实现插件化

  • 函数声明不是表达式,只要不是表达式就不能加执行符号()
  • 只要是表达式就可以在后面加执行符号(),被括起来就是表达式

将架构和逻辑分离开来,是最简单的模块化方式

5、JS引擎遇到script标签做了什么

会阻塞,遇到第一个script时,后面的script不会执行,要等待第一个script加载完才执行

  • 多个script标签会产生变量覆盖问题,变量重名问题,会污染全局

6、NodeJS带来的模块化体验

NodeJS诞生带来了前所未有的模块化体验:require导入 module.exports 导出

CommonJS特点
  • 是一种模块化规范
  • 用的require引用,只要一引用就会创建一个模块实例
  • 有缓存机制,在Node上运行,依赖webpack解析,是同步方法
  • 写服务端用CommonJS比较多
require
  • require并不是全局变量,只要引进来之后就会被解析成一个立即执行函数
(function(exports, require, module, __filename, __dirname) {

})();
使用
/** module_a.js */ 
var a = (function() {
	return [1, 2, 3].reverse();
})();

module.exports = { // 导出
	a: a
}

/** module_b.js */ 
var moduleA = require('./module_a'); // 引入模块
var b = (function() {
	return moduleA.a.concat([4, 5, 6]);
})();

module.exports = { // 导出
	b: b
}

/** module_c.js */ 
var moduleB = require('./module_b'); 
var c = (function() {
	return moduleB.b.join('-');
})();

module.exports = {
	c: c
}


/** index.js */ 
var moduleA = require('./module_a');
var moduleB = require('./module_b');
var moduleC = require('./module_c');

console.log(moduleA.a);
console.log(moduleB.b);
console.log(moduleC.c);

7、客户端的CommonJS(AMD)

通过requireJS实现AMD

  • AMD:Asynchronous Moudle Definition 异步模块定义
  • define定义模块 require使用模块

阿里为模块化做了贡献:CMD,Common Module Definition 通用模块定义

  • 特点
  1. 实现了异步加载模块
  2. require的时候,所有模块加载完毕,回调函数才会执行 -> 前置依赖
  3. 规范了模块的输入输出
  1. 定义模块
define(moduleName, ['module'], factory);
  1. 引入模块
require(['module'], callback);
  1. 使用
<script src="./require.js"></script>
<script src="./index.js"></script>
// ---------- module_a.js ----------
define('moduleA', function() {
    var a = [1, 2, 3, 4, 5];
    return {
        a: a.reverse();
    }
})

// ---------- module_b.js ----------
define('moduleB', ['moduleA'], function(moduleA) {
    var b = [6, 7, 8, 9, 10];
    return {
        b: moduleA.a.concat(b);
    }
})

// ---------- module_c.js ----------
define('moduleC', ['moduleB'], function(moduleB) {
    return {
        c: moduleB.b.join('-')
    }
})

// ---------- index.js ----------
// 需要配置路径
require.config({
    paths: {
        moduleA: 'js/module_a',
        moduleB: 'js/module_b',
        moduleC: 'js/module_c',
    }
})
require(['moduleA', 'moduleB', 'moduleC'], function(moduleA, moduleB, moduleC) {
    console.log(moduleA.a);
    console.log(moduleB.b);
    console.log(moduleC.c);
})

8、通用模块定义Common Module Definition(CMD)

  • 特点:
  1. CMD 通过 require 加载, defined 定义, exports 导出, module 操作模块
  2. 使用模块是需要配置模块URL
  3. 依赖加载完毕后执行factory
  4. 依赖就近 按需加载
  • 这是和CommonJS AMD本质的不同
  1. AMD:依赖前置,全部都加载完成,才执行回调
  2. CMD:需要的时候在加载,依赖就近,按需加载
// 定义模块
define(function(require, exports, module) {});

// 使用模块
seajs.use([module路径], function(moduleA, moduleB, moduleC) {})
  1. 使用
<!-- 引入sea.js -->
<script src="./sea.js"></script>
<script src="./index.js"></script>
// ---------- module_a.js ----------
define(function(require, exports, module) {
    var a = [1, 2, 3, 4, 5];
    return {
        a: a.reverse();
    }
})

// ---------- module_b.js ----------
define(function(require, exports, module) {
    var moduleA = require('module_a'),
        b = [6, 7, 8, 9, 10];
    return {
        b: moduleA.a.concat(b);
    }
})

// ---------- module_c.js ----------
define(function(require, exports, module) {
    var moduleB = require('module_b');
    return {
        c: moduleB.b.join('-')
    }
})

// ---------- index.js ----------
seajs.use(['module_a.js', 'module_b.js', 'module_c.js'], function(moduleA, moduleB, moduleC) {
    console.log(moduleA.a);
    console.log(moduleB.b);
    console.log(moduleC.c);
})

9、ES官方推出:ES6模块化

// 导入模块
import module from '模块路径';
//导出模块
export module;
  1. 使用
// ---------- module_a.js ----------
export default {
    a: [1, 2, 3, 4].reverse()
};

// ---------- module_b.js ----------
// b.js 里引入模块
import moduleA from './module_a'; 
export default {
    b: moduleA.a.concat([6, 7, 8, 9])
}

// ---------- module_c.js ----------
import moduleB from './module_b';
export default {
    c: moduleB.b.join('-')
}

// ---------- index.js ----------
import moduleA from './module_a',
import moduleB from './module_b',
import moduleC from './module_c';
console.log(moduleA.a);
console.log(moduleB.b);
console.log(moduleC.c);

10、CommonJS跟ES6模块化的区别

  1. CommonJS模块输出的是一个值的拷贝,ES6模块化输出的是值的引用
  2. CommonJS模块是在运行时加载,ES6模块是在编译时加载
// ---------- export.js ----------
exports.a = 0;
setTimeout(() => {
    console.log('exportJS', ++exports.a); // 1
}, 300)

// ---------- common.js ----------
const {
    a
} = require('./export');
setTimeout(() => {
    console.log('commonJS', a); // 0
}, 500)

// ---------- es6.js ----------
import {
    a
} from './export';
setTimeout(() => {
    console.log('es6', a); // 1
}, 500)

YUI