JS 模块化学习笔记
一、模块化理解
0. 模块理解
简单理解,具有特定功能的
js
文件,就是js
模块
- 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
- 块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
1. 模块化理解
1.1 什么是模块化?(what
)
编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目
模块化进化历史(在文尾,有兴趣可查看)
1.2 模块化好处?(why
)
- 避免命名冲突(减少命名空间污染)
- 减少耦合度
- 复用性更高
- 可维护性更高
1.3 页面加载多个 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
文件 - 问题:
- 请求过多
- 依赖模糊
- 难以维护
- 这些问题可以通过现代模块化编码和项目构建来解决
二、模块化规范
4种模块化规范分别是 CommonJS、AMD、CMD、ES6
2.1 CommonJS
2.1.1 说明
在服务器端:模块的加载在运行时是同步加载的(可能导致阻塞)
在浏览器端:模块需要 Browserify
提前编译打包处理(浏览器不认识 require
)
2.1.2 基本语法
暴露模块
module.exports = value // 暴露了 exports,value 可以是对象、函数等
exports.xxx = value // 写完整应该是 module.exports.xxx = value,暴露了 exports 这个对象,对象中可以有 xxx 等属性
引入模块
// 1. 当模块是第三方模块时,xxx 为模块名
// 2. 当模块是自定义模块时,xxx 为模块文件路径
require(xxx)
2.2 AMD(requirejs)
2.2.1 说明
-
Asynchronous Module Definition
(异步模块定义) - 专门用于浏览器端,模块的加载是异步的
2.4.2 基本语法
暴露模块
//定义没有依赖的模块
define(function(){
return 模块
})
//定义有依赖的模块
define(['module1', 'module2'], function(m1, m2){
return 模块
})
引入模块
require(['module1', 'module2'], function(m1, m2){
使用m1/m2
})
2.3 CMD
(了解)
2.3.1 说明
-
Common Module Definition
(通用模块定义) - 专门用于浏览器端, 模块的加载是异步的
- 模块使用时才会加载执行
2.3.2 基本语法
暴露模块
//定义没有依赖的模块
define(function(require, exports, module){
exports.xxx = value
module.exports = value
})
//定义有依赖的模块
define(function(require, exports, module){
//引入依赖模块(同步)
var module2 = require('./module2')
//引入依赖模块(异步)
require.async('./module3', function (m3) {
})
//暴露模块
exports.xxx = value
})
引入模块
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
2.4 ES6
2.4.1 说明
ES6
内置了模块化的实现,在浏览器端运行
- 问题: 所有浏览器还不能直接识别ES6模块化的语法
- 解决:
- 使用
Babel
将ES6
--->ES5
(使用了CommonJS
) ----浏览器还不能直接运行 - 使用
Browserify
--->打包处理----浏览器可以运行
- 使用
2.4.2 基本语法
-
定义暴露模块:
export
分别暴露
export xxx export yyy
/*示例*/ // ES6规范 分别暴露 export function foo() { console.log("foo() module1") } export function bar() { console.log("bar() module1") } export const arr = [1]
统一暴露
export {xxx, yyy}
/*示例*/ // ES6规范 统一暴露 function fun1() { console.log("fun1() module2") } function fun2() { console.log("fun2() module2") } export { fun1, fun2 }
默认暴露
export default {}
/*示例*/ // ES6规范 默认暴露 export default { name: 'module3', fun3: function () { console.log("fun3() module3") } }
-
引入使用模块:
import
import {xxx, yyy} from '路径' // 分别暴露和统一暴露 引入方式 import zzz from '路径' // 默认暴露 引入方式
/*示例*/ // import $ from 'jquery' // 分别暴露和统一暴露 模块 import { foo, bar } from './module1.js' import { fun1, fun2 } from './module2.js' // 默认暴露 模块 import xxx from './module3.js' foo() bar() fun1() fun2() xxx.fun3() $('body').css('background', 'pink')
附:模块化进化历史
-
全局function模式
/** * 全局函数模式: 将不同的功能封装成不同的全局函数 * 问题: Global被污染了, 很容易引起命名冲突 */ let msg = 'module1' function foo () { console.log('foo()', msg) } function bar () { console.log('bar()', msg) }
-
namespace模式
/** * namespace模式: 简单对象封装 * 作用: 减少了全局变量 * 问题: 不安全(数据不是私有的, 外部可以直接修改) */ let obj = { msg: 'module2', foo: function () { console.log('foo()', this.msg) } }
-
IIFE模式
/** * IIFE模式: 匿名函数自调用(闭包) * IIFE : immediately-invoked function expression(立即调用函数表达式) * 作用: 数据是私有的, 外部只能通过暴露的方法操作 * 问题: 如果当前这个模块依赖另一个模块怎么办? */ (function (window) { let msg = 'module3' function foo () { console.log('foo()', msg) } window.module3 = { // foo foo: foo } })(window)
-
IIFE模式增强
/** * IIFE模式增强 : 引入依赖(jQuery) * 这就是现代模块实现的基石 */ // 给页面加红色背景 (function (window, $) { let msg = 'module4' function foo () { console.log('foo()', msg) } // foo() window.module4 = foo $('body').css('background', 'red') })(window, jQuery)