前端工程化思想一直以来都是一个非常热议的话题,其实前端工程化从技术方面来说,无非就是这么几点:CommonJS、AMD、CMD、UMD和ES6 Modules,那么我们就来说说前端这几个模块。
1、CommonJS
说到CommonJS,那就的从他的出发点说起了,commonjs的出发点是js没有完善的模块系统。标准库较少,缺少包管理工具,而在node.js兴起之后。能让js在任何地方运行,特别是服务端,也能具备开发大型项目的能力,所以commonjs也就应用而生了。node.js是commonjs的最佳实践者。他有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global.实际使用时,用module.exports定义当前模块对外输出的接口(不推荐直接使用exports),用require加载模块。
commonjs用同步的方式加载模块,在服务端,模块文件够存储在本地磁盘,读取会非常快。所以这样做不会有问题。但是在浏览器端,限制于网络等原因,更合理的加载方式应该是异步加载。
暴露模块:module.exports = value;或者exports.xxx = value;
引入模块:const xxx = require('xxxx')
commonjs规范:
注意:
2、AMD
Asynchronous Module Definition,异步加载模块。他是在一个在浏览器端模块化的开发规范。不是原生js的规范。使用AMD规范进行页面需求开发,需要用到对应的函数库,require.js。
AMD采用异步加载的模块的方式。模块的加载并不会影响到后面语句的执行。所有以来这个模块的语句都定义在一个回掉函数中,等到加载完成之后,这个回掉函数才会执行。
使用require.js来实现AMD规范的模块化:用require.config()指定引用路径。用define()来定义模块用require来加载模块。
define('moduleName',['a','b'],function(ma, mb) {
return someExportValue;
})
require(['a', 'b'], function(ma, mb) {
// do something
})
require主要解决的问题:
语法:difine(id, dependencies, factory)
3、CMD
CMD是另一种模块化方案,它和AMD很类似,不同点在于:AMD推崇依赖前置,提前执行,而CMD推崇依赖就近,延迟执行,这个规范其实就是sea.js推广过程产生的。
因为CMD推崇一个文件一个模块。因此经常会用文件名作为模块id,CMD推崇依赖就近,所以一般不在define的参数中写依赖,而是在factory中写:define(id,deps,factory)
factory: function(require,exports,module) {}
// 定义没有依赖的模块
define(function(require, exports, module){
exports.xxx = value
module.exports = value
})
// 定义有依赖的模块
define(function(require, exports, module){
var module1 = require('./module1.js')
require.async('./module.js', function(m2) {
// do something
})
export.xxx = value
})
// 引入模块
define(function(require, exports, module){
const m1 = require('./module.js')
me.show()
})
4、UMD
一个整合了commonJS和AMD规范的方法。希望能解决跨平台模块的解决方案。
运行的原理:
UMD会先判断是否支持Node.js的模块(export)是不是存在。存在则使用node.js的模块方式。
再判断是不是支持AMD(define是不是存在。存在则使用AMD方式加载模块。
(function(window, factory){
if (typeof exports === 'object') {
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
define(factory)
} else {
window.eventUtil = factory()
}
})(this,function () {
// do something
})
5、ES6Module
ES6在语言标准的层面上,实现了模块化功能,而且实现的相当简单,旨在成为浏览器和服务器通用的模块化解决方案,其模块化功能主要由俩个命令构成:exports和import,export命令由于规定模块的对外接口,import命令用于输入其他模块的功能。其实ES6还提供了export default的命令。为模块指定默认输出。对应的import语句不需要大括号。这也更接近AMD的引用写法。
ES6模块不是对象,import命令被JavaScript引擎静态分析,在编译的时候就引入模块代码。而不是在代码运行时加载,所以无法实现条件加载。也就使得静态分析成为可能。
(1)export
export可以导出的是对象中包含多个属性、方法,export default只能导出一个可以不具名的函数。我们可以输用import引入。同时我们也可以直接使用require使用,原因是webpack启用了server相关。
(2)import
import { fn } from './xxx' // export导出的方式
import fn from 'xx' // export default方式
总结:
commonJS是同步加载的,主要是node.js也就是服务端也应用的模块化机制。通过module.exports导出声明。通过require('')加载。每个文件都是一个模块。他有自己的独立的作用域。文件内的变量,属性函数等不能被外界访问,node会将模块缓存,在第二次加载的时候会从缓存中获取。
AMD是异步加载的,主要是在浏览器环境下应用,require.js是遵守AMD规范的模块化工具。它通过define()定义声明,通过require('',function() {})来加载。
ES6模块化加载时,通过exports default导出。用import来导入,可以通过对导出内容进行解构。ES6模块运行机制与commonjs运行机制不一样。js引擎对脚本静态分析的时候,遇到模块加载指令后会生成一个只读引用。等到脚本真正执行的时候。才会通过引用模块中获取值,在引用到执行的过程中,模块中的值发生变化,导入的这里也会跟着发生变化。ES6模块是动态引入的。并不会缓存值。模块里总是绑定其所在的模块。