一次搞懂-JavaScript模塊化詳解

JavaScript-模塊 _1_.png

模塊化的意義

將代碼拆分紅獨立的塊,而後再把這些塊使用模塊模式鏈接起來實現不一樣的功能。javascript

就像小時候玩的拼圖同樣,不一樣的拼圖組合在一塊兒就能夠拼成任意的形狀。php

這種模式的背後思想也很簡單:把邏輯分塊、各自封裝,相互獨立,同時自行決定引入執行那些外部模塊以及暴露自身的那些模塊。html

這個基本的思想是全部的 JavaScript 模塊系統的基礎。前端

文中代碼案例地址:https://github.com/AnsonZnl/JS-Modules-Samplejava

模塊化的好處

  • 避免命名衝突(減小命名空間污染)
  • 更好的分離, 按需加載
  • 更高複用性
  • 高可維護性

JS 中常見的模塊

IIFE 模式:匿名函數自調用(閉包)

主要應用在瀏覽器端。node

利用閉包的原理創造一個獨有的函數做用域來保存私有變量,達到模塊化的效果。jquery

使用git

HTMLes6

<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
  console.log(myModule.get()); // output-data(獲取內部數據)
  myModule.set("new data"); // 設置內部數據
  console.log(myModule.data); //output-undefined (不能訪問模塊內部數據)
  myModule.data = "xxxx"; //不是修改的模塊內部的data
  console.log(myModule.get()); //output-new data 修改後的值
</script>

JSgithub

// module.js文件
(function(window) {
  let data = "data";
  //獲取數據
  function get() {
    return data;
  }
  // 修改數據
  function set(val) {
    data = val;
  }
  //暴露行爲
  window.myModule = {
    get,
    set,
  };
})(window);

CommonJS

主要應用在服務端,若是在瀏覽器端運行須要藉助其餘工具(Browserify)。

暴露模塊: module.exports = value或者exports.xx = value(exports 是一個導出的對象)

引入模塊: require(xx),若是是第三方模塊,xxx 爲模塊名,若是爲自定義模塊,xxx 爲模塊的文件路徑。

特色

  • 全部代碼都運行在模塊做用域,不會污染全局做用域。
  • 模塊能夠屢次加載,可是隻會在第一次加載時運行一次,而後運行結果就被緩存了,之後再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
  • 模塊加載的順序,按照其在代碼中出現的順序。

使用
在 Node 中 安裝 uniq 函數。

npm init
npm install uniq --save
// module.js
let arr = [1, 2, 2, 3, 3];
module.exports = {
  arr,
};
// app.js
let module1 = require("./module.js");
let uniq = require("uniq");

console.log(uniq(module1.arr)); // [1,2,3]

AMD

全稱是 Asynchronous Module Definition - 異步模塊定義

和 CommonJS 不一樣的是 AMD 採用非同步的方式來加載模塊。

基本語法

定義暴露模塊

// 定義沒有依賴的模塊
define(function() {
  return 模塊;
});
// 定義有依賴的模塊
define(["module1", "module2"], function(m1, m2) {
  return 模塊;
});

引入使用模塊

require(["module1", "module2"], function(m1, m2) {
  使用m1 和 m2;
});

使用案例

<!-- index.html -->
<body>
  <!-- 引入require.js並指定js主文件的入口 -->
  <script
    data-main="main"
    src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"
  ></script>
</body>
// main.js
(function() {
  require(["module.js"], function(module) {
    let currentUrl = module.getUrl();
    alert("當前頁面的URl:" + currentUrl);
  });
})();
// module.js
// 定義模塊
define(function() {
  let url = window.location.href;

  function getUrl() {
    return url.toUpperCase();
  }
  // 暴露模塊
  return {
    getUrl,
  };
});

更多的使用方法請參考:https://requirejs.org/

CMD

CMD---是 SeaJS 在推廣過程當中對模塊定義的規範化產出,是一個同步模塊定義,是 SeaJS 的一個標準,SeaJS 是 CMD 概念的一個實現,SeaJS 是淘寶團隊提供的一個模塊開發的 JS 框架。

何時用到何時引入,即用即返回,這是一個同步概念。

特色: CMD 是 AMD 在基礎上改進的一種規範,和 AMD 不一樣在於依賴模塊的執行機制不一樣,CMD 是就近依賴,而 AMD 是前置依賴。

環境: 瀏覽器環境

語法:

  • 導入:define(function(require, exports, module){})
  • 導出:define(function(){return '值'})

使用

