js模块化历程

262 阅读3分钟

前言

一开始js做的交互很简单,没有模块化的概念,随着前端交互功能的复杂化,js文件的代码量变得越来越大,也就衍生出模块化的概念。

模块化历程

CommonJs

出现原因:nodejs为了解决模块化问题设计的,一个文件就是一个作用域,拥有自己的变量和方法。

通过requiremoudle.exports实现,CommonJs的输出是值的拷贝,外部引入该文件的变量和方法时,该模块代码只有在第一次运行的时候会被加载,运行结果会被缓存下来,外部的下次引用都是从缓存中直接读取,如下:

// a.js文件
var num = 1;
function updateNum(){
    num++;
}

moudle.exports={
    num:num,
    updateNum:updateNum
};
// b.js文件
var num = require('./a').num;
var updateNum = require('./a').updateNum;

console.log(num); // 1
updateNum();
console.log(num); // 1 因为num只是对a模块中的num的值的拷贝,而updateNum修改的是a模块中的num

AMD

出现原因:nodejs是在服务端运行,文件都是存在磁盘,不需要发起请求获取,所以CommonJs的模块化加载是同步执行的。但是用于浏览器环境显然不合适,script标签引入脚本是异步的,所以衍生出AMD,AMD能够异步的加载模块脚本

实现库是require.js,通过definerequire实现,模块的加载不影响后面语句的执行,所有依赖该模块的语句,都会放在一个回调函数当中,当模块加载完毕才执行该回调函数。

define:用于定义模块,需要3个参数,第一个参数是模块标识,没传默认为本文件名,第二个参数是数组,存放该模块的依赖,第三个参数是一个函数,模块初始化时候执行,只执行一次。

require:用于读取其他模块,需要2个参数,第一个参数是数组,放需要加载模块的集合,第二个参数是回调函数,等模块加载完执行。

// a.js文件
define(['dependency'],function(){
    var num = 1;
    function updateNum(){
        num++;
    }
    return {
        num:num,
        updateNum:updateNum
    };
})
// b.js文件
require(['a'],function(obj){
    var num = obj.num;
    console.log(num);// 1
})

CMD

CMD与AMD区别在于,实现库是sea.js,AMD推从依赖前置,CMD推崇依赖就近。

ES

ES的模块化加载通过exportexport defaultimport实现, ES的输出是值的引用。b模块引用了a模块暴露的updateNum方法执行后,修改了a模块中的num值,b模块也会获取到最新的num值,如下。

// a.js文件
const num = 1;
const updateNum = ()=>{
    num++;
}

export { num, updateNum };
// b.js文件
import { num, updateNum } from './a.js' 

console.log(num);// 1
updateNum();
console.log(num);// 2 

CommonJs和ES的区别

1、CommonJs的模块输出是值的拷贝,ES的模块输出是值的引用

2、CommonJs模块是运行时加载,ES模块是编译时输出

3、CommonJs的require()是同步加载模块,会阻塞后面语句的执行,ES的import是异步加载模块,有一个独立的模块依赖解析阶段。