这是我参与更文挑战的第3天,活动详情查看: 更文挑战
模块化的理解
什么是模块?将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起;块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信。
一个模块的组成:数据是内部的属性;操作数据的行为是内部的函数。
编码时按照模块一个一个编码的,那么整个项目就是一个模块化的项目。
模块化的进化过程
全局 function 模式
module1.js
//数据
let data = 'hello world'
//操作数据的函数
function foo() {
console.log(`foo() ${data}`)
}
function bar() {
console.log(`bar() ${data}`)
}
module2.js
let data2 = 'other data';
function foo() { //这里与另一个模块中的函数冲突了
console.log(`foo() ${data2}`)
}
index.html
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript">
let data = "我是修改后的数据"
foo()
bar()
</script>
- 编码:将不同的功能封装成不同的全局函数。
- 问题:Global 被全局污染了,很容易引起命名冲突
namespace 模式
module1.js
let myModule = {
data: 'module1',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
module2.js
let myModule2 = {
data: 'module2 atguigu.com',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}
index.html
<script type="text/javascript" src="module2.js"></script>
<script type="text/javascript" src="module22.js"></script>
<script type="text/javascript">
myModule.foo()
myModule.bar()
myModule2.foo()
myModule2.bar()
//可以直接修改模块内部的数据
myModule.data = 'other data'
myModule.foo()
- 编码:简单对象封装
- 作用:减少了全局变量
- 问题:依旧可以修改模块内部代码,不安全
IIFE 模式
module1.js
(function (window) {
//数据
let data = 'atguigu.com'
//操作数据的函数
function foo() { //向外暴露的内部私有函数
console.log(`foo() ${data}`)
}
function bar() {//向外暴露的内部私有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}
function otherFun() { //未暴露的内部私有函数
console.log('otherFun()')
}
//暴露行为
window.myModule = {foo, bar}
})(window)
index.html
<script type="text/javascript" src="module1.js"></script>
<script type="text/javascript">
myModule.foo()
myModule.bar()
//myModule.otherFun() //报错:myModule.otherFun is not a function
console.log(myModule.data) //undefined 不能访问模块内部数据
myModule.data = 'xxxx' //并不是修改的模块内部的data
myModule.foo() //未受影响
</script>
- 编码:匿名函数自调用(闭包)
- IIFE : immediately-invoked function expression(立即调用函数表达式)
- 作用:数据是私有的,外部只能通过暴露的方法操作
- 问题:如果当前这个模块依赖另一个模块怎么办?
IIFE模式增强
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)
index.html
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script type="text/javascript" src="module4.js"></script>
<script type="text/javascript">
myModule.foo()
</script>
- 引入依赖
- 是现代模块实现的基石
- 页面加载多个
js的问题:一个页面引入多个js的时候,会出现请求过多、依赖模糊、难以维护的问题。
模块化规范
CommonJS
CommonJS 分为浏览器端和服务器端
基本语法
定义暴露模块:exports
exports.xxx = value
module.exports = value
引入模块:require
var module = require('模块名/模块相对路径')
引入模块发生在什么时候?
服务器端:运行时, 动态同步引入
浏览器端:在运行前对模块进行编译/转译/打包的处理(已经将依赖的模块包含进来了),运行的是打包生成的 js,运行时不存在需要再从远程引入依赖模块。
AMD
AMD 只存在在浏览器端,需要依赖 require.js
定义暴露模块:define
define([依赖模块名], function(){return 模块对象})
引用模块:require
require(['模块1', '模块2', '模块3'], function(m1, m2){//使用模块对象})
配置项
require.config({
//基本路径
baseUrl : 'js/',
//标识名称与路径的映射
paths : {
'模块1' : 'modules/模块1',
'模块2' : 'modules/模块2',
'angular' : 'libs/angular',
'angular-messages' : 'libs/angular-messages'
},
//非AMD的模块
shim : {
'angular' : {
exports : 'angular'
},
'angular-messages' : {
exports : 'angular-messages',
deps : ['angular']
}
}
})
CMD
CMD 只存在在浏览器端,需要依赖于 sea.js,目前暂无一个正式的官网。
定义暴露模块:define
define(function(require, module, exports){
通过require引入依赖模块
通过module/exports来暴露模块
exports.xxx = value
})
引用模块:使用 seajs
seajs.use(['模块1', '模块2'])
ES6
ES6 内置了模块化的实现
定义暴露模块 : export
// 暴露一个对象
export default 对象
// 暴露多个
export var xxx = value1
export let yyy = value2
var xxx = value1
let yyy = value2
export {xxx, yyy}
引入使用模块 : import
// default 模块
import xxx from '模块路径/模块名'
// 其他模块
import {xxx, yyy} from '模块路径/模块名'
import * as module1 from '模块路径/模块名'
浏览器不能直接识别 ES6 模块化的语法,因此需要使用 Babel 将 ES6 转换成 ES5,但由于转换完的文件中还使用了 CommonJS,因此需要使用 Browserify 将文件打包处理,转换成浏览器可以识别使用的文件。
最后说一句
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。