1. 程式人生 > 實用技巧 >6-前端模組化

6-前端模組化

一、為什麼需要模組化

1、JavaScript的原始功能

在網頁開發的早期,js 製作作為一種指令碼語言,做一些簡單的表單驗證或動畫實現等,那個時候程式碼很少,直接將程式碼寫在 <script> 標籤中即可:

<script>
  document.getElementById('button').onclick = function(){
      console.log('按鈕發生了點選');
    }
</script>

隨著 ajax 非同步請求的出現,慢慢形成了前後端的分離,客戶端需要完成的事情越來越多,程式碼量也是與日俱增,為了應對程式碼量的劇增,

我們通常會將程式碼組織在多個js檔案中,進行維護,但是這種維護方式,依然不能避免一些災難性的問題;


比如全域性變數同名問題:

// a.js 檔案中,小明定義了一個變數 flag , 值為 true
flag = true

// b.js 檔案中,小紅也定義了一個變數 flag , 值為 false
flag = false

// main.js 檔案中,小明想通過 flag 進行一些判斷,完成後續的事情
if (flag) {
  console.log('小明是個畫家');    // 結果可能會因為flag同名的問題,而出現邏輯錯誤
}


另外,這種程式碼的編寫方式對 js 檔案的依賴順序幾乎是強制性的,當 js 檔案過多,比如有幾十個的時候,弄清楚它們的順序是一件比較麻煩的事情,

而且即使你弄清楚順序了,也不能避免上面出現的這種尷尬問題的發生;

這時,我們可以使用匿名函式來解決方面的重名問題;


2、匿名函式的解決方案

在 a.js 檔案中,我們使用匿名函式:

(function(){
  flag = true
})()

使用匿名函式雖然解決了重名問題,但是如果我們希望在 main.js 檔案中,用到 flag,應該如何處理呢?

顯然,另外一個檔案中不方便使用,因為 flag 是一個區域性變數。

這時,我們可以將需要暴露到外面的變數,使用一個模組作為出口。


3、使用模組作為出口

// 使用模組作為出口
var ModuleA = (function(){
  // 1. 定義一個物件
  var obj = {}
  // 2. 在物件內部新增變數和方法
  obj.flag = true
  obj.myFunc = function(info){
    console.log(info);
  }
  // 3. 將物件返回
  return obj
})()


以上程式碼在匿名函式內部,定義一個物件,給物件新增各種需要暴露到外面的屬性和方法(不需要暴露的直接定義即可),

最後將這個物件返回,並且在外面使用了一個 MoudleA 接受。


接下來,在 main.js 中使用只需要使用屬於自己模組的屬性和方法即可:

// 使用模組
if(ModuleA.flag){
  console.log('小明是個畫家');
}
ModuleA.myFunc('小明是個畫家')
console.log(ModuleA);


這就是模組最基礎的封裝,事實上模組的封裝還有很多高階的話題,幸運的是,前端模組化開發已經有了很多既有的規範,以及對應的實現方案;

常見的模組化規範:

  • CommonJS
  • AMD
  • CMD
  • ES6 Modules


4、瞭解CommonJS

模組化有兩個核心: 匯入、匯出

// CommonJS 匯出
// 在a.js中:
module.exports = {
  flag: true,
  sum(a, b) {
    return a + b
  }
}

// CommonJS 匯入
// 在main.js中:
let {flag,sum} = require('a.js')


5、ES6的export和import

(1)export基本使用
export指令用於匯出變數,比如下面的程式碼:

// info.js
export let name = 'lyy'
export let age = '18'
export let height = '1.75'

上面的程式碼還有另外一種寫法:

// info.js
let name = 'lyy'
let age = '18'
let height = '1.75'
export {name, age, height}


(2)匯出函式或類

上面我們主要是輸出變數,也可以輸出函式或者輸出類

上面的程式碼也可以寫成這種形式:


(3)export default

某些情況下,一個模組中包含某個的功能,我們並不希望給這個功能命名,而且讓匯入者可以自己來命名

這個時候就可以使用 export default

// info.js
export default function(){
    conse.log('default function')
}

來到 main.js 中,這樣使用就可以了:

import myFunc from './info.js'
myFunc()


這裡的 myFunc 是自己命名的,可以根據需要命名它對應的名字

另外,需要注意:

export default 在同一個模組中,不允許同時存在多個


(4)import使用

我們使用 export 指令匯出了模組對外提供的介面,就可以通過 import 命令來載入對應的這個模組了

首先,需要在HTML程式碼中引入兩個 js 檔案,並且型別需要設定為 module:

<script src="info.js" type="module"></script>
<script src="main.js" type="module"></script>


import指令用於匯入模組中的內容,比如main.js的程式碼:

import {name,age,height} from "./info.js"


如果我們希望某個模組中所有的資訊都匯入,一個個匯入顯然有些麻煩,則可以匯入模組中所有的 export 變數,

但是通常情況下我們需要給起一個別名,方便後續的使用:

import * as info from "./info.js"
console.log(info.name,info.age,info.height)