在ES6模块解决方案出现之前,工具库或包常用三种解决方案提供对外使用的接口,最早是直接暴露给全局变量,比如我们熟知的Jquery暴露的全局变量是$,Lodash对外暴露的全局变量是_,后来出现了AMD和CommonJS(CMD的一种实现)两种常用的模块解决方案.

  • 全局变量
// MyDependency is in your global scope
var MyModule = function() {
MyDependency.xxx()
};
  • CommonJS
var MyDependency = require('my-dependency');
module.exports = function() {
MyDependency.xxx()
};
  • AMD
define(['my-dependency'], function(MyDependency) {
return function() {
MyDependency.xxx()
};
});

为了同时兼容各种场景下的使用,业界提出了UMD的解决方案.遵循该标准的包可以通过上面三种方式引入.

以Lodash为例,其源码结构大致如下:

;(function(){

    ...
...
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; /** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')(); /** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; /** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; function lodash(value) {
return xxxx
} lodash.xxx= xxxxxx
lodash.prototype.xxxx= xxxxx ...
...
... /*--------------------------------------------------------------------------*/
// 导出为AMD模块
if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { root._ = lodash; define(function() {
return lodash;
});
}
// 导出为CommonJS模块
else if (freeModule) {
(freeModule.exports = lodash)._ = lodash;
freeExports._ = lodash;
}
//导出到全局变量
else {
root._ = lodash;
} }.call(this))

关于UMD的实现方式有很多种,都大同小异,下面是常见的实现方式.

(function (root, factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('b'));
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['b'], function (b) {
return (root.returnExportsGlobal = factory(b));
});
} else {
// Global Variables
root.returnExportsGlobal = factory(root.b);
}
}(this, function (b) {
// Your actual module
return {
b.xxx
};
}));

以Zepto为例,由于其仅用于浏览器端,因此不需要支持CommonJS.

/* Zepto v1.2.0 - zepto event ajax form ie - zeptojs.com/license */
;(function(global, factory) {
if (typeof define === "function" && define.amd)
define(function() {
return factory(global)
})
else factory(global)
})(this, function(window) {
var Zepto = (function() {
...
...
...
})()
window.Zepto = Zepto
window.$ === undefined && (window.$ = Zepto)()
return Zepto
})

Ramda的UMD封装通用性更强,还支持Babel转换ES6模块的方式.参见babel-plugin-transform-es2015-modules-commonjs

//  Ramda v0.25.0
// https://github.com/ramda/ramda
// (c) 2013-2017 Scott Sauyet, Michael Hurley, and David Chambers
// Ramda may be freely distributed under the MIT license. ;(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined"
? factory(exports) // CommonJS
: typeof define === "function" && define.amd
? define(["exports"], factory) // AMD
: factory((global.R = {})) // Global Variables
})(this, function(exports) {
"use strict" exports.F = F
exports.T = T
exports.__ = __
exports.add = add
exports.addIndex = addIndex
exports.adjust = adjust
exports.all = all
// ...
// ...
exports.zipWith = zipWith // 支持 Babel 转换的 ES6 module
Object.defineProperty(exports, "__esModule", { value: true })
})

当然实际开发过程中,我们不需要自己写样板代码,可以使用webpack+babel打包.例如我们设计一个简单的求和模块.

// sum.js
function Sum(a, b){
return a + b
}
export { Sum }

安装如下几个依赖

// package.json
"devDependencies": {
"@babel/core": "^7.1.6",
"babel-loader": "^8.0.4",
"babel-plugin-transform-es2015-modules-umd": "^6.24.1",
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2"
}

webpack配置如如下:

var path = require('path')
module.exports = {
mode: 'development',
entry: './sum.js',
output: {
filename: 'sum.umd.js',
libraryTarget: 'umd',
library: 'UMDSUM',
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
},
]
},
devtool: 'source-map'
};

执行npx webpack打包结果如下:

(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["UMDSUM"] = factory();
else
root["UMDSUM"] = factory();
})(window, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./sum.js");
/******/ })
/************************************************************************/
/******/ ({ /***/ "./sum.js":
/*!****************!*\
!*** ./sum.js ***!
\****************/
/*! exports provided: Sum */
/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Sum", function() { return Sum; });
function Sum(a, b) {
return a + b;
} /***/ }) /******/ });
});
//# sourceMappingURL=sum.umd.js.map

参考
Browserify and the Universal Module Definition