// main.js
define(function(require, exports, module) {
  var moduleA = require("./module.js");
  alert(moduleA.a); // 打印出:hello world
});
// module.js
define(function(require, exports, module) {
  exports.a = "hello world";
});
<body>
  <script
    data-main="main"
    src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"
  ></script>
</body>

Sea.js 用法請參考:https://seajs.github.io/seajs...

UMD

全稱 Universal Module Definition 看名字就知道,特色是兼容 AMD 和 CommonJS 規範,並且兼容全局引入。

環境: 服務器環境和瀏覽器端

UMD 實現原理很簡單:

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

使用

(function(root, factory) {
  if (typeof define === "function" && define.amd) {
    //AMD
    define(["jquery"], factory);
  } else if (typeof exports === "object") {
    //Node, CommonJS之類的
    module.exports = factory(require("jquery"));
  } else {
    //瀏覽器全局變量(root 即 window)
    root.returnExports = factory(root.jQuery);
  }
})(this, function($) {
  //方法
  function myFuncA() {} // 私有方法,由於沒有返回
  function myFuncB() {} // 公共方法,由於返回了

  //暴露公共方法
  return {
    myFuncB,
  };
});

你們平時引入的 jQuery 的 CND 就是 UMD 的,源碼能夠查看:https://cdn.bootcdn.net/ajax/...

ES6 Module

在 ES6 以前,模塊化主要是社區在推進進行的,從而出現了 CommonJS 和 AMD 兩個,前者用於服務器後者用於瀏覽器,ES6 模塊的出現將徹底替代 CommonJS 和 AMD 規範,成爲瀏覽器和服務器通用的解決方案。

ES6 模塊的設計思想是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。好比,CommonJS 模塊就是對象,輸入時必須查找對象屬性。

特色

  • 按需加載(編譯時加載)
  • import 和 export 命令只能在模塊的頂層,不能在代碼塊之中(如:if 語句中),import()語句能夠在代碼塊中實現異步動態按需動態加載

環境: 服務器環境和瀏覽器端

語法:

  • 導入:import {modules1,modules1,} from '模塊路徑'
  • 導出:export或者export default
  • 動態導入:import('模塊路徑').then(..)
    <!-- 揺樹(tree-shaking) -->

使用

Node 中 先安裝 Babel:

npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
npm install --save @babel/polyfill
# 而後運行
npx babel-node main.js
// modules/double.js
let mes = "Hello Modules for double";
function sum(value) {
  return `${mes} - ${value * 2}`;
}
export default {
  mes,
  sum,
};
// main.js
import module from "./modules/double";
console.log(module.sum(10)); // Hello Modules for double - 20

瀏覽器中

區別

  • 和 CommonJS 的區別:

    • CommonJS 模塊輸出的是一個值得拷貝,ES6 模塊輸出的是值的引用
    • CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口
    • CommonJS 模塊的 require()是同步加載模塊,ES6 模塊的 import 命令是異步加載,有一個獨立的模塊依賴的解析階段。

缺點
瀏覽器和服務器目前的支持不是很好,現階段使用須要藉助一些工具(Babel)。

  • 瀏覽器支持:在新版本的瀏覽器(如 Chrome)中可使用<script type="module" src="./foo.js"></script>寫法
  • 服務器支持(Node)有兩種模式,分別是 ES6 模塊和 CommonJS。

    • 從 Node.js v13.2 開始,默認支持 ES6 模塊,可是須要採用.mjs爲後綴名、或者在package.json中修改type字段爲module(推薦)
    • 使用 CommonJS 的話須要以.cjs爲後綴,也能夠設置package.json中修改type字段爲commonjs(推薦)。

最好不要二者混用。更多的使用方法能夠參考:https://es6.ruanyifeng.com/#d...

總結

  • CommonJS 規範主要用於服務端編程,加載模塊是同步的,這並不適合在瀏覽器環境,由於同步意味着阻塞加載,瀏覽器資源是異步加載的,所以有了 AMD CMD 解決方案。
  • AMD 規範在瀏覽器環境中異步加載模塊,並且能夠並行加載多個模塊。不過,AMD 規範開發成本高,代碼的閱讀和書寫比較困難,模塊定義方式的語義不暢。
  • CMD 規範與 AMD 規範很類似,都用於瀏覽器編程,依賴就近,延遲執行,能夠很容易在 Node.js 中運行。不過,依賴 SPM 打包,模塊的加載邏輯偏重
  • ES6 在語言標準的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代 CommonJS 和 AMD 規範,成爲瀏覽器和服務器通用的模塊解決方案。

參考

相關文章
相關標籤/搜索