前端模块化的演变

前端模块化的演变

2017, Nov 09    

commonjs

  • commonjs是专攻服务器的,由于它的api简单直接,在nodejs中推广开来。
  • commonjs规范是无法用在浏览器的,主要原因在于: 在服务端和浏览器环模块的加载方式截然不同。 服务器中加载一个模块直接在硬盘中读取文件就可以了 但浏览器环境需要动态的创建script标签,然后异步加载模块,并且要等到模块执行完成,才能够使用其中的API.

浏览器端模块化的尝试

1 直接把commonjs闺房封装到浏览器环境(browserify)
2 异步加载模块,依赖前置(AMD)
3 异步加载模块,按需加载(CMD)

1)AMD (requirejs)

  • AMD思想,异步加载所需的模块,并在回调函数中执行剩下的逻辑,这正是我们开发浏览器习惯的方式。 AMD规范包含一下规则:
    1用全局函数define来定义模块,define(id, dependencies, factory)
    2id为模块标识,dependencies为依赖的模块,factory为回调函数,参数中要传递对应的形参。
    # define('x', ['A','B','C'], function(a,b,c){})
    

    3如果dependencies不写,默认为[‘require’, ‘exports’, ‘module’]。
    4factory函数向外暴露:

    # define('x', ['A','B','C'], function(a,b,c){
        //return {}
        //exports.xxx = xxx (==commonjs规范)
        //module.exports = xxx
    })
    

    5若factory为对象,则该对象则为暴露的内容。

  • requirejs的缺点
    1,requirejs不仅预加载模块,而且都执行完了才进入到回调函数。这个性能消耗是值得注意的。
    2,依赖模块的执行顺序和书写顺序不一定一致

    如何让模块预先加载,需要才执行呢??

2)CMD (seajs)

  • cmd是commonjs另外的一种模块加载方案 1,一个文件一个模块,所以经常就用文件名作为模块id
    2,CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写
    3,factory是一个函数,有三个参数,function(require, exports, module)
    4,require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口:require(id)
    5,exports 是一个对象,用来向外提供模块接口
    6,module 是一个对象,上面存储了与当前模块相关联的一些属性和方法
  // 定义模块  myModule.js
  define(function(require, exports) {
    // 获取模块 a 的接口
    var a = require('./a');
    // 调用模块 a 的方法
    a.doSomething();
    });

    // 加载模块
    seajs.use(['myModule.js'], function(my){

    });
  • 貌似同步加载模块,实则代码在运行时,
    需要遍历所有的require关键字,找出后面的依赖。具体做法是将function toString后,用正则匹配出require关键字后面的依赖。
    仅仅在表现形式上,AMD依赖前置,CMD就近依赖。