zoukankan      html  css  js  c++  java
  • So, How About UMD模块-Universal Module Definition

    在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

  • 相关阅读:
    使用BeanShell 对比取出来的值
    https 请求的端口是443 注意
    Jmeter录制App 请求是HTTPS的
    Charles-断点
    随手记--分配事件概率
    看日志有没有 出现错误的字段 (如 crash ) 查找app闪退
    学习的网站
    xss 攻击 sql 注入
    app的apk 安装的方法--adb--命令安装 (含把apk放某个文件夹,每次启动自己安装)
    把2列相加的方法
  • 原文地址:https://www.cnblogs.com/star91/p/So-How-About-UMD-mo-kuaiUniversal-Module-Definitio.html
Copyright © 2011-2022 走看看