自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JS模塊化之JavaScript模塊化方案總結

開發(fā) 前端
本文包含兩部分,第一部分通過簡明的描述介紹什么是 CommonJS、AMD、CMD、UMD、ES Module 以及它們的常見用法,第二部分則根據(jù)實際問題指出在正常的 webpack 構建過程中該如何指定打包配置中的模塊化參數(shù)。

 JS模塊化之JavaScript 模塊化方案總結

 

 

本文包含兩部分,第一部分通過簡明的描述介紹什么是 CommonJS、AMD、CMD、UMD、ES Module 以及它們的常見用法,第二部分則根據(jù)實際問題指出在正常的 webpack 構建過程中該如何指定打包配置中的模塊化參數(shù)。

JavaScript 模塊化方案

模塊化這個話題在 ES6 之前是不存在的,因此這也被詬病為早期 JavaScript 開發(fā)全局污染依賴管理混亂問題的源頭。這類歷史淵源和發(fā)展概述在本文將不會提及,因此感興趣可以自行搜索 JavaScript 發(fā)展史進行了解。

直接進入正題,我們來看看常見的模塊化方案都有哪些以及他們都有哪些內(nèi)容。

CommonJS

CommonJS 的一個模塊就是一個腳本文件,通過執(zhí)行該文件來加載模塊。CommonJS 規(guī)范規(guī)定,每個模塊內(nèi)部,module 變量代表當前模塊。這個變量是一個對象,它的 exports 屬性(即 module.exports)是對外的接口。加載某個模塊,其實是加載該模塊的 module.exports 屬性。

我們見過這樣的模塊引用

  1. var myModule = require('module'); 
  2. myModule.sayHello(); 

這是因為我們把模塊的方法定義在了模塊的屬性上:

  1. // module.js 
  2. module.exports.sayHello = function() { 
  3.  console.log('Hello '); 
  4. }; 
  5. // 如果這樣寫 
  6. module.exports = sayHello; 
  7. // 調(diào)用則需要改為 
  8. var sayHello = require('module'); 
  9. sayHello(); 

require 命令第一次加載該腳本時就會執(zhí)行整個腳本,然后在內(nèi)存中生成一個對象(模塊可以多次加載,但是在第一次加載時才會運行,結果被緩存),這個結果長成這樣:

  1.  id: '...'
  2.  exports: { ... }, 
  3.  loaded: true
  4.  ... 

Node.js 的模塊機制實現(xiàn)就是參照了 CommonJS 的標準。但是 Node.js 額外做了一件事,即為每個模塊提供了一個 exports 變量,以指向 module.exports,這相當于在每個模塊最開始,寫有這么一行代碼:

  1. var exports = module.exports; 

CommonJS 模塊的特點:

  • 所有代碼都運行在模塊作用域,不會污染全局作用域。
  • 獨立性是模塊的重要特點就,模塊內(nèi)部最好不與程序的其他部分直接交互。
  • 模塊可以多次加載,但是只會在第一次加載時運行一次,然后運行結果就被緩存了,以后再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
  • 模塊加載的順序,按照其在代碼中出現(xiàn)的順序。

AMD

CommonJS 規(guī)范很好,但是不適用于瀏覽器環(huán)境,于是有了 AMD 和 CMD 兩種方案。AMD 全稱 Asynchronous Module Definition,即異步模塊定義。它采用異步方式加載模塊,模塊的加載不影響它后面語句的運行。所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中,等到加載完成之后,這個回調(diào)函數(shù)才會運行。除了和 CommonJS 同步加載方式不同之外,AMD 在模塊的定義與引用上也有所不同。

  1. define(id?, dependencies?, factory); 

AMD 的模塊引入由 define 方法來定義,在 define API 中:

  • id:模塊名稱,或者模塊加載器請求的指定腳本的名字;
  • dependencies:是個定義中模塊所依賴模塊的數(shù)組,默認為 [“require”, “exports”, “module”],舉個例子比較好理解,當我們創(chuàng)建一個名為 “alpha” 的模塊,使用了require,exports,和名為 “beta” 的模塊,需要如下書寫(示例1);
  • factory:為模塊初始化要執(zhí)行的函數(shù)或對象。如果為函數(shù),它應該只被執(zhí)行一次。如果是對象,此對象應該為模塊的輸出值;
  1. // 示例1 
  2. define("alpha", ["require""exports""beta"], function (require, exports, beta) { 
  3.  exports.verb = function() { 
  4.  return beta.verb(); 
  5.  // 或者 
  6.  return require("beta").verb(); 
  7.  } 
  8. }); 

如果模塊定義不存在依賴,那么可以直接定義對象:

  1. define({ 
  2.  addfunction(x, y){ 
  3.  return x + y; 
  4.  } 
  5. }); 

