模块化
称职的作家会把他的书分章节和段落;好的程序员会把他的代码分成模块。
- 无模块化
- CommonJS规范
- AMD
- CMD
- UMD
- ES6 modules
无模块化
  <script src="jquery.js"></script>
  <script src="main.js"></script>
  <script src="a.js"></script>
  <script src="b.js"></script>
  <script src="c.js"></script>
缺点:
- 污染全局作用域。容易有命名冲突问题
- 开发和后期维护成本较高
- 依赖关系不明显,不利于维护
CommonJS规范
- 不是一个js类库,而是规范
- Node.js是commonJS规范的主要实践者
- 它有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global
用法
通过 require方法来同步加载所要依赖的其他模块,然后通过 exports 或者 module.exports 来导出需要暴露的接口
// a.js
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
// b.js
vara = require('./a.js');
console.log(example.x); // 5
console.log(example.addX(1)); // 6
优点:
CommonJS规范完成了JavaScript的模块化,解决了依赖、全局变量污染的问题
缺点:
commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。这就是AMD规范诞生的背景。
AMD规范
- 异步加载模块:这里异步指的是不堵塞浏览器其他任务(dom构建,css渲染等),而加载内部是同步的(加载完模块后立即执行回调)
- require.js是AMD规范的实现
用法
define()定义模块,用require()加载模块
- define(id, [depends], callback)
- require([module], callback)
require(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) {
a.test()
if (false) {
b.foo()
}
})
优点:
适合在浏览器环境中异步加载模块。 可以并行加载多个模块。
缺点:
不能按需加载,而是必须提前加载所有的依赖。
CMD规范
- 阿里的玉伯提出
- sea.js是对CMD规范的实现
- 按需加载,依赖就近,延迟执行
用法
define(function(require, exports, module) {
var a = require('./a'); //在需要时申明
a.doSomething();
if (false) {
var b = require('./b');
b.doSomething();
}
})
优点:
同样实现了浏览器端的模块化加载。 可以按需加载,依赖就近。
缺点: 依赖SPM打包,模块的加载逻辑偏重。
UMD
- 不是一种规范
- 是结合 AMD 和 CommonJS 的一种更为通用的 JS 模块解决方案
打包时配置:
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'vue.js',
library: 'Vue',
libraryTarget: 'umd'
}
表示打包出来的模块为 umd 模块,既能在服务端(node)运行,又能在浏览器端运行。我们来看 vue 打包后的源码 vue.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Vue = factory());
}(this, (function () { 'use strict';
// ...
})))
- 首先判断是否为 node 环境:exports 为一个对象,并且 module 存在
- 如果是 node 环境就用 module.exports = factory() 把 vue 导出 (通过 require(‘vue’) 进行引用)
- 如果不是 node 环境判断是否支持 AMD:define 为 function 并且 define.amd 存在
- 如果支持 AMD 就使用 define 定义模块,(通过 require([‘vue’]) 引用)
- 否则的话直接将 vue 绑定在全局变量上(通过 window.vue 引用)
ES6 module
- 之前的几种模块化方案都是前端社区自己实现的,只是得到了大家的认可和广泛使用
- ES6 在语言标准的层面上,实现了模块功能,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
- 由于ES6目前在部分浏览器无法执行,所以,我们只能通过babel将不被支持的import编译为当前受到广泛支持的 require
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
// main.js
import { firstName, lastName, year } from './profile.js';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
模块打包
打包CommonJS
可以使用Browserify
var myDependency = require('myDependency');
var myGrades = [93, 95, 88, 0, 91];
var myAverageGrade = myDependency.average(myGrades);
browserify main.js -o bundle.js
Browserify 首先会通过抽象语法树(AST)来解析代码中的每一个 require 语句,在分析完所有模块的依赖和结构之后,就会把所有的代码合并到一个文件中。然后你在HTML文件里引入一个bundle.js就够了
打包 AMD
开发者选择用RequireJS optimizer, r.js一类的构建工具来合并和压缩AMD的模块。
由于AMD是异步加载模块的。这也就意味着你不是必须把所有的代码打包到一个文件里,模块加载不影响后续语句执行,逐步加载的的模块也不会导致页面阻塞无法响应。
所以,在开发过程中,采用AMD的应用直到正式上线发布之前都不需要构建。
Webpack
Webpack 是新推出的构建工具里最受欢迎的。它兼容CommonJS, AMD, ES6各类规范
参考