JavaScript 模塊化詳解

前言:

1.概念

  • 將一個復雜的程序依據一定的規則(規范)封裝成幾個塊(文件), 並進行組合在一起;
  • 塊的內部數據與實現是私有的, 隻是向外部暴露一些接口(方法)與外部其它模塊通信。

2.模塊化的好處

  • 避免命名沖突(減少命名空間污染);
  • 更好的分離, 按需加載;
  • 更高復用性;
  • 更高可維護性。

3.引入多個script標簽後出現的問題

  • 請求過多(依賴的模塊過多,請求就會過多);
  • 依賴模糊(不知道模塊的具體依賴關系,導致加載順序出錯);
  • 難以維護(以上兩個原因就會造成這個結果)。
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="jQuery.js"></script>
  <script src="module.js"></script>
</head>
<body>
  <div>123</div>
</body>
<script>
  myModule.foo();
  myModule.bar();
  console.log(myModule.data) ;
  myModule.data = 'xxxx';
  myModule.foo();
</script>
</html>


//module.js IIFE(匿名函數自調用)
;(function(window,$){
  let data = "www.baidu.com";
  function foo() {
    console.log(`foo() ${data}`);
    //這裡需要使用jQuery庫
    $('body').css('background', 'red')
  }
  function bar() {
    console.log(`bar() ${data}`);
    otherFun();
  }
  function otherFun() {
    console.log(`otherFun()`);
  }
  window.myModule = { foo, bar };
})(window, jQuery)


一、CommonJS

  • NODE 就是基於commonJS模塊規范,每一個文件就是一個模塊;有自己的作用域;在服務器端,模塊的加載是同步的;在瀏覽器端,模塊需提前編譯打包處理

特點:

  • 所有代碼都運行在模塊作用域,不會污染全局作用域;
  • 模塊可以多次加載,但是隻會在第一次加載時運行一次,然後運行結果就被緩存瞭,以後再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
  • 模塊加載的順序,按照其在代碼中出現的順序。

語法:

  • 暴露模塊:js module.exports = value 或者 js exports.xxx = value
  • 引入模塊:js require('xxx') 如果是第三方模塊,xxx為模塊名;如果是自定義模塊,xxx為模塊文件路徑

CommonJS規范規定,每個模塊內部,module變量代表當前模塊。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,其實是加載該模塊的module.exports屬性。

require命令用於加載模塊文件。require命令的基本功能是,讀入並執行一個JavaScript文件,然後返回該模塊的exports對象。如果沒有發現指定模塊,會報錯。

CommonJS模塊的加載機制是,輸入的是被輸出的值的拷貝。也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值

二、AMD

  • 相比於CommonJS的同步加載模塊,AMD更適合於瀏覽器端的非同步模塊加載,因為AMD允許指定回調函數
  • 目錄結構

使用require.js

<!-- index.html -->
<script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"></script>


//定義一個沒有依賴的module1模塊
define('module1', () => {
  let count = 0;
  const add = () => ++ count;
  const reset = () => count = 0;
  const upperCase = string => string.toUpperCase()

  return {
    add,
    reset,
    upperCase
  }
})


//定義一個有依賴的module2模塊,依賴module1
define('module2',['module1'], (module1) => {
  const showMsg = () => module1.upperCase('hello-amd');

  return {
    showMsg
  }
})


<!-- 在html文件中使用模塊 -->
<body>

<script>
  require.config({
    paths: {
      module1: './modules/module1',
      module2: './modules/module2'
    }
  })
  require(['module1', 'module2'], (module1, module2) => {
    console.log(module1.add()) // 1
    console.log(module1.reset()) //0
    console.log(module2.showMsg()) //HELLO-AMD
  })
</script>
</body>


三、CMD

  • CMD集CommonJS與AMD的優點於一身,cmd 規范專門用於瀏覽器端,模塊的加載是異步的,模塊使用時才會加載執行(實現瞭按需加載,而AMD是不支持按需加載的)
  • 目錄結構

使用sea.js

<script src="https://cdn.bootcdn.net/ajax/libs/seajs/3.0.3/sea.js"></script>


//定義模塊module1
define((require, exports, module) => {

  let string = 'I am a string';
  const readString = () => 'module1 show() ' + string;

  //向外暴露
  exports.readString = readString;
})


//定義模塊module2
define((require, exports, module) => {
  exports.msg = "正是在下啊"
})


//定義模塊module
define((require, exports, module) => {
  //引入依賴模塊(同步)
  var module1 = require('./module1');
  console.log(module1.readString()) // module1 show() I am a string

  //引入依賴模塊(異步)
  require.async('./module2', md2 => {
    console.log(`這是異步引入的:${md2.msg}`) //這是異步引入的:正是在下啊
  })
})


<!-- html文件使用module -->
<body>
<script>
  seajs.use('./modules/module')
</script>


四、ES6模塊化

ES6 模塊的設計思想是盡量的靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都隻能在運行時確定這些東西。
兩個關鍵字 import和export

  • import 導入
  • export 導出
//mian.js
export default {
  showMsg() {
    console.log('hahahahahah')
  }
}
export const msg = "正是花好月圓時!"

//index.js
import module1 from "./module1"; //對應export default
module1.showMsg()
import { msg } from './module1'; //對應export
console.log(msg)

/*tips: 不要在html裡使用<script type="module">
import ....., 有跨域問題,可以在vscode裡下載一個插件,或者本地起服務都可以解決,就不贅述瞭。
</script>*/

到此這篇關於JavaScript 模塊化詳解的文章就介紹到這瞭,更多相關JavaScript 模塊化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: