• 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
            } 
        }
    })
    

    参考

  • 相关阅读:
    隐私保护政策
    童真儿童简笔画
    方块十字消
    iOS 判断一断代码的执行时间(从网上看的,自己实现一下)
    iOS BLOCK回调:(妖妖随笔)
    typedef struct
    #define和预处理指令
    UIActivityIndicatorView
    Expected a type 的错误
    iOS 本地化字符串—(妖妖随笔)
  • 原文地址:https://www.cnblogs.com/BillyQin/p/10058454.html
Copyright © 2020-2023  润新知