在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. 将CSV文件存为HTML文件形式

    # -*- coding: utf-8 -*- """ Created on Mon Apr 29 09:24:04 2019 @author: history &quo ...

  2. vue-cli 项目踩坑 npm install 时出错

    1.报错如下: 2.此时你执行npm run dev  / npm run build 会报错如下 npm ERR! code ELIFECYCLEnpm ERR! errno 1npm ERR! v ...

  3. 使用vcastr22.swf做flash版网页视频播放器

    flash的安装设置参考  Flash设置(各种版本浏览器包括低版本IE) 百度搜索下载vcastr22.swf文件 然后使用方式很简单,浏览器安装flash相关插件就能看了 视频路径主要在这里,视频 ...

  4. 基于jeesite的cms系统(三):使用RESTful API在前端渲染数据

    使用RESTful API可以更好的开发前后分离的应用,后面一节会介绍使用模版引擎Beetl开发后端渲染的应用. 一.配置Swagger(Api 接口文档) 1.使用系统自带 拷贝jeesite-mo ...

  5. nginx 容器反向代理网址的设置

    先讲一下场景:  nginx 容器要和SpringBoot 容器部署在一台机器上, nginx 为 SpringBoot 提供反向代理, 需要在 nginx.conf 中写上 SpringBoot 的 ...

  6. nodeJS模块寻址规则

    引子 阮一峰一则教程中, 将应用放置在  npm 模块安装目录同等级的目录(https://github.com/ruanyf/webpack-demos)下. 但是应用目录文件中, 引用标准库的使用 ...

  7. PHP代码审计之XSS操作

    XSS XSS是Cross Site Scripting(跨站脚本攻击), 它与sql注入攻击类似,SQL注入攻击中以SQL语句作为用户输入,从而达到查询/修改/删除的目的,而在XSS攻击中,通过插入 ...

  8. XSS绕过<>进行测试

    大家都知道,普遍的防御XSS攻击的方法是在后台对以下字符进行转义:<.>.’.”,但是经过本人的研究发现,在一些特殊场景下,即使对以上字符进行了转义,还是可以执行XSS攻击的. 首先看一个 ...

  9. js手写'Promise'

    /* * pending:初始化成功 * fulfilled:成功 * rejected:失败 * */ function Promise(executor) {// 执行器 this.status ...

  10. Groovy闭包详解

    Groovy闭包是一种可执行代码块的方法,闭包也是对象,可以向方法一样传递参数,因为闭包也是对象,因此可以在需要的时候执行,像方法一样闭包可以传递一个或多个参数.闭包最常见的用途就是处理集合,可以遍历 ...