共计 5631 个字符,预计需要花费 15 分钟才能阅读完成。
一、模块化了解
1. 什么是模块
- 将简单的程序根据肯定的规定 (标准) 拆分成多个模块(文件)
- 模块的外部数据与实现是公有的, 只是向内部裸露一些接口 (办法) 与内部其它模块通信
2. 模块化的进化过程
全局 function : 将不同的性能封装成不同的全局函数
` 毛病 `:虽说能够实现肯定的封装成果,然而大量的全局函数,净化全局命名空间,容易引起命名抵触
function module1 () {//...}
function module2 () {//...}
命名空间 : 简略对象封装
` 毛病 `:缩小了全局变量,解决命名抵触,然而内部能够间接批改模块外部的数据
let module = {
data: 'aaa',
func () {console.log(`${this.data}`)
}
}
module.data = 'bbb' // 间接批改模块外部的数据
module.fn() // bbb
IIFE:(自执行函数)
` 毛病 `:实现数据公有, 内部只能通过裸露的办法操作,如果以后这个模块依赖另一个模块怎么办?
// module.js 文件()
(function (window) {
let data = 'aaa'
function func () {console.log(`${this.data}`)
}
// 裸露接口
window.module = {func}
})(window)
// index.html 文件
<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
module.func() // aaa
console.log(module.data) // undefined 不能拜访模块外部数据
module.data = 'bbb' // 不能批改的模块外部数据
module.func() // aaa
</script>
IIFE 加强 : 引入依赖
// module.js 文件
(function (window, $) {
let data = 'aaa'
function func () {console.log(`${this.data}`)
}
function func2 () {$('body').css('background', 'red')
}
// 裸露接口
window.module = {func, func2}
})(window, jQuery)
// index.html 文件
<!-- 引入的 js 必须有肯定程序 -->
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
module.func2()
</script>
下面引入 jQuery 库,就把这个库当作参数传入,保障模块的独立性,使得模块之间的依赖关系变得显著。
3. 模块化的作用
通过下面的模块拆分,咱们发现:
- 缩小了全局变量,无效的防止了命名净化
- 更好的拆散,按需加载
- 进步了复用性,维护性
然而比较复杂的利用,模块比拟多,不免须要引入多个<script>
,这样又会呈现其余问题:
- 申请过多
- 依赖关系含糊
模块化诚然有多个益处,然而一个页面须要引入多个 js 文件,还得按肯定的程序引入,就可能呈现因为引入程序谬误而导致整个我的项目呈现重大问题。而这些问题能够通过模块化标准来解决。
模块化标准
CommonJs
CommonJS 经 node.js 应运而生,依据 CommonJS 标准,每一个模块都是一个独自的作用域。也就是说,在该模块外部定义的变量,无奈被其余模块读取。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块须要提前编译打包解决。
其核心思想就是一个独自文件就是一个模块,通过 require
办法来同步加载要依赖的模块,而后通过 extports
或module.exports
来导出须要裸露的接口。
// module1.js
var data = 5;
var doSomething = function (value) {return value + data;};
// 裸露的接口
module.exports.data = data;
module.exports.doSomething = doSomething;
下面代码通过 module.exports
输入变量 data
和函数 doSomething
。
var example = require('./module1.js');
console.log(example.data); // 5
console.log(example.doSomething(1)); // 6
require
命令用于加载模块文件。require
命令的基本功能是,读入并执行一个 JavaScript 文件,而后返回该模块的 exports 对象。
长处
:服务器端模块复用性,NPM 中模块包多,有将近 20 万个。
毛病
:加载模块是同步的,只有加载实现后能力执行前面的操作,也就是说现加载现用,不仅加载速度慢,而且还会导致性能、可用性、调试和跨域拜访等问题。因为 Node.js 次要用于服务器编程,加载的模块文件个别都存在本地硬盘,加载起来比拟快,不必思考异步加载的形式,因而,CommonJS 标准比拟实用。然而,这并不适宜在浏览器环境,同步意味着阻塞加载,浏览器资源是异步加载的,鉴于浏览器的状况,为了解决上述同步加载问题,实现异步加载依赖模块,因而有了 AMD、CMD 解决方案。
AMD(Asynchronous Module Definition)
AMD 是 RequireJS
在推广过程中对模块定义的规范化产出。AMD 标准是 异步加载模块
,容许指定回调函数。对于依赖的模块,AMD 推崇 提前执行(依赖前置)
,不过 RequireJS 从 2.0 开始,也改成能够提早执行(依据写法不同,解决形式不同)。
其外围接口是:define(id?, dependencies?, factory)
,它要在申明模块的时候指定所有的依赖 dependencies
,并且还要当做形参传到factory
中,对于依赖的模块提前执行,依赖前置。
// a.js (定义没有依赖的模块)
define(function () {
let data = 'aaa'
function doSomething () {console.log(data)
}
return {doSomething} // 裸露接口
})
// b.js (定义有依赖的模块)
define(['c'], function (c) {
let data = 'bbb'
function doSomething () {console.log(data + c.getData())
}
return {doSomething} // 裸露接口
})
// c.js (此模块为 b.js 依赖)
define(function () {
let data = 'ccc'
function getData () {return data}
return {getData} // 裸露接口
})
// 引入依赖的模块
require(['./a', './b'], function (a, b) { // 依赖必须一开始就写好
a.doSomething()
// ...
b.doSomething()
// ...
})
<body>
<!-- 引入 require.js 并指定 js 主文件的入口 -->
<script data-main="./index" src="https://cdn.staticfile.org/require.js/2.3.6/require.min.js"></script>
<script>
setTimeout(() => {console.log('setTimeout')
}, 0)
</script>
</body>
require()
函数在加载依赖的函数的时候是异步加载的,这也是我在这里放了个 setTimeout
证实一下,这样浏览器不会失去响应,它指定的回调函数,只有后面的模块都加载胜利后,才会运行,解决了依赖性的问题。AMD 的异步加载解决了阻塞加载、性能问题,模块之间的依赖关系也能分明的显示进去。
CMD(Common Module Definition)
CMD 是 SeaJS
在推广过程中对模块定义的规范化产出。CMD 标准和 AMD 很类似,解决同样问题,只是运行机制不同。对于依赖的模块,CMD 推崇 提早执行(依赖就近)
。
// a.js(定义没有依赖的模块)
define(function (require, exports, module) {
let data = 'aaa'
function doSomething () {console.log(data)
}
exports.doSomething = doSomething // 裸露接口
})
// b.js (定义有依赖的模块)
define(function (require, exports, module) {
let data = 'bbb'
function doSomething () {var c = require('./c') // 依赖能够就近书写
console.log(data + c.data)
}
exports.doSomething = doSomething // 裸露接口
})
// c.js (此模块为 b.js 依赖)
define(function (require, exports, module) {
let data = 'ccc'
exports.data = data // 裸露模块
})
// 引入依赖的模块
define(function (require, exports, module) {// 引入依赖模块(异步)
require.async('./a', function (a) {a.doSomething()
console.log('a 是异步的')
})
// 引入依赖模块(同步)
var b = require('./b') // 依赖能够就近书写
b.doSomething()
// ...
var c = require('./c') // 依赖能够就近书写
console.log(c.data)
// ...
})
<body>
<script src="https://cdn.staticfile.org/seajs/3.0.3/sea.js"></script>
<script>
setTimeout(() => {console.log('setTimeout')
}, 0)
seajs.use('./index')
</script>
</body>
ES6
ES6 模块的设计思维是尽量的动态化,使得编译时就能确定模块的依赖关系,以及输出和输入的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些货色。
export
命令用于规定模块的对外接口,import
命令用于输出其余模块提供的性能。为了提供方便,不必浏览文档就能加载模块,就要用到 export default
命令,为模块指定默认输入。
// a.js (定义模块)
var data = 'aaa'
var doSomething = function () {console.log('log:' + data)
};
export {data, doSomething}
// 援用模块
import {data, doSomething} from './a'
这里在语法不做过多介绍,次要说一说 ES6 模块
与 CommonJS 模块
的差别。
它们有两个重大差别:
- CommonJS 模块输入的是一个值的拷贝,ES6 模块输入的是值的援用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输入接口。
第二个差别是因为 CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种动态定义,在代码动态解析阶段就会生成。
咱们来看看第一个差别,CommonJS 模块的加载机制:
// module1.js
var data = 5;
var doSomething = function () {data++;};
// 裸露的接口
module.exports.data = data;
module.exports.doSomething = doSomething;
var example = require('./module1.js');
console.log(example.data); // 5
example.doSomething();
console.log(example.data); // 5
ES6 模块的加载机制:
// module1.js
let data = 5;
function doSomething() {data++;}
export {data, doSomething}
import {data, doSomething} from './module1';
console.log(data); // 5
doSomething();
console.log(data); // 6
ES6 模块的运行机制与 CommonJS 不一样。ES6 模块是动静援用,并且不会缓存值,模块外面的变量绑定其所在的模块。
总结
- CommonJS 模块输入的是一个值的拷贝,CommonJS 模块是运行时加载 ,CommonJS 标准次要用于服务端编程, 加载模块是同步的,同步意味着阻塞加载,浏览器资源是异步加载的,因而有了 AMD、CMD 解决方案。
- AMD 是
RequireJS
在推广过程中对模块定义的规范化产出。AMD 标准在浏览器环境中 异步加载模块 ,而且能够并行加载多个模块。AMD 的 API 默认是 一个当多个用,对于依赖的模块,AMD 推崇提前执行(依赖前置) - CMD 是
SeaJS
在推广过程中对模块定义的规范化产出。CMD 的 API 严格辨别,推崇 职责繁多 , 加载模块是异步的,CMD 推崇提早执行(依赖就近)。 - ES6 模块输入的是值的援用,ES6 模块是编译时输入接口,ES6 在语言规范的层面上,实现了模块性能简略,齐全能够成为浏览器和服务器通用的模块解决方案。