首頁 >web前端 >js教程 >構建您的第一個JavaScript庫

構建您的第一個JavaScript庫

尊渡假赌尊渡假赌尊渡假赌
尊渡假赌尊渡假赌尊渡假赌原創
2025-03-11 00:09:09626瀏覽

Build Your First JavaScript Library

你是否曾驚嘆於React的魔力?是否曾好奇Dojo是如何運作的?是否曾對jQuery的巧妙操作感到好奇?在本教程中,我們將潛入幕後,嘗試構建一個超簡化的jQuery版本。

我們幾乎每天都在使用JavaScript庫。無論是實現算法、提供API抽像還是操作DOM,庫在大多數現代網站中都執行許多功能。

在本教程中,我們將嘗試從頭開始構建一個這樣的庫(當然,這是一個簡化的版本)。我們將創建一個用於DOM操作的庫,類似於jQuery。是的,這很有趣,但在你興奮之前,讓我澄清幾點:

  • 這不會是一個功能齊全的庫。我們將編寫一套可靠的方法,但這並非完整的jQuery。我們將做的足夠多,讓你對構建庫時會遇到的問題類型有很好的了解。
  • 我們在這裡不會追求跨所有瀏覽器的完全兼容性。我們今天編寫的代碼應該可以在Chrome、Firefox和Safari上運行,但在IE等舊版瀏覽器上可能無法運行。
  • 我們不會涵蓋我們庫的每一個可能的用途。例如,我們的prepend方法只在你傳遞給它們我們的庫實例時才有效;它們不適用於原始DOM節點或節點列表。

  1. 創建庫的框架

我們將從模塊本身開始。我們將使用ECMAScript模塊(ESM),這是一種在Web上導入和導出代碼的現代方法。

export class Dome { constructor(selector) { } }

如你所見,我們導出一個名為Dome的類,其構造函數將接受一個參數,但它可以是多種類型。如果它是一個字符串,我們將假設它是一個CSS選擇器,但我們也可以接受單個DOM節點或document.querySelectorAll的結果來簡化元素查找。如果它具有length屬性,我們將知道我們擁有一個節點列表。我們將把這些元素存儲在this.elements中,Dome對象可以包裝多個DOM元素,我們幾乎需要在每種方法中循環遍歷每個元素,因此這些實用程序將非常方便。

讓我們從一個map函數開始,它接受一個參數,一個回調函數。我們將循環遍歷數組中的項目,收集回調函數返回的內容,Dome實例將接收兩個參數:當前元素和索引號。

我們還需要一個forEach方法,默認情況下,我們可以簡單地將調用轉發到mapOne。很容易看出這個函數的作用,但真正的問題是,為什麼我們需要它?這需要一點你可能稱之為“庫理念”的東西。

簡短的理念探討

如果構建庫只是編寫代碼,那將不是一項太難的工作。但在從事這個項目時,我發現更難的部分是決定某些方法應該如何工作。

很快,我們將構建一個Dome對象,它包裝了多個DOM節點($("li").text()),你將得到一個包含所有元素文本連接在一起的單個字符串。這有用嗎?我認為沒有,但我不知道更好的返回值是什麼。

對於這個項目,我將把多個元素的文本作為數組返回,除非數組中只有一個項目;然後我們只返回文本字符串,而不是包含單個項目的數組。我認為你最常獲取單個元素的文本,所以我們對此情況進行了優化。但是,如果你正在獲取多個元素的文本,我們將返回你可以使用的內容。

返回編碼

因此,mapOne將首先調用map,然後返回數組或數組中的單個項目。如果你仍然不確定這如何有用,請繼續關注:你將看到!

mapOne(callback) { const m = this.map(callback); return m.length > 1 ? m : m[0]; };
  1. 使用文本和HTML

接下來,讓我們添加text方法來查看我們是在設置還是獲取。請注意,這只是遍曆元素並設置它們的文本。如果我們正在獲取,我們將返回元素的mapOne方法:如果我們正在處理多個元素,這將返回一個數組;否則,它將只是一個字符串。

html方法與text方法幾乎相同,只是它將使用innerHTML

html(html) { if (typeof html !== "undefined") { this.forEach(function (el) { el.innerHTML = html; }); return this; } else { return this.mapOne(function (el) { return el.innerHTML; }); } }

就像我說的:幾乎相同。


  1. 操作類

接下來,我們要能夠添加和刪除類,所以讓我們編寫addClassremoveClass方法。

我們的addClass方法將在每個元素上使用classList.add方法。當傳遞字符串時,只添加該類,當傳遞數組時,我們將遍歷數組並添加其中包含的所有類。

addClass(classes) { return this.forEach(function (el) { if (typeof classes !== "string") { for (const elClass of classes) { el.classList.add(elClass); } } else { el.classList.add(classes); } }); }

