一、CommJS
简介:
在JavaScript发展初期就是为了实现简单的页面交互逻辑,寥寥数语即可;如今CPU、浏览器性能得到了极大的提升,很多页面逻辑迁移到了客户端(表单验证等),随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀,这时候JavaScript作为嵌入式的脚本语言的定位动摇了,JavaScript却没有为组织代码提供任何明显帮助,甚至没有类的概念,更不用说模块(module)了,JavaScript极其简单的代码组织规范不足以驾驭如此庞大规模的代码。
特点:
- CommJs是Node.js首先采用的js模块化方案,它是服务器端的模块加载方案。
- CommJs是同步加载模块,服务器端的所有模块都放在本地硬盘,等待模块时间就是硬盘读取文件时的时间,很小,但是对浏览器而言,它需要从服务器加载模块,涉及网速,代理等原因,所以同步加载不适合浏览器端。
- Node.js四个重要的环境变量:module、exports、require、global为CommJs的实现提供了支持。
- require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。
- npm是CommJs的包管理器,所以它也是遵循CommJs规范的。
- CommonJS 模块的顶层this指向当前模块。
- 根据CommonJS规范,一个单独的文件就是一个模块,每一个模块都是一个单独的作用域。除非定义为global的属性。
// a.js
exports.fn = () => {console.log('fn')} === module.exports.fn = () => {console.log('fn')}
或者 module.exports = {fn: () => { console.log('fn')}}
// index.js
var test = require(./a.js)
test.fn()
// fn
二、AMD
AMD 即Asynchronous Module Definition,中文名是异步模块定义的意思。它是一个在浏览器端模块化开发的规范,由于不是JavaScript原生支持,使用AMD规范进行页面开发需要用到对应的库函数,也就是RequireJs,实际上AMD 是 RequireJS 在推广过程中对模块定义的规范化的产出。
特点
- 异步加载模块。它指定的回调,只有当所有模块都被加载成功后,才会运行,避免了网页失去响应。
- 依赖前置:在解析和执行当前模块之前,模块作者就必须指明当前模块所依赖的模块。
require(['./a','./b'],function(a,b){
a.doSomething()
b.doSomething()
})
- 性能好(比较CMD(SeaJs)):代码运行到此处就知道依赖的所有模块。
- 管理模块之间的依赖性,便于代码的编写和维护。
文件路径
- index.html
- js
- main.js
- other
- moduleA.js
- moduleB.js
// 1. 引入RequireJs(index.html)
// async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。
// 2. data-main属性指定网页程序的主模块
<script data-main='js/main' src="js/require.js" defer async="true" ></script>
// 3. 主模块main.js的写法
// 所有模块加载完后才执行
require(["other/moudleA","other/moudleB","other/moduleC","other/jquery"], function(moduleA,moudleB,moduleC,$){
// your code......
})
三、CMD
CMD(Common Module Definition)规范是国内发展起来的,中文名是通用模块定义,它需要用到的库函数就是SeaJs。
特点
- 异步加载
- 依赖就近:可以把你依赖写到代码中的任何地方
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
var b = require('./b')
b.doSomething()
})
- 性能差(比较AMD(RequireJs)),但是使用方便
总结AMD和CMD:
AMD开始就定义了所有模块,根据网络速度,哪个模块先下载好,哪个模块就执行,但是所有模块下载完成后才会执行require回调函数。
CMD开始不知道需要下载那些模块,因为reuqire在各文件的任何位置,所以遍历所有require的位置后,才开始下载所有模块,下载完后才依次执行reuqire的模块,所以消耗性能,但是执行循序和书写顺序是一致的。
简述:AMD、CMD 相对命比较短,到2014年就摇摇欲坠,因为Node.js和Browsersify的兴起,运行时异步加载逐渐被构建时模块化合并分块所替代,webpack已经成为前端必备神器。
四、ES6模块化
import/export
此文主要讲ES6模块化和CommonJs原理上的区别
- CommonJs模块是值的拷贝:
加载时执行,且只执行一次并缓存下来,之后拷贝的都是缓存的初始值。
ES6模块是值的引用:
JS引擎对脚本静态分析时就会生成引用,等脚本真正执行时,import拿到的是执行时的值,所以是动态引用。 - CommonJS模块是运行时加载,ES6模块是编译时输出接口。
- CommonJs模块是浅拷贝,可以外部修改其值。ES6模块只能静态读取,不能修改其值。
- CommonJs模块是对象,模块内顶层this指向本模块的对象。ES6模块内的顶层this返回undefined。