首頁 >web前端 >js教程 >設計和構建自己的JavaScript庫:提示和技巧

設計和構建自己的JavaScript庫:提示和技巧

Jennifer Aniston
Jennifer Aniston原創
2025-02-18 08:24:13313瀏覽

構建和發布你自己的JavaScript庫:一份詳盡指南

Design and Build Your Own JavaScript Library: Tips & Tricks

核心要點

  • 明確目標: 在開始構建庫之前,明確它要解決的具體問題,保持專注並確保其實用性。
  • 以用戶為中心的API設計: 以最終用戶為中心設計你的庫,使其簡單易用,從而提高用戶採用率和滿意度。
  • 靈活性和自定義: 通過配置、公共方法和事件處理提供自定義選項,以適應不同的用戶需求。
  • 測試和文檔: 使用Mocha或Jasmine等框架進行徹底的測試,並確保提供全面的文檔以幫助理解和使用。
  • 模塊加載器兼容性: 確保你的庫支持各種模塊加載器,方法是使用通用模塊定義 (UMD) 或類似方法來最大限度地提高兼容性。
  • 版本控制和發布: 使用語義版本控制進行更新,並將你的庫發佈到npm或Bower等包管理器,以覆蓋更廣泛的受眾。

本文由Adrian Sandu、Vildan Softic和Dan Prince同行評審。感謝所有SitePoint的同行評審者,使SitePoint的內容達到最佳狀態!

我們經常使用庫。庫是打包好的代碼,開發人員可以在他們的項目中使用它,這無疑節省了工作量,並避免了重複造輪子。擁有可重用的包(無論是開源的還是閉源的)都比重建相同的功能或從過去的項目中手動複製粘貼更好。

但除了打包的代碼之外,庫究竟是什麼呢?除了一些例外情況外,庫應該始終是一個文件,或者位於單個文件夾中的幾個文件。它的代碼應該單獨維護,並在將其實現到項目中時保持不變。庫應該允許你設置項目特定的配置和/或行為。把它想像成一個USB設備,它只允許通過USB端口進行通信。一些設備,如鼠標和鍵盤,允許通過設備提供的接口進行配置。

在本文中,我將解釋庫是如何構建的。儘管涵蓋的大部分主題都適用於其他語言,但這篇文章主要關注構建JavaScript庫。

為什麼構建你自己的JavaScript庫?

首先,庫使現有代碼的重用非常方便。你無需挖掘舊項目並複制一些文件,只需引入庫即可。這還會使你的應用程序碎片化,使應用程序代碼庫更小,更容易維護。

Design and Build Your Own JavaScript Library: Tips & Tricks

基督教堂圖書館(來源)
任何使實現特定目標更容易並且可以重用的代碼,例如抽象,都是可以打包到庫中的候選者。一個有趣的例子是jQuery。儘管jQuery的API遠不止簡化的DOM API,但在幾年前,當跨瀏覽器DOM操作相當困難時,它意義重大。

如果一個開源項目變得流行起來,並且越來越多的開發者使用它,那麼人們很可能會加入並通過提交問題或為代碼庫做出貢獻來幫助該項目。無論哪種方式,它都會使庫及其依賴它的所有項目受益。

一個流行的開源項目也可能帶來巨大的機會。一家公司可能會對你工作的質量印象深刻,並提供給你一份工作。也許一家公司會要求你幫助將你的項目集成到他們的應用程序中。畢竟,沒有人比你更了解你的庫。

對許多人來說,這僅僅是一種愛好——享受編寫代碼、幫助他人以及在此過程中學習和成長的過程。你可以突破你的極限,嘗試新事物。

範圍和目標

在編寫第一行代碼之前,應該清楚你的庫的目的是什麼——你必須設定目標。有了它們,你可以專注於你希望用你的庫解決什麼問題。記住,你的庫應該比原始形式的問題更容易使用和記住。 API越簡單,用戶學習使用你的庫就越容易。引用Unix哲學:

只做一件事,並把它做好

問問你自己:你的庫解決了什麼問題?你打算如何解決它?你會自己編寫所有內容,還是可以使用其他人的庫?

無論庫的大小如何,都嘗試制定路線圖。列出你想要的所有功能,然後儘可能多地刪除功能,直到你擁有一個微小但功能齊全的庫,就像最小可行產品一樣。這將是你的第一個版本。從那裡,你可以為每個新功能創建里程碑。本質上,你將項目分解成小塊,使每個功能都更像一項成就,更令人愉快。相信我,這會讓你保持理智。