而使用時我們依舊通過 require 關鍵字,它包含兩個參數(shù),第一個數(shù)組為要加載的模塊,第二個參數(shù)為回調(diào)函數(shù):

  1. require([module], callback); 

舉個例子:

  1. require(['math'], function (math) { 
  2.  math.add(2, 3); 
  3. }); 

CMD

CMD 全稱為 Common Module Definition,是 Sea.js 所推廣的一個模塊化方案的輸出。在 CMD define 的入?yún)⒅?,雖然也支持包含 id, deps 以及 factory 三個參數(shù)的形式,但推薦的是接受 factory 一個入?yún)?,然后在入?yún)?zhí)行時,填入三個參數(shù) require、exports 和 module:

  1. define(function(require, exports, module) { 
  2.  var a = require('./a'); 
  3.  a.doSomething(); 
  4.  var b = require('./b');  
  5.  b.doSomething(); 
  6.  ... 
  7. }) 

通過執(zhí)行該構造方法,可以得到模塊向外提供的接口。在與 AMD 比較上存在兩個主要的不同點(來自玉伯回答):

  1. 對于依賴的模塊,AMD 是提前執(zhí)行,CMD 是延遲執(zhí)行。不過 RequireJS 從 2.0 開始,也改成可以延遲執(zhí)行(根據(jù)寫法不同,處理方式不同)。CMD 推崇 as lazy as possible.
  2. CMD 推崇依賴就近,AMD 推崇依賴前置。

如果說的不清楚,那么我們直接看上面的代碼用 AMD 該怎么寫:

  1. define(['./a''./b'], function(a, b) {  
  2.  a.doSomething(); 
  3.  b.doSomething(); 
  4.  ... 
  5. }) 

UMD

UMD,全稱 Universal Module Definition,即通用模塊規(guī)范。既然 CommonJs 和 AMD 風格一樣流行,那么需要一個可以統(tǒng)一瀏覽器端以及非瀏覽器端的模塊化方案的規(guī)范。

直接來看看官方給出的 jQuery 模塊如何用 UMD 定義的代碼:

  1. (function (factory) { 
  2.  if (typeof define === 'function' && define.amd) { 
  3.  // AMD. Register as an anonymous module. 
  4.  define(['jquery'], factory); 
  5.  } else if (typeof module === 'object' && module.exports) { 
  6.  // Node/CommonJS 
  7.  module.exports = function( root, jQuery ) { 
  8.  if ( jQuery === undefined ) { 
  9.  // require('jQuery'returns a factory that requires window to 
  10.  // build a jQuery instance, we normalize how we use modules 
  11.  // that require this pattern but the window provided is a noop 
  12.  // if it's defined (how jquery works) 
  13.  if ( typeof window !== 'undefined' ) { 
  14.  jQuery = require('jquery'); 
  15.  } 
  16.  else { 
  17.  jQuery = require('jquery')(root); 
  18.  } 
  19.  } 
  20.  factory(jQuery); 
  21.  return jQuery; 
  22.  }; 
  23.  } else { 
  24.  // Browser globals 
  25.  factory(jQuery); 
  26.  } 
  27. }(function ($) { 
  28.  $.fn.jqueryPlugin = function () { return true; }; 
  29. })); 

UMD的實現(xiàn)很簡單:

  • 先判斷是否支持 AMD(define 是否存在),存在則使用 AMD 方式加載模塊;
  • 再判斷是否支持 Node.js 模塊格式(exports 是否存在),存在則使用 Node.js 模塊格式;
  • 前兩個都不存在,則將模塊公開到全局(window 或 global);

ES Modules

當然,以上說的種種都是社區(qū)提供的方案,歷史上,JavaScript 一直沒有模塊系統(tǒng),直到 ES6 在語言標準的層面上,實現(xiàn)了它。其設計思想是盡量的靜態(tài)化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時確定這些東西。比如,CommonJS 模塊就是對象,輸入時必須查找對象屬性。而 ES Modules 不是對象,而是通過 export 命令顯式指定輸出的代碼。

ES Modules 的模塊化能力由 export 和 import 組成,export 命令用于規(guī)定模塊的對外接口,import 命令用于輸入其他模塊提供的功能。我們可以這樣定義一個模塊:

  1. // 第一種方式 
  2. export var firstName = 'Michael'
  3. export var lastName = 'Jackson'
  4. export var year = 1958; 
  5. // 第二種方式 
  6. var firstName = 'Michael'
  7. var lastName = 'Jackson'
  8. var year = 1958; 
  9. export { firstName, lastName, year }; 

