社群媒體 照片由 Andre Taissin 在 Unsplash 上拍攝
增強任何 DOM 內建元素的最簡單方法:
無需填充,所有現代瀏覽器都可以工作™️
可以毫無問題地擴充HTML 、 SVG或任何其他自訂命名空間,例如MathML
元素可以從頭開始創建,也可以根據需要升級以實現優雅的水合作用
適合313 位元組(核心)或774 字節,包含自訂元素生命週期回呼(沒有什麼新東西需要學習)
這個範例可以進行現場測試,它只是觸及了這個極其微小但功能強大的模組的皮毛。
// const createRegistry = require('nonchalance');import createRegistry from 'nonchalance/core';// 對於所有沒有原生 DOM 的環境,預設導出可選擇接受類似 `globalThis` 的上下文。公開限定的類別名稱以及將用於建立元素的「文件」參考。的註冊表擴充了HTML.Input { // 將繼承靜態標籤 field = "input" 建構子(...args) {super(...args);this.type = '密碼'; } // 避免惡意腳本輕鬆檢索任何密碼 獲取值(){返回'********'; } // 示範案例:設定密碼時仍使用原生 `value` 設定值(秘密){super.value =秘密; }}document.body.innerHTML = ` <form> <input type="text" name="user" placeholder="user"> <input type="password" name="password" placeholder="password"> <input type="submit"></form>`;// 透過new ClassTypenew Password(document.querySelector('[type="password"]')) 升級所需的元素;// 或建立一個新實例const Secret = Object.分配(新密碼,{ 名稱:“通過”, 值:Math.random(), placeholder: '運行時密碼'});const form = document.querySelector('form');form.insertBefore(secret, form.lastElementChild);
使用自訂功能模組,可以升級任何類型的元素,而不會遇到非法建構函數錯誤,當自然class extends HTMLSomethingElement {}
意圖時,該錯誤會在任何時候出現,當此類未在全域範圍內定義為條目時customElements
註冊表。
不僅沒有透過該模組在全局上下文中全局共享任何內容,而且完全不需要讓任何內建擴充功能工作的每一項尷尬的額外工作:
新的或傳遞的元素總是保留其原型根鏈
不會發生額外的屬性或衝突的名稱
最重要的是,因為可以為每個模組或項目創建任何HTML 註冊表以在其組件之間共享,所以還可以將任何虛假或模擬的globalThis
環境傳遞給此類註冊表創建,至少有一個公開createElementNS(namespace, tagName)
的document
欄位createElementNS(namespace, tagName)
方法,以及專案要測試的一個或多個類,例如HTMLElement
和/或此類專案成功所需的任何其他類別。
然而,由於該模組的主要目標是DOM ,因此globalThis
引用被用作合理的預設值,但這仍然並不意味著透過預設導出創建的註冊表共享任何內容。
不。 custom-function
工作方式可以總結如下:
# a native <p> protoype chain HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to new (class CustomP extends HTML.P {}) CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to class AnotherP extends CustomP {} AnotherP -> CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node
簡而言之,透過new AnotherP
建立元素或透過new AnotherP(liveParagraph)
升級元素只需更新原型鏈,而不需要元素離開 DOM 或更改其本機性質,因為它保留在原型繼承鏈中。
摘要:無關緊要的註冊表只是升級元素而不改變其性質,這與本機內建擴展底層工作的方式完全相同。
是的,透過/jsx
導出或透過/ref
導出。
關於 /jsx請參閱/jsx 導出是什麼?部分。
關於 /ref請參閱/ref 匯出是什麼?部分。
/ce
匯出以與類別的connectedCallback
、 disconnectedCallback
和attributeChangedCallback
方法及其靜態observedAttributes
欄位相容的方式自動升級元素。
該模組使用已經運行良好的自訂元素模組的微調版本。
請參閱 codepen 上的現場演示,以了解其工作原理。
不。打個比方來說, HTML元素既具有語意意義,又具有明確定義的、期望的實用性,就像JS函數將永遠是JS函數一樣,即使Object.setPrototypeOf(() => {}, Number.prototype)
發生了......你能看到,或同意,這有多麼錯誤?
該模組不想(也可能無法)防止其功能的誤用,因此請確保每當元素升級時,它都會在幕後保留其本機原型鏈,否則您將獨自對抗DOM 。這很不方便?
簡而言之,同樣的方式customElements.define('my-link', class extends HTMLDivElement {}, {extends: 'a'})
沒有意義,這個模組相信它的使用者無意義的類別將有望被避免。
目前,該模組的預設 / main 導出指向完全相同的/core
導出。
因為這個模組以其簡單性和汽件代碼大小打開了潘多拉魔盒,而且主要是因為它仍然落後於0.
版本,所以我正在嘗試考慮索引中應該包含哪些內容,這裡是我的一些想法:
如果有一個基於 ESX 的模組能夠理解以這種方式定義的元件,這不是很酷嗎?
擁有一個透過該模組創建組件的JSX pragma 函數不是很酷嗎?
擁有...(這裡是您的佔位符)...不是很酷嗎?
是的,這會很酷,如果我能決定如何命名預設導出,我很樂意將該名稱與其他優點一起作為該模組的預設條目...請繼續關注,或者請給我關於如何做到這一點的想法和提示
但在那之前,請使用明確匯出來確保將來的更新不會擾亂您的邏輯,如果最近的更改給您帶來了麻煩,我深表歉意,但我很確定您可以輕鬆關聯或理解這是永遠的!
當元素遠距離升級時,這些元素可能附加了一些沒有機會通過其訪問器的屬性。
這個幫助器只是確保繼承的屬性作為自己的元素鍵被刪除,然後立即被當作存取器觸發。
import createRegistry from 'nonchalance/ce';導入訪問器 from 'nonchalance/accessors';const {HTML} = createRegistry();class WithAccessors extends HTML.Div { 建構子(...args){訪問器(super(...args)); } 取得值() {console.log('取得值', this._value);回傳this._value; } 設定值(_value) {this._value = _value;console.log('設定值', this._value); }}// 原生div元素const div = document.createElement('div');div.value = 123;//升級新WithAccessors(div);//重新檢查console.log(div.value);
請觀看現場直播以進行更多測試。
/builtin
匯出(248 位元組)與/core
完全相同,只是它不在幕後使用custom-function
,這意味著:
不可能使用new BuiltIn()
或new BuiltIn(element)
因為這會引發錯誤,除非尚未註冊為customElementbuiltinextend
它可用於自動化組件註冊,如 CodePen 上的現場演示所示
關於此匯出的唯一主要警告是,因為它是基於真正的標準自訂元素,所以 Safari 或 WebKit 可能需要內建的 polyfill,例如:
<!-- 僅適用於Safari 的最頂層頁面腳本--><script>self.chrome ||self.netscape ||document.write('<script src="//unpkg.com/@webreflection/custom- elements -builtin"><x2fscript>');</script>
請注意,雖然預設允許HTML
和SVG
命名空間作為內建擴展,但自訂元素不接受SVG擴展,因此當前/builtin
導出實際上只能進行HTML擴展。
./dummy
導出主要用於SSR ,提供完全相同的實用程式來擴展僅攜帶靜態tag
字段的類別。
結合/tag
可以在遠處進行 100% SSR 和水合物。
從'https://unpkg.com/nonchalance/dummy'導入createRegistry;從'https://unpkg.com/nonchalance/tag'導入createTag;const {HTML} = createRegistry();class HelloDiv延伸HTML.Div { connectCallback() {console.log('我在這裡'); }}// 建立一個可重複使用的命名空間const nmsp = {HelloDiv};// 建立一個標籤Transformerconst tag = createTag(nmsp);// 想像一個伺服器回應// 注意:此程式碼僅用於示範console.log (標籤`<!doctype html><script type="module">從'https://unpkg.com/nonchalance/ce'導入createRegistry;const {HTML} = createRegistry();const nmsp = {};for ( const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; 刪除el.dataset.comp; new nmsp[comp](el);}</script><HelloDiv> < /HelloDiv>` 。 。 .replace('const nmsp = {};',`const nmsp = { ${[...Object.entries(nmsp)].map( ([key, value]) => `${key}: ${值}` ).join(',n')} };` ));
/jsx
匯出(976 位元組)接受額外的createElement
選項並傳回一個jsx
函數,該函數可用作@jsx pragma進行轉換,以及React或Preact中預設已工作的所有其他內容,以及透過HTML或SVG註冊表擴展的類,包括/ce
為這些類別帶來的所有功能:自訂元素,例如生命週期,上面有一些香料:
類別將在其建構函數中接收沿元素傳遞的props ,啟用訊號、其他功能或處理預設JSX元件可能處理的任何內容。
當呼叫建構函式時,該元素將已經填入其子元素,從而避免在解析文件之前定義/註冊類別時標準自訂元素可能出現的惡作劇。
與/builtin
extends 類似, new Component(props)
是不可能的,但<Component {...props} />
總是可以的。
在 CodePen 上查看它在React實踐中的實際應用。
DOM就是DOM ,不論中間有多少間接。根據框架功能的不同,您的 DX 可能會有所不同,但如果Ract是您所追求的,那麼有一種微小而優雅且基於ref
的方法可以使用nonchalance/core或nonchalance/ce來提升常規JSX 節點:
import referenced from 'nonchalance/ref';// 指示組件將作為引用傳遞// 注意:這只是一個授予類完整性的輕量級代理// 無論其在Wild 中的用法如何const Component = referenced(class extends HTML .分區{ 建構子(...args) {super(...args);this.addEventListener('click', console.log); }});ReactDOM.render( <div ref={Component}>點我</div>, 文檔.正文);
ref
實用程式也可以用作裝飾器,並且不會影響常規nonchalance類別的任何功能。另外,每個元素僅升級一次,因此可以安全地在建構函式中新增偵聽器或邏輯。
請在 codepen 上觀看此示範並進行操作。
/selector
匯出(796 位元組)允許即時定義任何 CSS 選擇器,以便相關類別將自動升級與該選擇器相符的任何元素。
請注意,此類選擇器應盡可能唯一,否則可能會發生意外。使用特定的類別或data-
屬性來最好地描述您的選擇器。
import createRegistry from 'nonchalance/core';import { Define } from 'nonchalance/selector';const { HTML } = createRegistry();const Special = Define('[data-comp="special"]', class extends HTML.分區{ constructor(...args) {super(...args);this.textContent = '我很特別! '; }});// 將包含“我很特別!”文字一次 livedocument.body.innerHTML = `<div data-comp="special"></div>`;
./tag
導出(188 位元組)允許以水合友好的方式進行模板轉換。
它可以用作功能齊全的模板文字標籤背後的中間值,一旦這些元素登陸DOM就會發生水合作用。
從 'nonchalance/ce' 導入 createRegistry;從 'nonchalance/tag' 導入 createTag;const {HTML} = createRegistry();class HelloDiv 擴充 HTML.Div { connectCallback() {console.log('我在這裡'); }}// 建立一個可重複使用的命名空間const nmsp = {HelloDiv};// 建立一個標籤Transformerconst tag = createTag(nmsp);// 快速而骯髒的demodocument.body.innerHTML = tag`<HelloDiv /> `.join( '');// 水合例for (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; 刪除 el.dataset.comp; // 升級元素一次 新 nmsp[comp](el);}
在 CodePen 上觀看直播。
是的。預設情況下, /core
和/ce
匯出都可以建立HTML和SVG登錄:
import createRegistry from 'nonchalance/core';const {HTML, SVG} = createRegistry();class Circle 擴充 SVG.Circle { 建構子(options) {Object .assign(super(), options) .setAttribute('fill', 'gold'); } 設定 cx(value) { this.setAttribute('cx', value) } 設定 cy(value) { this.setAttribute('cy', value) } 設定 r(value) { this.setAttribute('r', value) }}document.querySelector('svg').append( 新圓({cx: 100, cy: 100, r: 50}));
在 codepen 上觀看直播。
任何命名空間也可以傳遞給createRegistry(options)
,以{MathML: "http://www.w3.org/1998/Math/MathML"}
為例。
任何對document.createElementNS
有意義的命名空間都是允許的,我們可以升級的DOM元素類型沒有限制。
我之前對此有過很長的回答,但總結是該模組使用W3C 、 WHATWG或ECMAScript提供的標準,並且它需要不到 1KB 的空間即可在任何地方工作。
這不是polyfill,它是一個實用程序,可以幫助您在 JS 世界中編寫組件,並且不用擔心這些組件會發生衝突,需要工具,或者不能跨您喜歡/需要/喜歡的任何目標項目移植。
簡而言之,如果您可以添加少於 1K 位元組來為前端和後端世界提供通用元件,那麼您就選擇了正確的模組?
沒有什麼比成為一個粗心的孩子,在泥地裡與所有「不要那樣做! 」的思想家玩耍更自由的了。
這個模組在某種程度上代表了現代JS 功能提供的自由的感覺,展示了一種優雅、可移植和超輕量級的替代方案,以替代瀏覽器供應商和現代規範所提供的不斷增加的複雜性,所有這些都迫使開發人員解決簡單擴展的能力內建並保留 Web 聞名的簡單性和良好的可訪問性。