javascript之模块加载方案

前言

主要学习一下四种模块加载规范:

  1. AMD
  2. CMD
  3. CommonJS
  4. ES6 模块

历史

前端模块化开发那点历史

require.js

requirejs 为全局添加了 define 函数,你只要按照这种约定的方式书写这个模块即可。

define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});
//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

以上示例代码来源于require.js官网

demo代码详见 https://github.com/BillyQin/jsModule/tree/master/requireJs

AMD

require.js 为全局添加了define 函数,按照这种约定方式写即可。

这个约定方式就是AMD(The Asyncchronous Module Definition)

所以AMD规范就是定义了怎么写define函数。只要按照这个规范来写模块和依赖,require.js就能正确解析。

sea.js

demo代码详见 https://github.com/BillyQin/jsModule/tree/master/seaJs

CMD

同样的道理,CMD就是Sea.js对模块定义对规范化产出。

所以CMD的内容就是描述该如何定义模块,如何引入模块,如何导出模块。只要按照这个规范来写模块和依赖,sea.js就能正确解析。

AMD 和 CMD

  1. AMD 推崇依赖前置,
  2. CMD推崇依赖就近 

  3. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。
  • AMD 是将需要使用的模块先加载完再执行代码

  • CMD 是在 require 的时候才去加载模块文件,加载完再接着执行。

CommonJS

AMD 和 CMD 都是用于浏览器的模块规范,而在服务端(node),则采用CommonJS。

CommonJS和sea.js一样,require的时候才去加载模块文件,加载完再接着执行。

demo代码详见 https://github.com/BillyQin/jsModule/tree/master/commonJs

为什么浏览器中不支持 CommonJS 语法呢?

这是因为浏览器环境中并没有 module、 exports、 require 等环境变量。

ES6

es6定义了新的模块加载方案。

// 导出
const addr = 'China'
const year = 2018
export { addr, year } 
// 导入
import { addr, year } from './index.js'

和require.js(AMD)一致,将需要使用的模块加载完再执行代码。

ES6 和 CommonJS的差异

  1. CommonJS模块输出值的拷贝, ES6输出值的引用。 CommonJS模块输出值的拷贝, 也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

  2. CommonJS是运行时加载,ES6是编译时输出接口。 CommonJS加载的是一个对象,就是module.exports属性。该对象只有在脚本运行完成后才会生成。而es6模块不是对象,对外接口只是一种静态定义,在代码静态解析阶段就会生成。

Babel

es6语法在线转换

在浏览器不支持es6的时候,如果要使用es6的语法,一般都会在项目里加入babel。

// es6
let firstName = 'Michael';
const lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};

转换后

Object.defineProperty(exports, "__esModule", {
  value: true
});
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

exports.firstName = firstName;
exports.lastName = lastName;
exports.year = year;

webpack

Babel 只是把 ES6 模块语法转为 CommonJS 模块语法,而浏览器不支持CommonJs。这时候webpack出动。

浏览器不支持CommonJs的本质是因为浏览器环境中并没有 module、 exports、 require 等环境变量。 webpack 打包后的文件之所以在浏览器中能运行,就是靠模拟了这些变量的行为。

webpack怎么模拟呢?

// commonJs
let multiply = require('./multiply')
console.log('加载 square 模块')

let square = function (num) {
  return multiply.multiply(num, num)
}

module.exports = {
  square: square
}

模拟后:

// 包裹一层,注入这些变量
function(module, exports, require) {
    console.log('加载了 square 模块');

    var multiply = require("./multiply");
    module.exports = {
        square: function(num) {
            return multiply.multiply(num, num);
        }
    };
}

整个CommonJs项目改写后

// 自执行函数
(function(modules){
    // 存储已加载的模块
    var installModules = {}
    // 关键的require方法 
    function require(moduleName) {
        if (installModules.moduleName) {
            return installModules.moduleName.exports
        }
        
        var module = installModules[moduleName] = {
            exports: {}
        }
        
        modules[moduleName](module, module.exports, require);
        return module.exports;
    }
    
    return require('main')
})({
    'main': function(module, exports, require) {
        var addModule = require("./add");
        console.log(addModule.add(1, 1))

        var squareModule = require("./square");
        console.log(squareModule.square(3));
    },
    './add': function(module, exports, require) {
        console.log('加载 add 模块')
        var add = function (x, y) {
          return x + y
        }
        module.exports = {
            add: add
        }
    },
    './multiply': function(module, exports, require) {
        console.log('加载 multiply 模块')
        var multiply = function (x, y) {
          return x * y
        }
        module.exports = {
            multiply: multiply
        } 
    },
    './square': function(module, exports, require) {
        console.log('加载 square 模块')
        var multiply = require('./multiply')
        var square = function (num) {
          return multiply.multiply(num, num)
        }
        module.exports = {
            square: square
        } 
    }
})

参考

原文地址:https://www.cnblogs.com/BillyQin/p/10058454.html