带你全面理解JS模块的标准(AMD、CMD、COMMON.JS 、 UMD、ESM)

在前端的世界演变里,有着几种JS的模块规范,从出现的顺序来说就是:

①amd
②cmd
③common.js
④ umd
⑤ esm

现在Vue框架里面都是遵守esm规范,不得不说esm是目前最好最流行的一种js规范了

amd - 浏览器中的js模块化解决方案

AMD 全称是 Async Module Definition -中文: 异步模块化定义

require.js是AMD模块规范的一个具体实现

核心是通过define方法对无序的代码进行有序的模块化定义,通过require方法实现代码的模块化加载

1、通过define方法定义模块

base.js

define(function (){
    var control = {};
    return control;
});

control.js

define(['jquery', 'jqmd5', 'cookie', 'base'], function (){
    var control = {};
 
    /**
     * 登录状态检测
     */
    control.cookie = function (){
        setTimeout(WK.LC.syncLoginState, 100);
    };
 
    /**
     * 模块调用及配置
     */
    control.template = function (){
        if($('.naver').length > 0) base.naver();
 
        if(CATEGORY == 'login')
        {
            if(MODEL == 'index'){
                // 登录页
                require(['login'], function (Login){
                    Login.form();
                });
            };
 
            if(MODEL == 'register' || MODEL == 'check'){
                // 注册页
                require(['register'], function (Register){
                    Register.form(MODEL);
                });
            };
        };
 
        if(CATEGORY == 'goods')
        {
            // 详情页
            if(MODEL == 'index'){
                require(['detail'], function (Detail){
                    // Detail.form();
                });
            };
        };
    };
 
    return control;
});
2、通过require方法加载模块(异步加载)

注意:参数里面有define声明的模块

require(['control'], function (Control){
    Control.cookie();
    Control.template();
});

cmd - 类似amd的用于浏览器中的js模块规范

CMD 全称是 Common Module Definition -中文: 通用模块化定义

sea.js是CMD模块规范的一个具体实现

在定义模块方面, CMD和AMD一样通过define函数来定义模块; 两者的主要区别在于对依赖的加载上, CMD中不需要在define的参数中直接声明需要用到的模块

1、通过define方法定义模块

calculator.js

define('calculator', function(require, exports) {
	// 通过require方法加载其他模块
    var math = require('math');
    exports.add = function(left, right) { return math.add(left, right) };
    exports.subtract = function(left, right) { return math.subtract(left, right) };
})

可以看到calculator模块所的依赖的math模块没有在define函数的参数中进行声明, 而是通过require(‘math’)来引入的

2、使用calculator模块
seajs.use(['calculator'], function(calculator) {
    console.log('1 + 1 = ' + calculator.add(1, 1));
    console.log('2 - 2 = ' + calculator.subtract(2, 1));
})

common.js -Node中使用的模块规范

通过exportsmodule.exports来暴露模块中的内容。
通过require来加载模块。

demo
1、通过exports 暴露模块接口

study.js

var hello = function () {
    console.log('hello studygd.com.');
}
exports.hello = hello;

main.js

const studygd = require('./study');
studygd.hello();
2、通过module.exports 暴露模块接口

定义math模块
math.js

module.exports = {
    add: function(left, right) {
        return left + right;
    },
    subtract: function(left, right) {
        return left - right;
    }
}

使用刚才定义的math模块, 并再定义一个calculator模块
calculator.js

const math = require('./math.js');
module.exports = {
    add: math.add
}

umd - 一种同时兼容了amd cmd common.js的规范

amd cmd 通常只能在浏览器中使用, commonjs只能在服务端(Node)**环境下使用, 这样子搞会导致我们基于其中某一种模块规范写的js模块无法在服务端和浏览器端进行复用. umd解决了这个问题, 它兼容并包, 使得使用此规范写的 js模块既可以在浏览器环境下使用, 也可以在Node(服务端)环境中用

(function (root, factory) {
    if (typeof exports === 'object' && typeof module === 'object')
        // commonjs
        module.exports = factory()
    else if (typeof define === 'function' && define.amd)
        // amd、cmd
        define([], factory)
    else if (typeof exports === 'object')
        // commonjs
        exports['math'] = factory()
    else
        // 全局对象, 浏览器中是 window
        root['math'] = factory()
})(this, function() {
    return { add: function(left, right) { return left + right; } }
})

*其实只要你看过jq源码,你就会觉得 上面的这段代码很熟悉,是的,jq源码里面就是采用了umd规范去做兼容,所以jq可以说是umd规范的一种代表

