這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
一、介紹
模組,(Module),是能夠單獨命名並獨立地完成一定功能的程式語句的集合(即程式程式碼和資料結構的集合體)。
兩個基本的特徵:外部特徵和內部特徵
-
外部特徵是指模組跟外部環境聯絡的介面(即其他模組或程式呼叫該模組的方式,包括有輸入輸出引數、引用的全域性變數)和模組的功能
-
內部特徵是指模組的內部環境具有的特點(即該模組的區域性資料和程式程式碼)
為什麼需要模組化
- 程式碼抽象
- 程式碼封裝
- 程式碼複用
- 依賴管理
如果沒有模組化,我們程式碼會怎樣?
- 變數和方法不容易維護,容易汙染全域性作用域
- 載入資源的方式透過script標籤從上到下。
- 依賴的環境主觀邏輯偏重,程式碼較多就會比較複雜。
- 大型專案資源難以維護,特別是多人合作的情況下,資源的引入會讓人奔潰
因此,需要一種將JavaScript
程式模組化的機制,如
- CommonJs (典型代表:node.js早期)
- AMD (典型代表:require.js)
- CMD (典型代表:sea.js)
AMD
Asynchronous ModuleDefinition
(AMD),非同步模組定義,採用非同步方式載入模組。所有依賴模組的語句,都定義在一個回撥函式中,等到模組載入完成之後,這個回撥函式才會執行
代表庫為require.js
/** main.js 入口檔案/主模組 **/ // 首先用config()指定各模組路徑和引用名 require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", //實際路徑為js/lib/jquery.min.js "underscore": "underscore.min", } }); // 執行基本操作 require(["jquery","underscore"],function($,_){ // some code here });
CommonJs
CommonJS
是一套 Javascript
模組規範,用於服務端
// a.js module.exports={ foo , bar} // b.js const { foo,bar } = require('./a.js')
其有如下特點:
- 所有程式碼都執行在模組作用域,不會汙染全域性作用域
- 模組是同步載入的,即只有載入完成,才能執行後面的操作
- 模組在首次執行後就會快取,再次載入只返回快取結果,如果想要再次執行,可清除快取
require
返回的值是被輸出的值的複製,模組內部的變化也不會影響這個值
既然存在了AMD
以及CommonJs
機制,ES6
的Module
又有什麼不一樣?
ES6 在語言標準的層面上,實現了Module
,即模組功能,完全可以取代 CommonJS
和 AMD
規範,成為瀏覽器和伺服器通用的模組解決方案
CommonJS
和AMD
模組,都只能在執行時確定這些東西。比如,CommonJS
模組就是物件,輸入時必須查詢物件屬性
// CommonJS模組 let { stat, exists, readfile } = require('fs'); // 等同於 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile;
ES6
設計思想是儘量的靜態化,使得編譯時就能確定模組的依賴關係,以及輸入和輸出的變數
// ES6模組 import { stat, exists, readFile } from 'fs';
述程式碼,只載入3個方法,其他方法不載入,即 ES6
可以在編譯時就完成模組載入
由於編譯載入,使得靜態分析成為可能。包括現在流行的typeScript
也是依靠靜態分析實現功能
二、使用
ES6
模組內部自動採用了嚴格模式,這裡就不展開嚴格模式的限制,畢竟這是ES5
之前就已經規定好
模組功能主要由兩個命令構成:
export
:用於規定模組的對外介面import
:用於輸入其他模組提供的功能
export
一個模組就是一個獨立的檔案,該檔案內部的所有變數,外部無法獲取。如果你希望外部能夠讀取模組內部的某個變數,就必須使用export
關鍵字輸出該變數
// profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export var year = 1958; 或 // 建議使用下面寫法,這樣能瞬間確定輸出了哪些變數 var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export { firstName, lastName, year };
輸出函式或類
export function multiply(x, y) { return x * y; };
透過as
可以進行輸出變數的重新命名
function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion };
import
使用export
命令定義了模組的對外介面以後,其他 JS 檔案就可以透過import
命令載入這個模組
// main.js import { firstName, lastName, year } from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; }
同樣如果想要輸入變數起別名,透過as
關鍵字
import { lastName as surname } from './profile.js';
當載入整個模組的時候,需要用到星號*
// circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; } // main.js import * as circle from './circle'; console.log(circle) // {area:area,circumference:circumference}
輸入的變數都是隻讀的,不允許修改,但是如果是物件,允許修改屬性
import {a} from './xxx.js' a.foo = 'hello'; // 合法操作 a = {}; // Syntax Error : 'a' is read-only;
不過建議即使能修改,但我們不建議。因為修改之後,我們很難差錯
import
後面我們常接著from
關鍵字,from
指定模組檔案的位置,可以是相對路徑,也可以是絕對路徑
import { a } from './a';
如果只有一個模組名,需要有配置檔案,告訴引擎模組的位置
import { myMethod } from 'util';
在編譯階段,import
會提升到整個模組的頭部,首先執行
foo(); import { foo } from 'my_module';
多次重複執行同樣的匯入,只會執行一次
import 'lodash'; import 'lodash';
上面的情況,大家都能看到使用者在匯入模組的時候,需要知道載入的變數名和函式,否則無法載入
如果不需要知道變數名或函式就完成載入,就要用到export default
命令,為模組指定預設輸出
// export-default.js export default function () { console.log('foo'); }
載入該模組的時候,import
命令可以為該函式指定任意名字
// import-default.js import customName from './export-default'; customName(); // 'foo'
動態載入
允許您僅在需要時動態載入模組,而不必預先載入所有模組,這存在明顯的效能優勢
這個新功能允許您將import()
作為函式呼叫,將其作為引數傳遞給模組的路徑。 它返回一個 promise
,它用一個模組物件來實現,讓你可以訪問該物件的匯出
import('/modules/myModule.mjs') .then((module) => { // Do something with the module. });
複合寫法
如果在一個模組之中,先輸入後輸出同一個模組,import
語句可以與export
語句寫在一起
export { foo, bar } from 'my_module'; // 可以簡單理解為 import { foo, bar } from 'my_module'; export { foo, bar };
同理能夠搭配as
、*
搭配使用
三、使用場景
如今,ES6
模組化已經深入我們日常專案開發中,像vue
、react
專案搭建專案,元件化開發處處可見,其也是依賴模組化實現
vue
元件
<template> <div class="App"> 元件化開發 ---- 模組化 </div> </template> <script> export default { name: 'HelloWorld', props: { msg: String } } </script>
react
元件
function App() { return ( <div className="App"> 元件化開發 ---- 模組化 </div> ); } export default App;
包括完成一些複雜應用的時候,我們也可以拆分成各個模組
參考文獻
- https://macsalvation.net/the-history-of-js-module/
- https://es6.ruanyifeng.com/#docs/module