簡述JavaScript模塊化編程(一)

来源:https://www.cnblogs.com/mingdao/archive/2019/08/02/11289430.html
-Advertisement-
Play Games

在早期編寫JavaScript時,我們只需在<script>標簽內寫入JavaScript的代碼就可以滿足我們對頁面交互的需要了。但隨著時間的推移,時代的發展,原本的那種簡單粗暴的編寫方式所帶來的諸如邏輯混亂,頁面複雜,可維護性差,全局變數暴露等問題接踵而至,前輩們為瞭解決這些問題提出了很種的解決方 ...


在早期編寫JavaScript時,我們只需在<script>標簽內寫入JavaScript的代碼就可以滿足我們對頁面交互的需要了。但隨著時間的推移,時代的發展,原本的那種簡單粗暴的編寫方式所帶來的諸如邏輯混亂,頁面複雜,可維護性差,全局變數暴露等問題接踵而至,前輩們為瞭解決這些問題提出了很種的解決方案,其中之一就是JavaScript模塊化編程。總的來說,它有以下四種優點:

  1. 解決項目中的全局變數污染的問題。
  2. 開發效率高,有利於多人協同開發。
  3. 職責單一,方便代碼復用和維護 。
  4. 解決文件依賴問題,無需關註引用文件的順序。

先行者CommonJs

2009年Node.js橫空出世,將JavaScript帶到了伺服器端領域。而對於伺服器端來說,沒有模塊化那可是不行的。因此CommonJs社區的大牛們開始發力了,制定了一個與社區同名的關於模塊化的規範——CommonJs。它的規範主要如下:

  1. 模塊的標識應遵循的規則(書寫規範)。
  2. 定義全局函數require,通過傳入模塊標識來引入其他模塊,執行的結果即為別的模塊暴露出來的API。
  3. 如果被require函數引入的模塊中也包含依賴,那麼依次載入這些依賴。
  4. 如果引入模塊失敗,那麼require函數應該報一個異常。
  5. 模塊通過變數exports來向外暴露API,exports只能是一個對象,暴露的API須作為此對象的屬性。

根據CommonJS規範的規定,每個文件就是一個模塊,有自己的作用域,也就是在一個文件裡面定義的變數、函數、類,都是私有的,對其他文件是不可見的。通俗來講,就是說在模塊內定義的變數和函數是無法被其他的模塊所讀取的,除非定義為全局對象的屬性。

1 // addA.js
2 const a = 1;
3 const addA = function(value) {
4   return value + a;
5 }

上面代碼中,變數a和函數addA,是當前文件addA.js私有的,其他文件不可見。如果想在多個文件中分享變數a,必須定義為global對象的屬性:

1 global.a = 1;

這樣我們就能在其他的文件中訪問變數a了,但這種寫法不可取,輸出模塊對象最好的方式是module.exports:

1 // addA.js
2 var a = 1;
3 var addA = function(value) {
4   return value + x;
5 }
6 module.exports.addA = addA;

上面代碼通過module.exports對象輸出了一個函數,該函數就是模塊外部與內部通信的橋梁。載入模塊需要使用require方法,該方法讀取一個文件並執行,最後返迴文件內部的module.exports對象。

1 var example = require('./addA.js');
2 console.log(example.addA(1));  //2

CommonJs看起來是一個很不錯的選擇,擁有模塊化所需要的嚴格的入口和出口,看起來一切都很美好,但它的一個特性卻決定了它只能在伺服器端大規模使用,而在瀏覽器端發揮不了太大的作用,那就是同步!這在伺服器端不是什麼問題,但放在瀏覽器端就出現問題了,因為文件都放在伺服器上,如果網速不夠快的話,前面的文件如果沒有載入完成,瀏覽器就會失去響應!因此為了在瀏覽器上也實現模塊化得來個非同步的模塊化才行!根據這個需求,我們的下一位主角——AMD就產生了!

AMD 非同步模塊定義

AMD的全名叫做:Asynchronous Module Definition即非同步模塊定義。它採用了非同步的方式來載入模塊,然後在回調函數中執行主邏輯,因此模塊的載入不影響它後面的模塊的運行。它的規範如下:

1 define(id?, dependencies?, factory);
  1. 用全局函數define來定義模塊;
  2. id為模塊標識,遵從CommonJS Module Identifiers規範
  3. dependencies為依賴的模塊數組,在factory中需傳入形參與之一一對應
  4. 如果dependencies的值中有"require"、"exports"或"module",則與commonjs中的實現保持一致
  5. 如果dependencies省略不寫,則預設為["require", "exports", "module"],factory中也會預設傳入require,exports,module
  6. 如果factory為函數,模塊對外暴漏API的方法有三種:return任意類型的數據、exports.xxx=xxx、module.exports=xxx
  7. 如果factory為對象,則該對象即為模塊的返回值

具體分析AMD我們通過require.js來進行。require.js是一個非常小巧的JavaScript模塊載入框架,是AMD規範最好的實現者之一,require.js的出現主要是來解決兩個問題:

  1. 實現JavaScript文件的非同步載入,避免網頁失去響應。
  2. 管理模塊的依賴性,管理模塊的相互獨立性,也就是我們常說的低耦合,這有利於代碼的編寫與維護。

使用require.js我們首先要載入它,為了避免瀏覽器未響應,我們在後面可以加上async,告訴瀏覽器這個文件需要非同步載入(IE不支持該屬性,所以需要把defer也加上):

1 <script src="js/require.js" defer async="true" ></script>

定義模塊時,在require.js中我們可以使用define,但define對於需要定義的模塊是否是獨立的模塊的寫法是不同;所謂的獨立模塊就是指不依賴於其他模塊的模塊,而非獨立模塊就是指不依賴於其他模塊的模塊。

define在定義獨立模塊時有兩種寫法,一種是直接定義對象;另一種是定義一個函數,在函數內的返回值就是輸出的模塊了:

 1 define({
 2     method1: function() {},
 3     method2: function() {},
 4 });
 5 //等價於
 6 define(function () {
 7     return {
 8         method1: function() {},
 9         method2: function() {},
10     }
11 });

如果define定義非獨立模塊,那麼它的語法就規定一定是這樣的:

 1 define(['module1', 'module2'], function(m1, m2) {
 2 
 3     return {
 4         method: function() {
 5             m1.methodA();
 6             m2.methodB();
 7         }
 8     }
 9 
10 });

define在這個時候接受兩個參數,第一個參數是module是一個數組,它的成員是我們當前定義的模塊所依賴的模塊,只有順利載入了這些模塊,我們新定義的模塊才能成功運行。第二個參數是一個函數,當前面數組內的成員全部載入完之後它才運行,它的參數m與前面的module是一一對應的。這個函數必須返回一個對象,以供其他模塊調用,需要註意的是,回調函數必須返回一個對象,這個對象就是你定義的模塊。

在載入模塊方面,AMD和CommonJs都是使用require。require.js也同樣如此,它要求兩個參數:module,callback:

require([module], callback);

第一個參數[module],是一個數組,裡面的成員就是需要載入的模塊;第二個參數callback,則是載入成功之後的回調函數。
require方法本身也是一個對象,它帶有一個config方法,用來配置require.js運行參數。config方法接受一個對象作為參數。

 1 //別名配置
 2 requirejs.config({
 3     paths: {
 4         jquery: [   //如果第一個路徑不能完成載入,就調到第二個路徑繼續進行載入
 5             '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js',
 6             'lib/jquery'   //本地文件中不需要寫.js
 7         ]
 8     }
 9 });
10 
11 //引入模塊,用變數$表示jquery模塊
12 requirejs(['jquery'], function ($) {
13     $('body').css('background-color','black');
14 });

雖然require.js實現了非同步的模塊化,但它仍然有一些不足的地方,在使用require.js的時候,我們必須要提前載入所有的依賴,然後才可以使用,而不是需要使用時再載入,使得初次載入其他模塊的速度較慢,提高了開發成本。

CMD 通用模塊定義

CMD的全稱是Common Module Definition,即通用模塊定義。它是由螞蟻金服的前端大佬——玉伯提出來的,實現的JavaScript庫為sea.js。它和AMD的require.js很像,但載入方式不同,它是按需就近載入的,而不是在模塊的開始全部載入完成。它有以下兩大核心特點:

  1. 簡單友好的模塊定義規範:Sea.js 遵循 CMD 規範,可以像 Node.js 一般書寫模塊代碼。
  2. 自然直觀的代碼組織方式:依賴的自動載入、配置的簡潔清晰,可以讓我們更多地享受編碼的樂趣。