附:jq源码大体框架

( function( global, factory ) {

    "use strict";

    if ( typeof module === "object" && typeof module.exports === "object" ) {

        // For CommonJS and CommonJS-like environments where a proper `window`
        // is present, execute the factory and get jQuery.
        // For environments that do not have a `window` with a `document`
        // (such as Node.js), expose a factory as module.exports.
        // This accentuates the need for the creation of a real `window`.
        // e.g. var jQuery = require("jquery")(window);
        // See ticket #14549 for more info.
        module.exports = global.document ?
            factory( global, true ) :
            function( w ) {
                if ( !w.document ) {
                    throw new Error( "jQuery requires a window with a document" );
                }
                return factory( w );
            };
    } else {
        factory( global );
    }

// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

    //这里编写jquery主体代码...

    // AMD
    if ( typeof define === "function" && define.amd ) {
        define( "jquery", [], function() {
            return jQuery;
        } );
    }

    var
        // Map over jQuery in case of overwrite
        _jQuery = window.jQuery,

        // Map over the $ in case of overwrite
        _$ = window.$;

    jQuery.noConflict = function( deep ) {
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }

        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }

        return jQuery;
    };

    if ( !noGlobal ) {
        window.jQuery = window.$ = jQuery;
    }

    return jQuery;
} );

esm - ES6模块规范

使用import导入模块,通过export导出模块

math.js


export { add: (left, right) => left + right; }

在calculator.js导入

import { add } from './math.js';
 
console.log('1 + 1 = ' + add(1, 1));

总结

amd, cmd曾经是个很流行的js模块化产物,记得自己出来工作不久,去面试的时候,也被问过这个两个有什么不一样(上面已经答案),到底使用哪个好等问题,那时自己也是模糊不清,具体回答的不是很清楚,也许是当时那个公司在使用这些规范才考察我,不过现在amd、cmd感觉基本上是没有公司项目在使用了, 现在常用的模块规范一般就是es6模块和commonjs(只用于node)了, node中也已经提供了实验性的es模块支持.

随着微软放弃IE ,现代浏览器对es的import和export的支持也已经很不错了(除了IE其他主流浏览器都支持了)

index.vue:201 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'type') at _callee$ (index.vue:201:1) at tryCatch (regeneratorRuntime.js:44:1) at Generator.eval (regeneratorRuntime.js:125:1) at Generator.eval [as next] (regeneratorRuntime.js:69:1) at asyncGeneratorStep (asyncToGenerator.js:3:1) at _next (asyncToGenerator.js:22:1) at eval (asyncToGenerator.js:27:1) at new Promise (<anonymous>) at eval (asyncToGenerator.js:19:1) at VueComponent.handleNodeClick (index.vue:227:1) _callee$ @ index.vue:201 tryCatch @ regeneratorRuntime.js:44 eval @ regeneratorRuntime.js:125 eval @ regeneratorRuntime.js:69 asyncGeneratorStep @ asyncToGenerator.js:3 _next @ asyncToGenerator.js:22 eval @ asyncToGenerator.js:27 eval @ asyncToGenerator.js:19 handleNodeClick @ index.vue:227 handleCurrentChange @ index.vue:197 invokeWithErrorHandling @ vue.runtime.esm.js:3971 invoker @ vue.runtime.esm.js:1188 invokeWithErrorHandling @ vue.runtime.esm.js:3971 Vue.$emit @ vue.runtime.esm.js:2874 eval @ element-ui.common.js:1116 eval @ vue.runtime.esm.js:4097 flushCallbacks @ vue.runtime.esm.js:4019 Promise.then(异步) timerFunc @ vue.runtime.esm.js:4044 nextTick @ vue.runtime.esm.js:4109 queueWatcher @ vue.runtime.esm.js:3346 Watcher.update @ vue.runtime.esm.js:3584 Dep.notify @ vue.runtime.esm.js:710 reactiveSetter @ vue.runtime.esm.js:4380 proxySetter @ vue.runtime.esm.js:5158 handleCurrentChange @ element-ui.common.js:1069 invokeWithErrorHandling @ vue.runtime.esm.js:3971 invoker @ vue.runtime.esm.js:1188 invokeWithErrorHandling @ vue.runtime.esm.js:3971 Vue.$emit @ vue.runtime.esm.js:2874 onPagerClick @ element-ui.common.js:547 invokeWithErrorHandling @ vue.runtime.esm.js:3971 invoker @ vue.runtime.esm.js:1188 original_1._wrapper @ vue.runtime.esm.js:7265
06-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春风得意之时

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值