API設計

我個人非常喜歡從最終用戶的角度來處理我的庫。你可以稱之為以用戶為中心的設計。從本質上講,你正在創建庫的概要,希望對其進行更多思考,並使其對任何選擇使用它的人更方便。同時,你可以考慮哪些方面應該是可自定義的,這將在本文後面討論。

最終的API質量測試是吃自己的狗糧,在自己的項目中使用你的庫。嘗試用你的庫替換應用程序代碼,看看它是否涵蓋了你想要的所有功能。嘗試使庫盡可能精簡,同時使其足夠靈活,以便通過自定義(如本文後面所述)使其也適用於它們的極端情況。

以下是用戶代理字符串庫的實現或概要可能是什麼樣子:

<code>// 以空的UserAgent字符串开始
var userAgent = new UserAgent;

// 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us)
var application = new UserAgent.Product('EvilCorpBrowser', '1.2');
application.setComment('X11', 'Linux', 'en-us');
userAgent.addProduct(application);

// 创建并添加第二个产品:Blink/20420101
var engine = new UserAgent.Product('Blink', '20420101');
userAgent.addProduct(engine);

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101
userAgent.toString();

// 对引擎产品进行更多更改
engine.setComment('Hello World');

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World)
userAgent.toString();
</code>

根據庫的複雜性,你可能還需要考慮結構。利用設計模式是構建庫或克服某些技術問題的絕佳方法。它還降低了在添加新功能時重構大部分代碼的風險。

靈活性和自定義

使庫變得強大的東西是靈活性,但也很難在你可以自定義的內容和不能自定義的內容之間劃清界限。一個完美的例子是chart.js與D3.js。兩者都是可視化數據的優秀庫。 Chart.js使創建和設置不同類型的內置圖表變得非常容易。但是,如果你需要更多地控製圖形,則需要使用D3.js。

有多種方法可以賦予用戶控制權:配置、公開公共方法以及通過回調和事件。

庫的配置通常在初始化期間完成,但一些庫允許你在運行時修改選項。選項通常限於細枝末節,更改這些選項不應該做任何事情,除了更新這些值以供以後使用。

<code>// 在初始化时配置
var userAgent = new UserAgent({
  commentSeparator: ';'
});

// 使用公共方法进行运行时配置
userAgent.setOption('commentSeparator', '-');

// 使用公共属性进行运行时配置
userAgent.commentSeparator = '-';
</code>

可以公開方法來與實例交互,例如從實例檢索數據(getter),將數據放入實例(setter)以及執行操作。

<code>var userAgent = new UserAgent;

// 获取器,用于从所有产品中检索注释
userAgent.getComments();

// 用于打乱所有产品顺序的操作
userAgent.shuffleProducts();
</code>

回調有時會與公共方法一起傳遞,通常是在異步任務之後運行用戶代碼。

<code>var userAgent = new UserAgent;

userAgent.doAsyncThing(function asyncThingDone() {
  // 异步操作完成后运行代码
});
</code>

事件具有很大的潛力。它們類似於回調,除了添加事件處理程序不應該觸發操作。事件通常用於指示(你可能猜到了)事件!就像回調一樣,你可以提供其他信息並返回庫可以使用的值。

<code>var userAgent = new UserAgent;

// 验证添加的产品
userAgent.on('product.add', function onProductAdd(e, product) {
  var shouldAddProduct = product.toString().length 
  // 告诉库添加或不添加产品
  return shouldAddProduct;
});
</code>

在某些情況下,你可能希望允許用戶擴展你的庫。為此,你可以公開一個公共方法或屬性供用戶填充,就像Angular模塊(angular.module('myModule'))和jQuery的fn(jQuery.fn.myPlugin)一樣,或者什麼也不做,只需讓用戶訪問你的庫的命名空間:

<code>// AngryUserAgent 模块
// 可以访问UserAgent命名空间
(function AngryUserAgent(UserAgent) {

  // 创建新的方法 .toAngryString()
  UserAgent.prototype.toAngryString = function() {
    return this.toString().toUpperCase();
  };

})(UserAgent);

// 应用程序代码
var userAgent = new UserAgent;
// ...

// EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101
userAgent.toAngryString();
</code>

同樣,這也允許你覆蓋方法。

<code>// AngryUserAgent 模块
(function AngryUserAgent(UserAgent) {

  // 存储旧的 .toString() 方法以供以后使用
  var _toString = UserAgent.prototype.toString;

  // 覆盖 .toString()
  UserAgent.prototype.toString = function() {
    return _toString.call(this).toUpperCase();
  };

})(UserAgent);