在CMD規範中,一個文件就是一個模塊,代碼書寫的格式是這樣的:

define(factory);

當factory為函數時,表示模塊的構造方法,執行該方法,可以得到該模塊對外提供的factory介面,factory 方法在執行時,預設會傳入三個參數:require、exports 和 module:

 1 // 所有模塊都通過 define 來定義
 2 define(function(require, exports, module) {
 3 
 4   // 通過 require 引入依賴
 5   var $ = require('jquery');
 6   var Spinning = require('./spinning');
 7 
 8   // 通過 exports 對外提供介面
 9   exports.doSomething = ...
10 
11   // 或者通過 module.exports 提供整個介面
12   module.exports = ...
13 
14 });

它與AMD的具體區別其實我們也可以通過代碼來表現出來,AMD需要在模塊開始前就將依賴的模塊載入出來,即依賴前置;而CMD則對模塊按需載入,即依賴就近,只有在需要依賴該模塊的時候再require就行了:

 1 // AMD規範
 2 define(['./a', './b'], function(a, b) {  // 依賴必須一開始就寫好  
 3    a.doSomething()    
 4    // 此處略去 100 行    
 5    b.doSomething()    
 6    ...
 7 });
 8 // CMD規範
 9 define(function(require, exports, module) {
10    var a = require('./a')   
11    a.doSomething()   
12    // 此處略去 100 行   
13    var b = require('./b') 
14    // 依賴可以就近書寫   
15    b.doSomething()
16    // ... 
17 });

需要註意的是Sea.js的執行模塊順序也是嚴格按照模塊在代碼中出現(require)的順序。

從運行速度的角度來講,AMD雖然在第一次使用時較慢,但在後面再訪問時速度會很快;而CMD第一次載入會相對快點,但後面的載入都是重新載入新的模塊,所以速度會慢點。總的來說,
require.js的做法是並行載入所有依賴的模塊, 等完成解析後, 再開始執行其他代碼, 因此執行結果只會"停頓"1次, 而Sea.js在完成整個過程時則是每次需要相應模塊都需要進行載入,這期間會停頓是多次的,因此require.js從整體而言相對會比Sea.js要快一些。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • jQuery介紹 jQuery是一個輕量級JS庫,使用十分簡單; jQuery的核心是選擇器,用於獲取頁面元素; jQuery提供了大量高效的方法,開發速度大幅提升; jQuery選擇器 jQuery選擇器用於選中需要操作的頁面元素 語法1:jQuery(選擇器表達式) 語法2:$(選擇器表達式) ...
  • 今天在項目中,看到了flat的一個語法,是我之前沒有用過的,所以有必要記錄下來,作為新的知識點,鞏固我自己的知識點; 附贈轉載連接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Arr ...
  • 目錄 為什麼要討論this this是什麼 如何改變this的指向 為什麼要討論this this是什麼 如何改變this的指向 為什麼要討論this 代碼一: 會列印出什麼呢?是 ' I am aa ' 嗎, 結果是: 原因: 此時 this 指向 window 對象, this.aa 等同於 w ...
  • 居中是我們使用css來佈局時常遇到的情況。使用css來進行居中時,有時一個屬性就能搞定,有時則需要一定的技巧才能相容到所有瀏覽器,本文就居中的一些常用方法做個簡單的介紹。 註:本文所講方法除了特別說明外,都是相容IE6+、谷歌、火狐等主流瀏覽器的。 先來說幾種簡單的、人畜無害的居中方法: 1. 把m ...
  • 前置閱讀:簡述JavaScript模塊化(一) 在前面一文中,我們對前端模塊化所經歷的三個階段進行了瞭解: CommonJs,由於是同步的,所以主要應用於伺服器端,以Node.js為代表。 AMD,非同步模塊定義,預載入,推薦依賴前置。以require.js為代表。 CMD,通用模塊載入,懶載入,推薦 ...
  • 與數學中的集合概念類似,集合由一組無序的元素組成,且集合中的每個元素都是唯一存在的。可以回顧一下中學數學中集合的概念,我們這裡所要定義的集合也具有空集(即集合的內容為空)、交集、並集、差集、子集的特性。 在ES6中,原生的Set類已經實現了集合的全部特性,稍後我們會介紹它的用法。 我們使用JavaS ...
  • 一、HTML代碼如下: 二、JavaScript代碼如下: ...
  • 輪播效果: HTML: JS: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...