什么是模块/模块化?
模块
- 具有特定功能的 JS 文件
模块化
- 将一个复杂的程序依据一定的规范封装成几个模块 (JS文件), 最后再组合在一起
- 模块的内部数据/实现是私有的, 只是向外暴露一些接口(方法), 与外部其它模块进行通信
为什么要模块化?
- 降低程序复杂度
- 提高解耦性
- 部署方便 (按需引入)
模块化的好处
- 避免命名冲突
- 更好的分离, 按需加载
- 高复用性
- 高可维护性
页面加载引入 script
- 请求过多
- 依赖模糊
- 难以维护
模块化规范
CommonJS
说明
- 每个文件都可以当作一个模块
- 在服务端 —— 模块的加载是
运行时同步加载
的 - 在浏览器端 —— 模块
需要提前编译打包
处理
基本语法
- 暴露模块
- module.exports = value
- exports.xxx = value
- 暴露的是 module.exports 对象
- 引入模块
- require(xxx)
- 暴露模块
实现
服务端实现
- Node.js
浏览器端实现
也称为 CommonJS 的浏览器端的打包工具
1
2先处理 js 文件中不被浏览器识别的 require 语法
browserify js/src/app.js -o js/dist/bundle.js1
2// 在 html 中引入处理后的 js 文件
<script src="./js/dist/bundle.js"></script>
AMD(requirejs)
Asynchronous Module Definition
- 异步模块定义
- 专门用于浏览器端, 模块的加载是
异步
的
基本语法
定义暴露模块 (通过 return 向外暴露)
定义没有依赖的模块
1
2
3define(function () {
return 模块
})定义有依赖的模块
1
2
3define(['module1', 'module2'], function (m1, m2) {
return 模块
})
引入使用模块
1
2
3require(['module1', 'module2'], function (m1, m2) {
使用 m1 / m2
})
实现
用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44// dataService.js
define(function () {
let msg = 'dataService'
function getMsg() {
return msg
}
// 向外暴露
return { getMsg }
})
// alter.js
define(['dataService', 'jquery'], function (dataService, $) {
let msg = 'alert'
function showMsg() {
console.log(msg, dataService.getMsg())
}
$('body').css('background', 'pink')
// 向外暴露
return { showMsg }
})
// main.js
(function () {
// 配置
requirejs.config = {
baseUrl: 'js/', // 基于根路径去找下面引入的模块, 不写是基于当前文件去找
paths: {
dataService: './modules/dataService', // 不要加 .js
alter: './modules/alter',
jquery: './lib/jquery-1.10.0' // 只能小写, jQuery 自定义的模块名就是 jquery
angular: './lib/angular'
},
shim: { // 针对 angular 的单独配置
angular: {
exports: 'angular'
}
}
}
requirejs(['alter', 'angular'], function (alter, angular) {
alter.showMsg()
console.log(angular)
})
})()1
2<!-- 引入 require.js 并指定主文件的入口 -->
<script data-main="js/main.js" src="js/require.js"></script>
CMD(seajs)
- 说明
- Common Module Definition
- 通用模块规范
- 专门用于
浏览器端
, 模块的加载时异步的 - 模块使用时才会加载执行
- 实现(浏览器端)
ES6 模块化
说明
- ES6 Module
- 依赖模块需要编译打包处理
语法
- 导出模块 - export
- 引入模块 - import
如何实现(浏览器端)?
- 先将使用 Babel 将 ES6 编译为 ES5 代码
- 再使用 Browerify 编译打包 js
ES6 模块化的使用
定义 package.json 文件
1
2
3
4{
"name": "es6_babel-browerify",
"version": "1.0.0"
}安装 @babel/cli、@babel/preset-env、@babel/core
1
2npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill在根目录下定义 babel.config.json 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
"useBuiltIns": "usage",
}
]
]
}引入模块
编译
1
2npx babel js/src -d js/lib // ES6 编译为 ES5 (但包含 CommonJS 语法)
browserify js/lib/app.js -o js/lib/bundle.js // 编译 js 文件
默认暴露和常规暴露
常规暴露
export
export 可以写多次, import 的时候必须有 {}
方式一
1
2
3
4
5
6
7
8export function foo () {
...
}
export var arr = [1, 2, 3]
// 引入
import {foo, arr} from ''方式二
1
2
3
4
5
6
7
8
9function foo () {
...
}
var arr = [1, 2, 3]
export { foo, arr }
// 引入
import {foo, arr} from ''
默认暴露
export default
可以暴露任意数据类型
, 暴露的是什么数据, 接收到的就是什么数据只能写一次
1
2
3
4export default 任意类型
// 引入
import 变量名 from ''