var userAgent = new UserAgent;
// ...

// EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101
userAgent.toString();
</code>

在後一種情況下,允許用戶訪問你的庫的命名空間,會減少你對擴展/插件定義方式的控制。為了確保擴展遵循某些約定,你可以(也應該)編寫文檔。

測試

編寫概要是測試驅動開發的良好開端。簡而言之,這是在你編寫實際庫之前寫下測試形式的標準。如果這些測試檢查某個功能的行為是否符合預期,並且你在編寫庫之前就編寫了這些測試,則該策略稱為行為驅動開發。無論哪種方式,如果你的測試涵蓋了庫中的每個功能,並且你的代碼通過了所有測試,你可以安全地假設你的庫可以工作。

Jani Hartikainen解釋瞭如何使用Mocha在“使用Mocha和Chai進行單元測試你的JavaScript”中編寫單元測試。在“使用Jasmine、Travis和Karma測試JavaScript”中,Tim Evko展示瞭如何使用另一個名為Jasmine的框架設置一個很棒的測試管道。這兩個測試框架非常流行,但還有許多其他類型的框架。

我前面在本文中創建的概要已經對預期輸出進行了註釋。這就是所有測試的開始:從期望開始。我的庫的Jasmine測試如下所示:

<code>// 以空的UserAgent字符串开始
var userAgent = new UserAgent;

// 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us)
var application = new UserAgent.Product('EvilCorpBrowser', '1.2');
application.setComment('X11', 'Linux', 'en-us');
userAgent.addProduct(application);

// 创建并添加第二个产品:Blink/20420101
var engine = new UserAgent.Product('Blink', '20420101');
userAgent.addProduct(engine);

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101
userAgent.toString();

// 对引擎产品进行更多更改
engine.setComment('Hello World');

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World)
userAgent.toString();
</code>

一旦你完全對第一個版本的API設計感到滿意,就該開始考慮架構以及你的庫將如何使用。

模塊加載器兼容性

你可能使用也可能不使用模塊加載器。但是,選擇實現你的庫的開發人員可能會使用,因此你希望你的庫與模塊加載器兼容。但是哪個?你如何在CommonJS、RequireJS、AMD和其他模塊加載器之間進行選擇?

實際上,你無需選擇!通用模塊定義 (UMD) 是另一種旨在支持多個模塊加載器的策略。你可以在網上找到不同版本的代碼片段,你也可以在UMD GitHub存儲庫中找到不同的UMD版本,以使你的庫與UMD兼容。使用其中一個模板開始你的庫,或者使用你喜歡的構建工具添加UMD,你就不必擔心模塊加載器了。

如果你希望使用ES2015導入/導出語法,我強烈建議使用Babel編譯成ES5,並結合Babel的UMD插件。這樣,你就可以在項目中使用ES2015,同時仍然生成適合所有人的庫。

文檔

我完全贊成對所有項目進行徹底的文檔記錄,但這通常被認為是很多工作,被推遲,最終被遺忘。

基本信息

文檔應該始終從基本信息開始,例如項目名稱和描述。這將幫助其他人理解你的庫的功能以及它是否適合他們。

你可以提供其他信息,例如範圍和目標,以更好地告知用戶,以及路線圖,以便他們知道將來會發生什麼或知道他們如何做出貢獻。

API、教程和示例

當然,你需要讓用戶了解如何使用你的庫。這從API文檔開始。教程和示例是很好的補充,但編寫這些可能需要大量工作。但是,內聯文檔並非如此。這些是可以解析並使用JSDoc轉換為文檔頁面的註釋。

元任務

一些用戶可能希望更改你的庫。在大多數情況下,這將是為了貢獻,但有些人可能希望為私人使用創建自定義版本。對於這些用戶,包含元任務的文檔非常有用,例如構建庫、運行測試、生成、轉換或下載數據等的命令列表。

貢獻

當你開源你的庫時,貢獻非常重要。為了指導貢獻者,你可以在其中添加文檔,解釋做出貢獻的步驟以及它應該滿足的標準。這將使你更容易審查和接受貢獻,並使他們更容易正確地進行貢獻。

許可證

最後但並非最不重要的一點是,請包含許可證。從技術上講,如果你選擇不包含許可證,它仍然受版權保護,但並非每個人都知道這一點。

