iT邦幫忙

2024 iThome 鐵人賽

DAY 5
1
Modern Web

Rust 的戰國時代:探索網頁前端工具的前世今生系列 第 5

Day 05:網頁前端工具的前世今生 (三) - CommonJS 與 Node.js 的 server side 模組化開端

  • 分享至 

  • xImage
  •  

前面有提到早期 JavaScript 開發者要做到發佈與載入模組沒有一個方便的做法,今天將從 2009 年出現的 CommonJS 社群切入,繼續來聊聊 JavaScript 的模組化歷史,並會介紹一些 Node.js 中使用 CommonJS 模組需要注意的細節。

day 05 banner

(Photo by Jr Korpa)

前言

昨天聊完了在 CommonJS 出現前的各種模組化歷史,雖然沒提到太多細節,今天在繼續考古的過程中發現了一篇文章,Sea.js 作者玉伯 (lifesinger) 曾更簡單明瞭的提過從模組模式到 YUI3 的脈絡,有興趣深入的讀者可以再參考看看。

JavaScript 的 Server Side 模組化 (2009-2010)

一些歷史

就在 AjaxjQueryYUI3 等技術與工具盛行之時,網頁應用逐漸變得複雜,有一群開發者也正努力想解決當時 JavaScript 在 server side 模組化缺乏一個統一規範的問題,這得從 ServerJS 專案開始說起。

當年 Mozilla 開發者之一的 Kevin Dangoor 在 2009 年初發表了一篇《What Server Side JavaScript needs》,其中講到一些 JavaScript 尚需補足的部份:

  • 缺乏標準的安裝與載入模組的語法
  • 缺乏標準的模組管理系統:社群既有模組無法發佈至一個共同的 repository 中,且也沒有像 Linux 那種用 apt-get 安裝套件的方式
  • server side API 在不同的執行環境中不一致:當時的 JavaScript 有多種不同的執行環境,諸如 Chrome 的 V8、Firefox 的 SpiderMonkey、Safari 的 JSCore、Java 的 Rhino 等,雖然這些執行環境在瀏覽器端的 API 有共同標準,但在檔案處理、目錄操作這類 server side 的功能卻有各自的實作

綜上所述 Kevin 發起這個稱為 ServerJS 的討論小組想要建立標準與模組化的方式,同年的 8 月時 ServerJS 更名為 CommonJS,展現也想將此標準推向瀏覽器端的野心。並在同年的 JSConf EU 上講了相關的議程

順帶一提,Ryan Dahl 也在同一場研討會中發表了 Node.js,其中模組化的實作便是直接用上了 CommonJS,可以說反而因此讓 CommonJS 成為最廣為人知的實踐範例。

ryan dahl at JSConf EU

另外附上一些考古到的討論紀錄:

  • 關於想同時建立 client side 模組標準化的討論
  • 宣布更名為 CommonJS
  • 另外也考古到了 Node.js 作者 Ryan Dahl 在 CommonJS 討論群組中推廣並與其他成員一同討論的紀錄

關於 CommonJS 與 Node.js 中的實作

講了這麼多歷史,實際來看個 Node.js 中 CommonJS 模組的範例:

// module.js
var author = 'codefarmer';

function add(a, b) {
  return a + b;
}

module.exports = {
  author: author,
  add: add
};
// main.js
var myModule = require('./module');

console.log(myModule.author); // codefarmer
console.log(myModule.add(2, 3)); // 5

從上面的這個範例,我們可以透過 module.exports 這個去輸出模組內的變數 、函式、class 等內容,並用 require 在需要的地方去載入模組。

而參考 Node.js 中 CommonJS 模組的文件,以下紀錄一些我有疑慮的注意事項。

Q. 可以用 exportsmodule.exports 來輸出內容,兩者有什麼差別?

// calculator.js

// 寫法一:使用 exports
exports.add = function (a, b) {
  return a + b;
};

exports.subtract = function (a, b) {
  return a - b;
};

// 寫法二:使用 module.exports
module.exports = {
  add: function (a, b) {
    return a + b;
  },
  subtract: function (a, b) {
    return a - b;
  }
};

兩種輸出方式達到的效果是一樣的,可以理解為當我們在 Node.js 建立一個模組時,Node.js 會為這個模組生成像這樣的空物件,而讓我們可以在模組中去輸出內容 (ref):

var module = { exports: {} };
var exports = module.exports;

但要特別注意的是直接將輸出值指定給 exports 並不會真的改變此模組的輸出,仍會需要用 module.exports,舉例像是這樣:

// 這不會改變模組的導出,因為此時原本的 module.exports 仍為空物件
exports = { add: function(a, b) {...} };

// 這才會改變模組的導出
module.exports = { add: function(a, b) {...} };

綜上所述,一般來說都還是會傾向直接用 module.exports 來輸出。

Node.js 中的 CommonJS 注意事項

  • require 是同步載入,也就是說在模組載入完成前,程式不會繼續執行
  • require 會緩存載入過的模組,因此多次去載入同一個模組並不會讓這個模組被多次執行

關於 Node.js 歷史的延伸資訊

關於 Node.js 的歷史雖然很精彩,但跟此系列較無關所以這邊不會贅述,有興趣的話可以參考 Honeypot 幾個月前推出的 Node.js: The Documentary | An origin story 這個高規格製作的 Node.js 紀錄片。

不得不說 Node.js 社群與 Joyent 的 drama 真的是讓這個紀錄片變成一個節奏緊湊精采的 Netflix 電影的感覺,紀錄一些影片中會提到的部分:

  • Node.js 作者 Ryan Dahl 背景與發明動機
  • 二代目領導 aka NPM 作者 Isaac Schlueter 觀點
  • 三代目領導 Joyent 的 TJ Fontaine 與社群之戰
  • 社群版分叉 io.js 的經過
  • Joyent 與社群達成共識,讓 Node.js 回歸開放

小結

今天又不小心挖得太深入,原本差點還要來寫 Node.js 的紀錄片筆記,但如上述較無關所以最後只保留了一小段,明天將會聊聊 CommonJS 社群對於走向 client side 模組標準化的意見分歧,而產生了 AMD、CMD、UMD 的百家爭鳴的故事。

文章同步發表於個人部落格


上一篇
Day 04:網頁前端工具的前世今生 (二) - 在 CommonJS 前未被提及的歷史
下一篇
Day 06:網頁前端工具的前世今生 (四) - Client Side 模組化的百家爭鳴
系列文
Rust 的戰國時代:探索網頁前端工具的前世今生30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言