四. 前端模組化
1. 為什麼需要模組化
1.1 JavaScript原始功能
在網頁開發的早期js製作作為一種指令碼語言,做一些簡單的表單驗證或動畫實現等,那個時候程式碼還是很少的。那個時候的程式碼是怎麼寫的呢?直接將程式碼寫在 <script>
標籤中即可。
隨著ajax非同步請求的出現慢慢形成了前後端的分離,客戶端需要完成的事情越來越多程式碼量也是與日俱增。為了應對程式碼量的劇增,我們通常會將程式碼組織在多個js檔案中進行維護。
但是這種維護方式依然不能避免一些災難性的問題,比如全域性變數同名問題如下面的案例。另外這種程式碼的編寫方式對js檔案的依賴順序幾乎是強制性的,當js檔案過多比如有幾十個的時候弄清楚它們的順序是一件比較同時的事情,而且即使你弄清楚順序了也不能避免下面案例出現的這種尷尬問題的發生。
1.2 匿名函式的解決方案
我們可以使用匿名函式來解決方面的重名問題,在aaa.js檔案中我們使用匿名函式。
(function(){
let flag = true
})()
但是如果我們希望在main.js檔案中用到flag應該如何處理呢?顯然另外一個檔案中不容易使用,因為flag是一個區域性變數。
1.3 使用模組作為出口
我們可以使用將需要暴露到外面的變數,使用一個模組作為出口是什麼意思呢?
來看下對應的程式碼:我們做了什麼事情呢?
- 非常簡單,在匿名函式內部定義一個物件。
- 給物件新增各種需要暴露到外面的屬性和方法(不需要暴露的直接定義即可)。
- 最後將這個物件返回,並且在外面使用了一個MoudleA接受。
var ModuleA = (function() {
//1.定義一個物件
var obj = {};
//2.在物件內部新增變數和方法
obj.flag = true;
obj.myFunc = function(info) {
console.log(info);
}
//3.將物件返回
return obj;
})
接下來我們在man.js中怎麼使用呢?我們只需要使用屬於自己模組的屬性和方法即可
if(ModuleA.flag) { console.log('小明是個天才!'); } ModuleA.myFunc('小明長的真帥!'); console.log(ModuleA);
這就是模組最基礎的封裝,事實上模組的封裝還有很多高階的話題。但是我們這裡就是要認識一下為什麼需要模組以及模組的原始雛形。
幸運的是,前端模組化開發已經有了很多既有的規範以及對應的實現方案。常見的模組化規範:CommonJS
、AMD
、CMD
,也有 ES6 的 Modules
。
2. 模組化開發相關規範
2.1 CommonJs
模組化有兩個核心:匯出和匯入
CommonJS的匯出
module.exports = {
flag: true,
test(a,b) {
return a + b;
},
demo(a,b) {
return a * b;
}
}
CommonJS的匯入
//CommonJS模組
let {test, demo, flag} = require('moduleA');
//等同於
let _mA = require('moduleA');
let test = _mA.test;
let demo = _mA.demo;
let flag = _mA.flag;
2.2 ES6的export指令
export指令用於匯出變數
//info.js
export let name = 'why';
exprot let age = 18;
export let height = 1.88;
還有另一種寫法
//info.js
let name = 'why';
let age = 18;
let height = 1.88;
export {name, age, height};
匯出函式或類
上面我們主要是輸出變數,也可以輸出函式或者輸出類
export function test(content) {
console.log(content);
}
export class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
run() {
console.log(this.name + '在奔跑')
}
}
function test(content) {
console.log(content);
}
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
run() {
console.log(this.name + '在奔跑')
}
}
exprot {test, Person}
export default
某些情況下一個模組中包含某個功能,我們並不希望給這個功能命名,而是讓匯入者可以自己來命名
這個時候就可以使用 export default
。
//info.js
export default function () {
console.log('default function');
}
我們來到main.js中這樣使用就可以了。這裡的myFunc是我自己命名的,你可以根據需要命名它對應的名字
import myFunc from './info.js'
myFunc();
注意:
export default
在同一個模組中不允許同時存在多個。
2.3 ES6的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'
console.log(name, age, height);
如果我們希望某個模組中所有的資訊都匯入,一個個匯入顯然有些麻煩。通過 *
可以匯入模組中所有的export變數。但是通常情況下我們需要給 *
起一個別名方便後續的使用
import * as info from './info.js'
console.log(info.name, info.age, info.height, info.friends);