我發現ChooseALicense.com是一個很好的資源,可以讓你在無需成為法律專家的情況下選擇許可證。選擇許可證後,只需將文本保存在項目根目錄下的LICENSE.txt文件中即可。

打包並添加一個蝴蝶結

版本控制對於一個好的庫至關重要。如果你選擇進行重大更改,用戶可能希望繼續使用對他們有效的版本。

當前事實上的版本命名標準是語義版本控制,或SemVer。 SemVer版本由三個數字組成,每個數字表示不同的更改:主版本、次版本和修補程序版本。

向你的Git存儲庫添加版本/版本

如果你有git存儲庫,你可以向存儲庫添加版本號。你可以將它們視為存儲庫的快照。我們稱之為標籤。要創建標籤,請打開終端並鍵入:

<code>// 以空的UserAgent字符串开始
var userAgent = new UserAgent;

// 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us)
var application = new UserAgent.Product('EvilCorpBrowser', '1.2');
application.setComment('X11', 'Linux', 'en-us');
userAgent.addProduct(application);

// 创建并添加第二个产品:Blink/20420101
var engine = new UserAgent.Product('Blink', '20420101');
userAgent.addProduct(engine);

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101
userAgent.toString();

// 对引擎产品进行更多更改
engine.setComment('Hello World');

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World)
userAgent.toString();
</code>

許多服務(如GitHub)將提供你所有版本的概述以及每個版本的下載鏈接。

發佈到公共存儲庫

npm

許多編程語言都帶有包管理器,或者可以使用第三方包管理器。這些允許我們專門為這些語言引入庫。例如,PHP的Composer和Ruby的RubyGems。

Node.js(一種獨立的JavaScript引擎)帶有npm。如果你不熟悉npm,我們有一個很棒的初學者指南。

默認情況下,你的npm包將公開發布。別擔心!你也可以發布私有包,設置私有註冊表或完全避免發布。

要發布你的包,你的項目需要一個package.json文件。你可以手動執行此操作或使用交互式嚮導。要啟動嚮導,請鍵入:

<code>// 在初始化时配置
var userAgent = new UserAgent({
  commentSeparator: ';'
});

// 使用公共方法进行运行时配置
userAgent.setOption('commentSeparator', '-');

// 使用公共属性进行运行时配置
userAgent.commentSeparator = '-';
</code>

version屬性應與你的git標籤匹配。此外,請確保擁有README.md文件。就像GitHub一樣,npm也使用它作為呈現你的包的頁面。

之後,你可以通過鍵入以下命令發布你的包:

<code>var userAgent = new UserAgent;

// 获取器,用于从所有产品中检索注释
userAgent.getComments();

// 用于打乱所有产品顺序的操作
userAgent.shuffleProducts();
</code>

就是這樣!你已經發布了你的npm包。

Bower

幾年前,出現了另一個名為Bower的包管理器。但是,這個包管理器並非為特定語言設計,而是為特定平台——Web設計的。你可以在那裡找到所有主要的前端資源。只有當你的庫與瀏覽器兼容時,在Bower上發布你的包才有意義。

如果你不熟悉Bower,我們也有一個初學者指南。

與npm一樣,你也可以設置私有存儲庫。你也可以在嚮導中完全阻止它發布。

有趣的是,在過去的一兩年裡,許多人似乎正在轉向npm用於前端資源。儘管npm包主要是JavaScript,但許多前端包也在npm上發布。無論哪種方式,Bower仍然很流行,所以我絕對建議你也將你的包發佈到Bower上。

我有沒有提到Bower實際上是一個npm模塊,並且最初是受其啟發的?命令非常相似。要生成bower.json文件,請鍵入:

<code>// 以空的UserAgent字符串开始
var userAgent = new UserAgent;

// 创建并添加第一个产品:EvilCorpBrowser/1.2 (X11; Linux; en-us)
var application = new UserAgent.Product('EvilCorpBrowser', '1.2');
application.setComment('X11', 'Linux', 'en-us');
userAgent.addProduct(application);

// 创建并添加第二个产品:Blink/20420101
var engine = new UserAgent.Product('Blink', '20420101');
userAgent.addProduct(engine);

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101
userAgent.toString();

// 对引擎产品进行更多更改
engine.setComment('Hello World');

// EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World)
userAgent.toString();
</code>

就像npm init一樣,說明是自解釋的。最後,要發布你的包:

<code>// 在初始化时配置
var userAgent = new UserAgent({
  commentSeparator: ';'
});

// 使用公共方法进行运行时配置
userAgent.setOption('commentSeparator', '-');

