JavaScript
模組化程式設計
模組化設計
問題、解決問題
JQuery 時代
JQuery 時代,只要 $ 的變數在全局的作用域下,就可以在任何處呼叫做使用
方便的同時
也造成下方的問題:
- 載入順序
- 作用域的命名
- 維護代碼變得更複雜
不同代碼間,存在隱形的依賴
無法得知 哪個函數屬於哪個腳本
模組化設計
解決方案:
- 只在需要在需要的時候才載入模組,藉由此方式來建立模組間的依賴關係
- 使用檔案路徑作為屬性的名稱,避免命名空間衝突
- 使用 JavaScrip 函式的作用域來區分彼此模組的作用域
// module_system.js
// 引用模組
function require(path) {
let mod = require.modules[path];
mod.exports = {};
mod.call(window, require, mod, mod.exports);
return mod.exports;
}
// 存放模組位置
require.modules = {};
// 註冊模組
require.register = function (path, fn) {
require.modules[path] = fn;
}
//-----------------------------------
// name.js
require
.register('name.js', function (require, module, exports) {
module.exports = 'shimingw'
})
// hello.js
require
.register('hello.js', function (require, module, exports) {
let name = require('name.js')
exports.hello_name = 'hello ' + name;
exports.hello_world = 'hello world';
})
模組化設計
藉由 module_system 的方法
來 註冊 與 使用 模組
就能輕鬆的解決在使用模組化前
我們所碰到的問題了
模組化設計
var hello = require("hello.js");
console.log(hello.hello_name)
// output 'hello zhiwei'
使用方式
模組化設計
定義、規範
模組共同點
在開發各自的 模組 時,共同的步驟 與 共識
- 定義模組
- 定義依賴的模組
- 模組必須具備耦合性 (可被任何模組引用)
基於 第三點 的 耦合性
如果 模組 是可被通用的話
則一定會有所謂的 規範 或是 標準 存在
協定、標準
如果有稍微好奇過 Javascript模組化 的相關資訊,你會發現關於這個話題還真有不少名詞
-
CommonJS
-
AMD
-
CMD
-
UMD
-
ES6
CommonJS
CommonJS
CommonJs 的核心非常簡單
// moduleA.js
module.exports = moduleA.someFunc;
導出:
// moduleA.js
const moduleA = require('./moduleA')
導入:
常見的環境與套件:
伺服器端
- Common.js
- .....
系統端
- node.js
CommonJS
優點:
操作上不管是 開發 還是 引用 都十分的容易
因為在使用上就只有 require 與 exports 而已
缺點:
CommonJS 是使用 同步 的方式進行載入的
在系統端主機上直接使用這個方式是沒問題的,因為 模組 都在同一個位置上
但在 瀏覽器 上就不是這麼一回事,因為在客戶端,每隻腳本的載入時間是無法控制的
AMD
Asynchronous Module Definition
AMD
前面提到的 CommonJS 在 瀏覽器端 存在 同步 載入的問題,所以只能採用 非同步 載入
這就是 AMD 規範誕生的背景
AMD 是 Asynchronous Module Definition 的縮寫,意思就是 非同步模組定義
AMD
AMD也採用 require() 語句載入模組,但是不同於CommonJS,它要求兩個引數
如下:
require(['module1', 'module2'], function ( module1, module2 ) {
// rest of your code here
module1.doSomething();
});
AMD
看完如何載入後,來看一下要如何 定義 AMD 模組
// Define a module "myModule" with two dependencies, jQuery and Lodash
define("myModule", ["jquery", "lodash"], function($, _) {
// This publicly accessible object is our module
// Here we use an object, but it can be of any type
var myModule = {};
var privateVar = "Nothing outside of this module can see me";
var privateFn = function(param) {
return "Here's what you said: " + param;
};
myModule.version = 1;
myModule.moduleMethod = function() {
// We can still access global variables from here, but it's better
// if we use the passed ones
return privateFn(windowTitle);
};
return myModule;
});
AMD
常見的環境與套件
- require.js
- cmd.js
- ......
UMD
Universal Module Definition
UMD
天啊~這麼還有啊~~
其實最主流的 標準 與 協定
就只有 CommonJS 與 AMD 兩種 而已
前面有說,模組是必須要有 耦合性
為了統一 CommonJS 與 AMD於是多出了UMD
UMD
// if the module has no dependencies, the above pattern can be simplified to
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.returnExports = factory();
}
}(this, function () {
// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
return {};
}));
其實 UMD 在代碼上,只是多了個 條件式 進行判斷,
判斷當前使用模組的方式是 CommonJS 還是 AMD
amd + commonjs + 全局变量
ES6 Module
ES6 Module
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 引用模組 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
定義模組:
引用模組:
ES6 Module
// module.js
export default 123
<!-- index.html -->
<script type="module">
import module from './module.js'
console.log(module) // 123
</script>
<!-- index.html -->
<script type="module" src="index.js"></script>
// 控制台 123
在 HTML 上使用:
module.js 檔案
ES6 Module
特性:
- 使用 import 被導入的 模組 在 嚴格模式 下 運行
- 使用 import 被導入入的变量是 唯讀 的,可以理解默使用 const 進行宣告,無法被赋值
- 使用 ES6 解構的特性,提升代碼的可讀性
- ES6 module的语法是 靜態 的,更能清楚的表示 模組 間的依賴,提升 tree-shake 帶來的效益
ES6 Module
什麼是 tree-shake ?
tree shake 的本意其實就是透過 工具
去 搖 我們的 js 的檔案,把不需要的東西給移除掉
參考資料
Javascript 模組化設計
By ZHI-WEI HUANG
Javascript 模組化設計
- 59