JavaScript 模块化:CommonJS、AMD、CMD、UMD、ES Module

13 篇文章 0 订阅
1 篇文章 0 订阅

JavaScript 模块化:CommonJS、AMD、CMD、UMD、ES Module

为什么要模块化

用原始方式加载,所有变量都挂载在window对象上,前端功能越来越多,引用的包越来越多,难免会有冲突。因此需要模块化来区分不同的功能。

以下简述下各种模块化方案的使用方法,只做了解,不做深入。

在webpack和babel工具统治下,我们只需要学好ES Module 即可

一、commonJS(CJS)

特点

  • 所有代码都运行在模块作用域,不会污染全局作用域。

  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。

  • 模块加载的顺序,按照其在代码中出现的顺序。

nodejs中默认使用commonJS规范,适用于后端开发。

用法

简约写法(需要构建工具额外支持)
// 引入
const moment = require('moment');

const getDateFormat = function (value) {
  return moment(value).format('L');
};

const data = {}

// 导出方式一
exports.data = data;
// 导出方式二
module.exports.getDateFormat = getDateFormat;
// 导出方式三
module.exports = {
    data,
    getDateFormat
}
// 错误的方式
exports = {
    data,
    getDateFormat
}
使用webpack 构建
  • 输出代码
// util.js
(function(module, exports, require) {
    // 引入
    const moment = require('moment');
    
    const getDateFormat = function (value) {
        return moment(value).format('L');
    };
    
    const data = {}
    
    module.exports = {
        data,
        getDateFormat
    }
 })(module, module.exports, require)
  • 模块管理中心
// tiny-browser-require 
function require(p){
  var path = require.resolve(p);
  var mod = require.modules[path];
  if (!mod) throw new Error('failed to require "' + p + '"');
  if (!mod.exports) {
    mod.exports = {};
    mod.call(mod.exports, mod, mod.exports, require.relative(path));
  }
  return mod.exports;
}

require.modules = {};

require.resolve = function (path){
  var orig = path;
  var reg = path + '.js';
  var index = path + '/index.js';
  return require.modules[reg] && reg
    || require.modules[index] && index
    || orig;
};

require.register = function (path, fn){
  require.modules[path] = fn;
};

require.relative = function (parent) {
  return function(p){
    if ('.' != p.charAt(0)) return require(p);
    var path = parent.split('/');
    var segs = p.split('/');
    path.pop();

    for (var i = 0; i < segs.length; i++) {
      var seg = segs[i];
      if ('..' == seg) path.pop();
      else if ('.' != seg) path.push(seg);
    }

    return require(path.join('/'));
  };
};

require.register("moment",momentSource);
require.register("./util.js", function(module, exports, require){
   // 引入
    const moment = require('moment');
    
    const getDateFormat = function (value) {
        return moment(value).format('L');
    };
    
    const data = {}
    
    // 导出方式一
    exports.data = data;
    // 导出方式二
    module.exports.getDateFormat = getDateFormat;
    // 导出方式三
    module.exports = {
        data,
        getDateFormat
    }
    // 错误的方式
    exports = {
        data,
        getDateFormat
    }
});


const util = require('./util');

util.getDateFormat(new Date());

二、AMD (RequireJS)

特点

AMD规范则是非同步加载模块,允许指定回调函数。
适用前端模块化开发。

用法

define(id?, dependencies?, factory);
  • 无依赖
define(function(){
 
    const log = function (...args) {
        return console.log(...args);
    };
    
    return {
        log
    }
})
  • 有具体名字
define('base',function(){
 
    const log = function (...args) {
        return console.log(...args);
    };
    
    return {
        log
    }
})
  • 有依赖
// util.js
define("util", ["moment"], function(moment){
 
    const getDateFormat = function (value) {
        return moment(value).format('L');
    };
    
    const data = {}
    
    return {
        data,
        getDateFormat
    }
})
  • 包装模块(向commonJS妥协)
define(function(require, exports, module) {
    const moment = require('moment');
    
    const getDateFormat = function (value) {
        return moment(value).format('L');
    };
     module.exports = {
        getDateFormat
    }
});
  • 加载模块

// 加载模块
require(['util'], function (util){
  util.getDateFormat(new Date());
});


三、CMD(sea.js)

https://www.jianshu.com/p/890c83fbcf50

 define(function(require,exports,module){...});

很明显在使用方式来说,CMD是AMD的子集。区别在于加载机制不同。

  • AMD在加载完成定义(define)好的模块就会立即执行,所有执行完成后,遇到require才会执行主逻辑。(提前加载)
  • CMD在加载完成定义(define)好的模块,仅仅是下载不执行,在遇到require才会执行对应的模块。(按需加载)
  • AMD用户体验好,因为没有延迟,CMD性能好,因为只有用户需要的时候才执行。

四、UMD(Universal Module Definition)

集结了 CommonJs、CMD、AMD 的规范于一身,没有自己的标准,只为适配多种模块化存在。

((root, factory) => {
    if (typeof define === 'function' && define.amd) {
        //AMD
        define(['moment','exceljs'], factory);
    } else if (typeof exports === 'object') {
        //CommonJS
        module.exports = factory(requie('jquery'),require('exceljs'));
    } else {
        root['util'] = factory(root.moment,root.exceljs);
    }
})(this, (moment,exceljs) => {
    
    const getDateFormat = function (value) {
        return moment(value).format('L');
    };
    const exportExcel = function(column,dataSource){
        exceljs.export('xx.xlsx',column,dataSource)
    }
    return {
        exportExcel,
        getDateFormat
    }
});

五、ES Module (ES6)

ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

  • export
// util.js
import moment from 'moment';
export const getDateFormat = function (value) {
        return moment(value).format('L');
};

const data = {}

export default data;

  • import
// app.js
import data,{ getDateFormat } from './util';

因为需要支持古董浏览器,所以需要用webpack、babel构建出AMD/CMD,CJS

babel转ES Module例子:

// rc-select es/index.js
import Select from './Select';
import Option from './Option';
import OptGroup from './OptGroup';
export { Option, OptGroup };
export default Select;

转换后:

// rc-select lib/index.js
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

// 只读
Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "Option", {
  enumerable: true,
  get: function get() {
    return _Option.default;
  }
});
Object.defineProperty(exports, "OptGroup", {
  enumerable: true,
  get: function get() {
    return _OptGroup.default;
  }
});
exports.default = void 0;

var _Select = _interopRequireDefault(require("./Select"));

var _Option = _interopRequireDefault(require("./Option"));

var _OptGroup = _interopRequireDefault(require("./OptGroup"));

var _default = _Select.default;
exports.default = _default;

babel,直接require的非ES模块的对象加个default属性,使得与ESModule保持一致。


// @babel/runtime/helpers/interopRequireDefault
function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : {
    "default": obj
  };
}

module.exports = _interopRequireDefault;
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值