很簡單,對吧?

現在,刪除類呢?為此,你幾乎要做同樣的事情,只是使用classList.remove方法。

  1. 使用屬性

接下來,讓我們添加attr函數。這將很容易,因為它與我們的html方法幾乎相同。像這些方法一樣,我們將能夠同時獲取和設置屬性:我們將接受一個屬性名稱和值來設置,只接受一個屬性名稱來獲取。

attr(attr, val) { if (typeof val !== "undefined") { return this.forEach(function (el) { el.setAttribute(attr, val); }); } else { return this.mapOne(function (el) { return el.getAttribute(attr); }); } }

如果val已定義,我們將使用setAttribute方法。否則,我們將使用getAttribute方法。

  1. 創建元素

我們應該能夠創建新元素,任何好的庫都可以做到這一點。當然,這作為Dome類的方法是沒有意義的。

export function create(tagName,attrs) { }

如你所見,我們將接受兩個參數:元素的名稱和屬性對象。大多數屬性將通過我們的attr方法應用,文本內容將通過text方法應用於Dome對象。以下是所有這些的實際操作:

export function create(tagName, attrs) { let el = new Dome([document.createElement(tagName)]); if (attrs) { for (let key in attrs) { if (attrs.hasOwnProperty(key)) { el.attr(key, attrs[key]); } } } return el; }

如你所見,我們創建元素並將其直接發送到新的Dome對像中。

但是現在我們正在創建新元素,我們將希望將它們插入到DOM中,對吧?

  1. 附加和前置元素

接下來,我們將編寫appendprepend方法。這些函數有點棘手,主要是因為有多種用例。以下是我們想要能夠做的事情:

dome1.append(dome2); dome1.prepend(dome2);

我們可能想要附加或前置:

  • 一個新元素到一個或多個現有元素
  • 多個新元素到一個或多個現有元素
  • 一個現有元素到一個或多個現有元素
  • 多個現有元素到一個或多個現有元素

我使用“新”來表示尚未在DOM中的元素;現有元素已在DOM中。讓我們現在逐步講解:

append(els) { }

我們期望els是一個Dome對象。一個完整的DOM庫會將其作為節點或節點列表接受,但我們不會這樣做。我們必須遍歷我們的每個元素,然後在其中,我們遍歷我們想要附加的每個元素。

如果我們正在附加,則來自作為參數傳入的外部Dome對象的i將只包含原始(未克隆的)節點。因此,如果我們只將單個元素附加到單個元素,則所有涉及的節點都將是它們各自的prepend方法的一部分。

  1. 刪除元素

為了完整起見,讓我們添加一個remove方法。這將非常簡單,因為我們只需要使用removeChild方法。為了使事情更簡單,我們將使用forEach循環反向遍歷,我將使用removeChild方法反向遍歷循環,每個元素的Dome對象仍然可以正常工作;我們可以使用任何我們想要的方法,包括將其附加或前置回DOM。不錯,對吧?

  1. 使用事件

最後但並非最不重要的是,我們將編寫一些事件處理程序函數。

查看on方法,然後我們將討論它:

on(evt, fn) { return this.forEach(function (el) { el.addEventListener(evt, fn, false); }); }

這很簡單。我們只需遍曆元素並使用addEventListener方法。 off函數(它取消掛鉤事件處理程序)幾乎相同:

off(evt, fn) { return this.forEach(function (el) { el.removeEventListener(evt, fn, false); }); }

  1. 使用庫

要使用Dome,只需將其放入腳本並導入它。

import {Dome, create} from "./dome.js"

從那裡,你可以像這樣使用它:

new Dome("li") ...

確保你導入它的腳本是ES模塊。

就是這樣!

我希望你能嘗試一下我們的小型庫,甚至可以擴展它一點。正如我前面提到的,我已經把它放在GitHub上了。隨意分叉它,玩耍,並發送拉取請求。

讓我再次澄清一下:本教程的目的並不是建議你應該總是編寫自己的庫。有專門的團隊在共同努力,使大型的、成熟的庫盡可能好。這裡的目的是讓你對庫內部可能發生的事情有所了解;我希望你在這裡學到了一些技巧。

我強烈建議你在你的一些最喜歡的庫中四處挖掘。你會發現它們並不像你想像的那麼神秘,而且你可能會學到很多東西。以下是一些不錯的起點:

  • 我從jQuery源代碼中學到的11件事(Paul Irish)
  • jQuery的幕後(James Padolsey)
  • React 16:深入了解我們前端UI庫的API兼容重寫

這篇文章已更新,其中包含Jacob Jackson的貢獻。 Jacob是一位網絡開發者、技術作家、自由職業者和開源貢獻者。

以上是構建您的第一個JavaScript庫的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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