前端模块化方式总结
本文旨在对前端模块化方式
CommonJS
,AMD
,CMD
,UMD
,ES6 Module
的大致使用方式、解决的问题以及特性进行归纳总结,方便以后查阅。
VanillaJs
IIFE & 闭包
// people.js
var people = (function() {
let name = "Sophia";
let age = 12;
return {
getName: () => name,
getAge: () => age
};
})();
// invoke.js
console.log(people.getName());
console.log(people.getAge());
有了 namespace 的概念,外部无法覆盖 name 和 age,但是 people 依然是全局变量。
改良
// people.js
(function(window) {
let name = "Sophia";
let age = 12;
return {
getName: () => name,
getAge: () => age,
say: () => say(name)
};
})(window, say);
// say.js
(function(window) {
window.say = (name) => {
console.log(`Hello ${name}.`)
}
})(window);
<script src="./dance.js"></script>
<script src="./people.js"></script>
<script>
people.say();
</script>
有了依赖的概念,但是依然没有解决全局变量的问题,而且模块引入顺序必须要靠人工维护。
CommonJS
主要用于 NodeJs,浏览器原生不支持,可通过 browserify 或 webpack 打包后在浏览器上跑。
// people.js
const { say } = require("./say.js");
let name = "Sophia";
let age = 12;
module.exports = {
getName: () => name,
getAge: () => age,
say: () => say(name)
};
// say.js
module.exports = {
say: (name) => {
console.log(`Hello ${name}.`);
}
}
// invoke.js
const people = require("./people.js");
people.say();
特性
- 以文件为单元模块
- 同步加载模块
- 模块可以被加载多次,但只会在第一次执行,而后读取的是之前运行的缓存。
- 模块加载顺序和代码中的顺序一致
- 导出的值是拷贝
AMD / RequireJs
AMD (Asynchronous Module Definition) 顾名思义,主要解决模块的异步加载的问题。
// people.js
define(['./say.js'], function({ say }) {
let name = "Sophia";
let age = 12;
return {
getName: () => name,
getAge: () => age,
say: () => say(name)
};
});
特性
- 异步加载
- 依赖前置
CMD / SeaJs
CMD (Common Module Definition) 通用模块定义,最早由阿里的大神玉伯提出。
// people.js
define(function (requie, exports, module) {
const { say } = require('./say.js');
let name = "Sophia";
let age = 12;
module.exports = {
getName: () => name,
getAge: () => age,
say: () => say(name)
};
});
特性
- 相对自然的依赖声明风格
- 依赖就近 (RequireJs 2.0+ 也加入此特性)
UMD
UMD (Universal module definition) 是 AMD 和 CommonJS 的综合的产物。AMD 适用于浏览器,CommonJS 用于 NodeJs 服务端,为了解决模块跨平台问题,衍生出 UMD 规范。其本质就是通过 if/else 判断全局变量,然后决定如何加载。
(function (window, factory) {
if (typeof exports === 'object') {
module.exports = factory();
} elseif (typeof define === 'function' && define.amd) {
define(factory);
} else {
window.eventUtil = factory();
}
})(this, function () {
//module ...
});
ES6 Module
// people.js
import { say } from "./say.js"
let name = "Sophia";
let age = 12;
export default {
getName: () => name,
getAge: () => age,
say: () => say(name)
};
特性
- 模块输出是值的引用,而 CommonJS 是值的拷贝
- 静态语法,只能卸载顶层,而 CommonJS 是动态语法,可以写在判断里
- 和 CommonJS 一样,模块只会被加载一次,不同的是,import 指向模块的引用,而 CommonJS 加载的时候就会执行该脚本,然后内存中生成一个对象,而后只会返回会缓存的结果
- this 是 undefined,而 CommonJS 是指向当前模块
- 无论模块内是否声明 use strict,都采用严格模式