模块化
模块化就是把系统分离成独立功能的方法,这样我们需要什么功能,就加载什么功能
- 每个模块都是独立的,良好设计的模块会尽量与外部的代码撇清关系,以便于独立对其进行改进和维护
- 可以重复利用,而不用经常复制自己之前写过的代码
模块化主要解决两个问题,“命名冲突”、“文件依赖”
命名冲突
// a.js
var a = 1;
// b.js
var a = 2;
文件依赖
// b.js依赖a.js,标签的书写顺序必须是:
<script src="/posts/news/a.js" type='text/javascript'></script>
<script src="/posts/news/b.js" type='text/javascript'></script>
这样在多人开发的时候很难协调啊,令人头疼的问题
解决冲突、依赖(按需加载)
使用命名空间
这样的写法会暴露所有模块内的成员,内部状态可以被外部改写
let module = {
name: "likang xie",
sayName() {
console.log(this.name);
},
};
立即执行函数+闭包
函数内部有自己独立的作用域,外部只能访问自己暴露的成员而不能读取内部的私有成员
let module = (function () {
let privateName = "private"; // 私有变量
let privateFn = function () {}; // 私有函数
// 对外暴露的成员
return {
name: "likang xie", // 公有属性
sayName() {
// 公有方法
console.log(this.name);
},
};
})();
// 外部调用
module.sayName(); // likang xie
使用立即执行函数+类
同上
const People = (function () {
let privateName = "private"; // 私有变量
let fn = function () {}; // 私有方法
return class People {
constructor() {
this.name = "likang xie"; // 公有变量
}
// 公有方法
sayName() {
console.log(this.name);
}
};
})();
CommonJs(用于 Node 环境)
根据 CommonJs 规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见,CommonJS 规范加载模块是同步的,也就是说,加载完成才可以执行后面的操作,Node.js 主要用于服务器编程,模块一般都是存在本地硬盘中,加载比较快,所以 Node.js 采用 CommonJS 规范。
定义模块
// module.js
let name = "liakng xie";
let sayName = function () {
console.log(name);
};
module.exports = { name, sayName };
// 或者
exports.sayName = sayName;
加载模块
// 通过 require 引入依赖
let module = require("./module.js");
module.sayName(); // likang xie
module.export 跟 exports 的区别
- module.exports 方法还可以单独返回一个数据类型(String、Number、Object...),而 exports 只能返回一个 Object 对象
- 所有的 exports 对象最终都是通过 module.exports 传递执行,因此可以更确切地说,exports 是给 module.exports 添加属性和方法
exports.name = "likang xie";
exports.age = 21;
console.log(module.exports); // 运行结果:{ name: 'likang xie', age: 21 }
- 同时用到 module.export 跟 exports 的时候
// 情况1
module.exports = { a: 1 };
exports.b = 2;
// { a:1 }
console.log(module.exports);
// 情况2
module.exports.a = 1;
exports.b = 2;
// { a:1, b:2 }
console.log(module.exports);
AMD(用于浏览器环境)
AMD 是"Asynchronous Module Definition"的简写,也就是异步模块定义。它采用异步方式加载模块。通过 define 方法去定义模块,require 方法去加载模块。
定义模块
define(["module"], function () {
let name = "likang xie";
function sayName() {
console.log(name);
}
return { sayName };
});
使用模块
// 通过 require 引入依赖
require(["module"], function (mod) {
mod.sayName(); // likang xie
});
CMD(用于浏览器环境)
CMD 与 AMD 用法很相似
定义模块、使用模块
define(function (require, exports, module) {
// 通过 require 引入依赖
var otherModule = require("./otherModule");
// 通过 exports 对外提供接口
exports.myModule = function () {};
// 或者通过 module.exports 提供整个接口
module.exports = function () {};
});
ES6 模块(用于浏览器环境)
模块功能主要由两个命令构成:export 和 import。export 命令用于规定模块的对外接口,import 命令用于输入其他模块提供的功能。
定义模块、输出变量
a.js
export default { name: "likang xie" };
b.js
// 输出多个变量
export let name = "likang xie";
export let sayName = () => console.log(name);
使用模块
import people from "a.js";
console.log(people); // { name: 'likang xie' }
// 将所有获取到的变量存到people里
import * as people from "b.js";
console.log(people); // 一个module对象 { name: 'likang xie', sayName: .... }
// 或者
import { name, sayName } from "b.js";
浏览器原生支持 ES6 的模块?
具体请参考在浏览器中使用 javascript module(译)
<script type="module">import * as fn from './b.js';</script>
Webpack
Webpack 其实就是一个打包工具,他的思想就是一切皆模块,css 是模块,js 是模块,图片是模块。并且提供了一些列模块加载(各种-loader)来编译模块。官方推荐使用 CommonJs 规范,但是也支持 CMD、AMD、ES6 模块,所以基本通杀.