模块化规范
模块化历史
1、文件划分
- 每个文件是一个独立的模块,通过 script 标签引入不同模块
- 缺点:模块之间缺少依赖关系、维护困难、没有私有空间、变量污染等
<script src="a.js"></script>
<script src="b.js"></script>
2、命名空间
- 规定每个模块只暴露一个全局对象,然后模块的内容都挂载到这个对象中
- 缺点:外部可以更改模块内的值
window.moduleA = {
name: 'zgh',
f1: function () {}
};
modeluA.name = 'js';
3、立即执行函数
为模块提供私有空间,通过参数的形式作为依赖声明。本质是匿名函数的自调用。
- 用 script 标签在页面引入模块,模块的加载不受控,维护困难
- script 标签的加载顺序不能乱
(function ($) {
var name = 'zgh';
function foo() {}
window.moduleA = {
foo: foo
};
})(jQuery);
例子中引入 Jquery 必须要在前面。只将 foo 方法暴露出去,外部无法修改内部值,如 name 的值。
理想方式:在页面中引入一个 JS 入口文件,其余的模块可以通过代码控制,按需加载进来
除了模块加载的问题以外,还需要规定模块化的规范,当前主流是 CommonJS 、ES Modules
模块化概述
什么是模块化?
将一个复杂的程序依据一定的规范封装成几个模块,并组合在一起。
模块的内部数据、方法是私有的,只是向外部暴露一些接口方法与外部模块通信。
为什么要有模块化?
- 数据、方法都是私有的,避免命名冲突,减少命名空间污染
- 降低耦合性,模块拆分,按需加载
- 高复用性,独立的功能模块便于多处复用
- 高可维护性,维护单独的小模块更方便,如果维护一个有很多功能放在一起的大文件会很困难
为什么要引入模块化规范?
如果引入模块化,可能就是在一个文件中引入多个 js 文件,如:
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
这样做会带来很多问题:
- 请求过多:引入 n 个 js 文件,就有 n 次 http 请求
- 依赖模糊:不同的 js 文件可能会相互依赖,如果改一个文件,其他文件可能会报错
最终可能难以维护,所以引入了模块化规范
CommonJS 规范
CommonJS 规范是一套约定标准,主要内容是模块通过 module.exports
导出对外的变量或接口,通过 require()
来导入其他模块的输出到当前模块作用域中。
特性:
- 每个文件就是一个模块,有自己的作用域
- 在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见
- 可以从
node_modules
中引入一个库或者从本地目录引入一个文件 - 所有代码都运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了
- 同步加载
Node.js 早期遵循的就是
CommonJS
规范。从v13.2.0
之后也引入了规范的ES Modules
机制,同时兼容早期的CommonJS
模块的导入导出
引入模块的方式:
const module1 = require('模块名');
暴露的模块本质是 exports
对象
const axios = require('axios');
function getData() {}
function postData() {}
// 方式一、exports
exports.getData = getData;
// 方式二、module.exports
// 导出单个
module.exports.getData = getData;
// 导出所有
module.exports = { getData, postData };
模块的初始化
一个模块中的 JS 代码仅在模块第一次被使用时执行一次,并且在使用的过程中进行初始化,然后会被缓存起来,便于后续继续使用
示例:add.js
let a = 1;
function add() {
return ++a;
}
exports.add = add;
在 main.js 中引入 add.js 模块
let addModule1 = require('./add');
let addModule2 = require('./add');
console.log(addModule1.add()); // 2
console.log(addModule2.add()); // 3
在终端执行 node main.js
运行程序,可以看出 add.js 这个模块虽然被引用了两次,但只初始化了一次
在浏览器端可以通过 browserify 中转使用 CommonJS 规范,属于历史产物。
AMD
AMD (Asynchronous Module Definition) 是 js 中一种模块定义的规范,可以在浏览器端异步加载模块。
AMD 规范主要解决的问题是浏览器中模块化开发的时候,如何保证模块的依赖能够被正确地加载。在 AMD 规范中,模块是以函数的形式组织,并且需要通过 define 函数进行定义。
define(id, dependencies, factory);
- id 是可选参数,表示模块标识符
- dependencies 是可选参数,表示依赖的模块列表
- factory 是一个函数,在模块加载完成后执行。这个函数返回模块的接口
define('moduleA', ['moduleB', 'moduleC'], function (moduleB, moduleC) {
// ... do something ...
return {};
});