在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