So, How About UMD模块-Universal Module Definition的更多相关文章

  1. Javascript模块化编程之CommonJS,AMD,CMD,UMD模块加载规范详解

    JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?     模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问 题进行系 ...

  2. 可能是最详细的UMD模块入门指南

    学习UMD 介绍 这个仓库记录了一些关于javascript UMD模块规范的demo,对我学习UMD规范有了很大帮助,希望也能帮助到你. 回顾 之前也写了几篇关于javascript模块的博客,链接 ...

  3. 前端模块化IIFE,commonjs,AMD,UMD,ES6 Module规范超详细讲解

    目录 为什么前端需要模块化 什么是模块 是什么IIFE 举个栗子 模块化标准 Commonjs 特征 IIFE中的例子用commonjs实现 AMD和RequireJS 如何定义一个模块 如何在入口文 ...

  4. 公共模块定义/草案(Common Module Definition / draft - CMD草案)

    This specification addresses how modules should be written in order to be interoperable in browser-b ...

  5. 【02】AMD、CMD、UMD 模块的写法

    AMD.CMD.UMD 模块的写法 简介 最近几年,我们可以选择的Javascript组件的生态系统一直在稳步增长.虽然陡增的选择范围是极好的,但当组件混合匹配使用时就会出现很尴尬的局面.开发新手们会 ...

  6. Axis2(9):编写Axis2模块(Module)

    Axis2可以通过模块(Module)进行扩展.Axis2模块至少需要有两个类,这两个类分别实现了Module和Handler接口.开发和使用一个Axis2模块的步骤如下: 1. 编写实现Module ...

  7. 多模块项目Module must not contain source root. The root already belongs to module

    多模块项目Module "*" must not contain source root *. The root already belongs to module "* ...

  8. Angular之特性模块 ( Feature Module )

    项目结构 一 创建特性模块,及其包含的组件.服务. ng g module art ng g component art/music ng g component art/dance ng g ser ...

  9. 程序集(Assembly)和模块(Managed Module)

    前言 一直都用集成开发坏境(IDE),一直对模块和程序集的概念理解的不是很直观,因为一Build就把你的单个模块塞进程序集里面去了.当然,对你的编程也不会造成太大的影响.但有些东西你最好还是知道比较好 ...

随机推荐

  1. 关于python深浅拷贝的个人浅见

    起初,关于python的深浅拷贝,总是习惯去用传值传址的方式去考虑,发现总是get不到规律,容易记混. python有着高度自治的内存管理,而不可变对象的内存分配,则是能省则省,就是说,无论用什么拷贝 ...

  2. 我的小OJ

    NCOJ 欢迎大家来瓷瓷.出题哦QwQ 嗯,没了.

  3. logstash filter 处理json

    根据输入的json字段,分别建立索引.循环生成注册log和登录log保存到testlog文件中,结果如下: {"method":"register"," ...

  4. ACM-自学之旅

    分类 知识清单 数据结构 链式前向星 树状数组 线段树 线段树的区间合并 基于ST表格的RMQ 图论 最近公共祖先 树的直径.树的重心与树的点分治 树的最小支配集,最小点覆盖与最大独立集 求无向连通图 ...

  5. Linux路径与Win路径的转换

    cygpath $ cygpath -p "$WinPath" -u LinuxPath $ cygpath -p "$LinuxPath" -w WinPat ...

  6. 075、配置Virtualbox backend(2019-04-22 周一)

    参考https://www.cnblogs.com/CloudMan6/p/7617211.html   Rex-Ray 支持多种backend,本节学习如何配置 virtualbox的backend ...

  7. windows 7 命令修改IP地址

    netsh interface ip set address "本地连接"  static 172.17.15.97 255.255.255.0 172.17.12.1

  8. ASP.NET Web API 2 OData v4教程

    程序数据库格式标准化的开源数据协议 为了增强各种网页应用程序之间的数据兼容性,微软公司启动了一项旨在推广网页程序数据库格式标准化的开源数据协议(OData)计划,于此同时,他们还发 布了一款适用于OD ...

  9. 【汇编语言】DOXBox 0.74 常用debug命令

    1.查看.修改寄存器(r命令) ①-r ②-r  ax(要修改的寄存器) -:m(输入想要改成什么值) 2.查看内存单元(d命令) ①-d 查看128个内存单元内容. ②-d 段地址:偏移地址 查看指 ...

  10. JAVA使用HttpClient时报错:Algorithm constraints check failed on signature algorithm: MD5withRSA

    今天使用httpClient.executeMethod时抛出异常:java.security.cert.CertPathValidatorException: Algorithm constrain ...