前言
在 JavaScript 发展初期就是为了实现简单的页面交互逻辑,寥寥数语即可;如今 CPU、浏览器性能得到了极大的提升,很多页面逻辑迁移到了客户端(表单验证等),随着 web2.0 时代的到来,Ajax 技术得到广泛应用,jQuery 等前端库层出不穷,前端代码日益膨胀,此时在 JS 方面就会考虑使用模块化规范去管理。
本文内容主要有理解模块化,为什么要模块化,模块化的优缺点以及模块化规范, 并且介绍下开发中最流行的 CommonJS、AMD、 ES6、CMD 规范。本文试图站在小白的角度,用通俗易懂的笔调介绍这些枯燥无味的概念,希望诸君阅读后,对模块化编程有个全新的认识和理解!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NggwiQK3-1589010196638)(https://pic4.zhimg.com/80/v2-b0129c9601bfdbc7117eabef1cdeb00b_720w.jpeg)]
一、模块化的理解1. 什么是模块?
模块是指将一个复杂的程序依据一定的规则 (规范) 封装成几个块 (文件),并进行组合在一起,块的内部数据与实现是私有的, 只是向外部暴露一些接口 (方法) 与外部其它模块通信。
- 模块化的进化过程
全局 function 模式:将不同的功能封装成不同的全局函数;
编码: 将不同的功能封装成不同的全局函数;
问题: 污染全局命名空间,容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系。
function m1(){
//...
}
function m2(){
//...
}
namespace 模式:简单对象封装
- 作用: 减少了全局变量,解决命名冲突
- 问题: 数据不安全 (外部可以直接修改模块内部的数据)
- let myModule = { data: ‘www.baidu.com’, foo() { console.log(
foo() ${this.data}
) }, bar() { console.log(bar() ${this.data}
) } } myModule.data = ‘other data’ // 能直接修改模块内部的数据 myModule.foo() // foo() other data
这样的写法会暴露所有模块成员,内部状态可以被外部改写。
IIFE 模式:匿名函数自调用 (闭包)
- 作用: 数据是私有的, 外部只能通过暴露的方法操作;
- 编码: 将数据和行为封装到一个函数内部, 通过给 window 添加属性来向外暴露接口;
- 问题: 如果当前这个模块依赖另一个模块怎么办?
// index.html 文件
<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
myModule.foo()
myModule.bar()
console.log(myModule.data) //undefined 不能访问模块内部数据
myModule.data = 'xxxx' // 不是修改的模块内部的 data
myModule.foo() // 没有改变
</script>
// module.js 文件
(function(window) {
let data = 'www.baidu.com'
// 操作数据的函数
function foo() {
// 用于暴露有函数
console.log(`foo() ${data}`)
}
function bar() {
// 用于暴露有函数
console.log(`bar() ${data}`)
otherFun() // 内部调用
}
function otherFun() {
// 内部私有的函数
console.log('otherFun()')
}
// 暴露行为
window.myModule = { foo, bar } //ES6 写法
})(window)
最后得到的结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XWT0Byk-1589010196653)(https://pic3.zhimg.com/80/v2-c796114ffa93bbe124eb8a323522cb6e_720w.png)]
IIFE 模式增强:引入依赖
这就是现代模块实现的基石。
// module.js 文件
(function(window, $) {
let data = 'www.baidu.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)
// index.html 文件
<!-- 引入的 js 必须有一定顺序 -->
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
myModule.foo()
</script>
上例子通过 jquery 方法将页面的背景颜色改成红色,所以必须先引入 jQuery 库,就把这个库当作参数传入。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。
- 模块化的好处
- 避免命名冲突 (减少命名空间污染)
- 更好的分离, 按需加载
- 更高复用性
- 高可维护性
- 引入多个
- 请求过多
首先我们要依赖多个模块,那样就会发送多个请求,导致请求过多。
- 依赖模糊
我们不知道他们的具体依赖关系是什么,也就是说很容易因为不了解他们之间的依赖关系导致加载先后顺序出错。
- 难以维护
以上两种原因就导致了很难维护,很可能出现牵一发而动全身的情况导致项目出现严重的问题。模块化固然有多个好处,然而一个页面需要引入多个 js 文件,就会出现以上这些问题。而这些问题可以通过模块化规范来解决,下面介绍开发中最流行的 co