// 使用公共属性进行运行时配置
userAgent.commentSeparator = '-';
</code>

就這樣,你已經將你的庫發佈到互聯網上,供每個人在他們的Node項目和/或Web上使用!

結論

核心產品是庫。確保它解決了問題,易於使用且穩定,你將使你的團隊或許多開發人員非常高興。

我提到的許多任務很容易自動化,例如:運行測試、創建標籤、更新package.json中的版本以及將你的包重新發佈到npm和bower。這就是你進入持續集成領域並使用Travis CI或Jenkins等工具的地方。我前面提到的Tim Evko的文章也涉及到這一點。

你構建並發布過庫嗎?請在下面的評論部分分享!

關於設計和構建你自己的JavaScript庫的常見問題 (FAQ)

創建我自己的JavaScript庫的好處是什麼?

創建你自己的JavaScript庫有很多好處。首先,它允許你在多個項目中重用代碼,從長遠來看節省了時間和精力。其次,它可以幫助你以更結構化和可讀的方式組織代碼。這在處理大型項目或與其他開發人員合作時尤其有用。最後,創建你自己的庫可以成為一個很好的學習體驗,幫助你加深對JavaScript和軟件開發原則的理解。

我如何開始創建JavaScript庫?

創建JavaScript庫的第一步是定義其用途。你希望你的庫提供什麼功能?一旦你清楚地了解你希望你的庫做什麼,你就可以開始編寫代碼了。這通常涉及定義一系列提供所需功能的函數。然後,這些函數通過其他開發人員可以使用的公共API公開。

我如何測試我的JavaScript庫?

測試是開發JavaScript庫的關鍵部分。有幾種JavaScript測試框架可用,例如Jest、Mocha和Jasmine。這些框架允許你為你的函數編寫單元測試,確保它們按預期工作。除了單元測試之外,你可能還需要編寫集成測試來檢查庫的不同部分是否可以一起正常工作。

我如何記錄我的JavaScript庫?

良好的文檔對於任何軟件庫都是必不可少的。它幫助其他開發人員了解如何使用你的庫以及每個函數的作用。你應該在庫中包含每個函數的詳細描述,包括其輸入、輸出和任何副作用。你還可以使用JSDoc等工具根據代碼註釋自動生成文檔。

我如何分發我的JavaScript庫?

有多種方法可以分發JavaScript庫。一種常見的方法是將其發佈到npm等包管理器上。這允許其他開發人員使用簡單的命令輕鬆安裝你的庫。你還可以通過將其託管在CDN(內容分發網絡)上或在你的網站上提供下載鏈接來分發你的庫。

我如何維護我的JavaScript庫?

維護JavaScript庫包括修復錯誤、添加新功能以及使庫與最新的JavaScript標準和實踐保持同步。定期測試你的庫並傾聽用戶的反饋非常重要。你還可以考慮對你的庫進行版本控制,以便用戶可以選擇使用穩定版本或具有新功能的最新版本。

我如何確保我的JavaScript庫高效?

為了確保你的JavaScript庫高效,你應該專注於編寫簡潔明了的代碼。避免不必要的計算和內存分配。使用Chrome DevTools等工具來分析你的庫並識別任何性能瓶頸。你還可以考慮壓縮你的庫以減小其文件大小並提高加載時間。

我如何使我的JavaScript庫與不同的瀏覽器兼容?

由於每個瀏覽器解釋JavaScript的方式不同,因此確保瀏覽器兼容性可能是一個挑戰。你可以使用Babel等工具將你的代碼轉換為與舊版瀏覽器兼容的JavaScript版本。你還應該在不同的瀏覽器中測試你的庫,以識別和修復任何兼容性問題。

我如何在JavaScript庫中處理錯誤?

錯誤處理是開發JavaScript庫的重要組成部分。你應該努力提供清晰、有幫助的錯誤消息,以幫助用戶了解出了什麼問題。你可以使用try/catch塊來捕獲和處理錯誤。你還可以考慮提供一種讓用戶報告錯誤和問題的方法。

我如何獲得有關我的JavaScript庫的反饋?

有多種方法可以獲得有關你的JavaScript庫的反饋。你可以請其他開發人員審查你的代碼,將你的庫發佈到論壇或社交媒體上,或者將其發佈到npm等包管理器上並尋求反饋。你應該對批評持開放態度,並願意根據你收到的反饋進行更改。

以上是設計和構建自己的JavaScript庫:提示和技巧的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn