在学习 JavaScript 模块化规范之前,我们应该想一下什么是模块?为什么我们要使用模块化?
什么是模块
模块是比对象和函数更大的代码单元,使用模块可以将程序进行归类,也就是说可以将一个复杂的程序依据一定规则(规范)封装成几个块(文件),并进行组合在一块的内部数据/实现私有的,只是向外暴露一些接口(方法)与外部其他模块通信。一般一个.js文件就是一个模块。
模块化
编码时时按照模块一个一个编码的,整个项目就是一个模块化的项目
模块化的进化过程
全局function模式:
* module1.js
let data = 'testdata'
//操作数据的函数
function foo() {
console.log(`foo() ${data}`)
}
function bar() {
console.log(`bar() ${data}`)
}
* module2.js
let data2 = 'other data'
function foo() { //与上面 module1.js 模块中的函数冲突了
console.log(`foo() ${data2}`)
}
说明:
- 全局函数模式:将不同的功能封装成不同的全局函数
- 问题:全局变量被污染了,容易引起命名冲突
namespace模式: 简单对象封装
* module1.js
let myModule = {
data: 'testdata',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
* module2.js
let myModule2 = {
data: 'testdata',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
通过添加命名空间的形式从某种程度上解决了变量命名冲突的问题,但是并不能凶根本上解决这个问题。
说明:
- 作用:将数据/行为封装到对象中,解决了命名冲突(减少了全局变量)
- 问题:数据不安全,暴露了所有的模块成员,内部状态可以被外部改写。
IIFE模式: 匿名函数自调用(闭包)
module3.js
(function (window) {
//数据
let data = 'testdata'
//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
}
function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //内部私有的函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar}
})(window)
说明:
- IIFE : immediately-invoked function expression(立即调用函数表达式)
- 作用:将数据和行为封装到一个函数内部,通过给 window 添加属性来向外暴露接口
- 问题:如果当前这个模块依赖另一个模块怎么办?
IIFE模式增强 : 引入依赖
* 引入jquery到项目中
* module4.js
```
(function (window, $) {
//数据
let data = 'atguigu.com'
//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
$('body').css('background', 'red')
}
function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //内部私有的函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar}
})(window, jQuery)
- 页面加载多个js的问题
- 页面:
<script type="text/javascript" src="module1.js"></script> <script type="text/javascript" src="module2.js"></script> <script type="text/javascript" src="module3.js"></script> <script type="text/javascript" src="module4.js"></script>
说明:
- 一个页面需要引入多个js文件
- 问题:
1、请求过多
2、依赖模糊
3、难以维护
模块化规范
- 服务器端规范主要是 CommonJS
- 浏览器端:Browserify,也称为js的打包工具
- 客户端规范主要有:AMD、CMD。AMD规范的实现主要有 RequireJS;CMD规范主要实现有SeaJS。
CommonJS
基本语法:
定义暴露模块 : exports
exports.xxx = value //value 可以是任意数据类型
module.exports = value
引入模块 : require
var module = require('模块名/模块相对路径')
exports.xxx 和module.exports的实际作用是一样的,但是可以多次使用前者在同一个文件中向外暴露属性,而后者若是多次使用,则最后一个使用module.exports暴露的属性将会覆盖前面定义的所有 module.exports暴露的属性。
AMD
首先我们需要下载 require.js, 并引入;官网: http://www.requirejs.cn/
基本语法:
定义暴露模块: define([依赖模块名], function(){return 模块对象})
引入模块: require(['模块1', '模块2', '模块3'], function(m1, m2){//使用模块对象})
* 配置:
require.config({
//基本路径
baseUrl : 'js/',
//标识名称与路径的映射
paths : {
'模块1' : 'modules/模块1',
'模块2' : 'modules/模块2'
}
})
AMD的