然后再這樣引入他們:

  1. import { firstName, lastName, year } from 'module'
  2. import { firstName as newName } from 'module'
  3. import * as moduleA from 'module'

除以上兩種命令外,還有一個 export default 命令用于指定模塊的默認輸出(一個模塊只能有一個默認輸出)。如果使用了 export default 語法,在 import 時則可以任意命名。由于 export default 命令的本質是將后面的值,賦給 default 變量,所以也可以直接將一個值寫在 export default 之后。當然,引用方式也存在多種:

  1. import { default as foo } from 'module'
  2. import foo from 'module'

需要注意的是 Modules 會自動采用嚴格模式,且 import 命令具有提升效果,會提升到整個模塊的頭部,首先執(zhí)行。

延伸閱讀 JavaScript 模塊的循環(huán)加載

webpack 打包輸出配置

說完理論,來看看實際項目中遇到的問題。當我們開發(fā)完一個 JavaScript 模塊必然要經(jīng)歷打包的流程,而在 webpack 配置中,通過指定 output 選項就可以告訴 webpack 如何輸出 bundle, asset 以及其他載入的內(nèi)容。那么如何實現(xiàn)不同環(huán)境可兼容的構建呢?

  • import:通過 ES Modules 規(guī)范語法進入引入;
  • 變量:作為一個全局變量,比如通過 script 標簽來訪問;
  • this:通過 this 對象訪問;
  • window:在瀏覽器中通過 window 對象訪問;
  • UMD:在 AMD 或 CommonJS 通過 require 引入后訪問;

output 中有一個屬性叫做 libraryTarget,被用來指定如何暴露你的模塊的屬性。你可以這樣嘗試賦值給一個變量或者指定對象的屬性:

  1. // 加載完成后將模塊賦值給一個指定變量(默認值) 
  2.  libraryTarget: 'var'
  3.  ... 
  4. // 賦值為指定對象的一個屬性,比如 `this` 或者 `window` 
  5.  libraryTarget: "this"
  6.  // libraryTarget: "window"
  7.  ... 
  8. // 同樣的,若是指定 commonjs,那么便可以將模塊分配給 exports,這也意味著可以用于 CommonJS 環(huán)境: 
  9.  libraryTarget: "commonjs"
  10.  ... 

如果需要更完整的模塊化 bundle,以確保和各模塊系統(tǒng)兼容,那么可以這樣嘗試:

  1. // 內(nèi)容分配給 module.exports 對象,用于 CommonJS 環(huán)境 
  2.  libraryTarget: 'commonjs2'
  3.  ... 
  4. // 暴露為 AMD 模塊,通過特定屬性引入 
  5.  libraryTarget: 'amd'
  6.  ... 
  7. // 所有模塊系統(tǒng)兼容的萬金油,可以在 CommonJS, AMD 環(huán)境下運行,或將模塊導出到 global 下的變量 
  8.  libraryTarget: 'umd'
  9.  ... 

因此,如果只看 output 內(nèi)容,那么我的一個 webpack 生產(chǎn)環(huán)境配置可以寫成這樣:

  1. module.exports = { 
  2.  output: { 
  3.  // webpack 如何輸出結果的相關選項 
  4.  path: path.resolve(__dirname, "dist"), 
  5.  filename: 'index.js'
  6.  library: 'hijiangtao'
  7.  umdNamedDefine: true
  8.  libraryTarget: 'umd'
  9.  }, 

 

責任編輯:龐桂玉 來源: 今日頭條
相關推薦

2016-10-09 11:03:41

Javascript模塊化Web

2012-11-08 09:45:44

JavaScriptrequireJS

2015-11-23 09:50:15

JavaScript模塊化SeaJs

2020-05-12 08:39:50

JavaScript工具技術

2013-08-20 15:31:18

前端模塊化

2017-05-18 10:23:55

模塊化開發(fā)RequireJsJavascript

2020-09-17 10:30:21

前端模塊化組件

2015-10-10 11:29:45

Java模塊化系統(tǒng)初探

2020-09-18 09:02:32

前端模塊化

2022-03-11 13:01:27

前端模塊

2017-04-10 14:23:01

typescriptjavascriptwebpack

2009-12-10 11:04:08

Java模塊化OSGiJigsaw

2013-08-20 18:39:34

JavaScript模requireJS

2009-08-17 10:11:12

C# Windows

2018-03-21 21:31:28

Java9編程Java

2022-09-05 09:01:13

前端模塊化

2012-11-08 10:21:41

JSrequireJavaScript

2018-06-21 09:36:09

模塊化數(shù)據(jù)中心集中化

2019-09-02 10:51:59

Python腳本語言程序員

2017-05-18 11:43:41

Android模塊化軟件
點贊
收藏

51CTO技術棧公眾號