初探:js模块化的进程

304 阅读3分钟

我是站在讲故事的角度去看js模块化进程。所以很多都比较浅嫩,甚至于直接照搬别人的观点,更多的是为了自己学习所用,梳理一下这段历程。所以大神们莫看,不然真的会浪费你宝贵的时间,抱着好玩有趣的心态的盆友请接着往下看。

久远的时代

不同的函数简单的堆叠在一起,就是一个个不同的模块

function time(){

}
function data(){
    
}
...
  • 缺点: 所有的函数名和函数都在全局作用域下,一旦函数多起来就会造成命名冲突。

命名空间

  • 为了减少全局变量,命名空间的概念被提出来。
var time={
        date:'2019/10/24';
        hour:function(){
            
        },
        minute:function(){
            
        },
        second:function(){
            
        }
    }
  • 这样依赖确实是减少了很多在全局作用域下的变量,也不容易发生命名冲突。但是这样的写法会暴露模块的内部成员,也即使内部的状态会被外部的状态所改变。这样一来根本不适用与多人协作开发。
    time.date='xxxx';
    console.log(time.date);

立即执行函数(IIFE模式)

var module1=(function(){
    var _count=0;
    var m1=function(){
       //... 
    };
    var m2=function(){
        //...
    }
})();

优点:函数立即执行,外部代码无法读取内部的_count变量。

继续改进:

立即执行函数—放大模式

如果一个模块很大,必须分成好几个部分,或者是一个模块需要继承另一个

模块。这时候就用到了放大模式。

var moudle1=(function(modoule){
    modoule.add=function(){
        //...
    };
    return modoule
})(moudule1)

上面的代码为module1模块添加了一个add方法,并且返回了一个新的module1模块。

立即执行函数—输入全局变量

模块的重要特性是模块应该具有独立性,模块的内部最好不要与程序的其他部分直接交互。但是很多时候,我们需要在模块内部调用全局变量,因此我们可以显示的将全局变量输入模块。

var module1=(function($){
    //...
})(jQuery)

这样依赖声明了依赖关系,也保证了模块的独立性。

悠久的时代

通过一大堆的script标签引入,这样的造成的问题很多。

按顺序执行,依赖大的文件必须放在最后加载。加载的过程中会暂停网页渲染。依赖关系不明显,而且可维护性很差。

<script src="..."></script>
<script src="..."></script>
//...
<script src="..."></script>

近代

CommandJS征服世界的第一步是跳出浏览器

当时的需求是在服务器端变成必须要有模块化,否则难以进行。

但是CommandJS的规范不适用于浏览器环境。

var diff=require('diff')
diff.compare('A','B');

第二行的执行时间取决于diff模块的加载速度。

这在服务器端完全可以接受,因为文件放在磁盘里,加载速度取决于硬盘的读取速度。而在浏览器就取决于网速,很容易处于假死状态。

AMD规范

AMD采用了异步加载模块。模块的加载不影响他后面的模块加载,所以依赖这个模块的语句都写在回调函数中。

    //依赖前置,所有依赖做为形参传入
    define("diff", ["dep1", "dep2"], function(d1, d2) {
        return someExportedValue;
    });
    
    require(['diff'],function(diff){
        diff.compare("A","B");
    })

在浏览器中可以异步加载,并行多个模块。但是依赖还是不够明显。代码阅读较为困难,开发成本提高。

现代

最后ES6发布,ES6模块化一统江湖,成为浏览器端和服务器端的通用解决方案。

最后感谢阮一峰大佬的文章,还是很多地方值的深挖,这里就先码着。