摘要:它賦予了僅僅使用純粹的就可以創(chuàng)建可重用組件的能力。我們會(huì)用到的來創(chuàng)建我們的用戶卡片。在較早版本的瀏覽器中,我們不能使用來隔離組件。這可以部分歸咎于對(duì)的影響很大的。你可以在這里閱讀第二部分的教程使用純粹的構(gòu)建
原文鏈接:https://ayushgp.github.io/htm...
譯者:阿里云 - 也樹
Web Component 出現(xiàn)有一陣子了。 Google 費(fèi)了很大力氣去推動(dòng)它更廣泛的應(yīng)用,但是除 Opera 和 Chrome 以外的多數(shù)主流瀏覽器對(duì)它的支持仍然不夠理想。
但是通過 polyfill,你可以從現(xiàn)在開始構(gòu)建你自己的 Web Component,你可以在這里找到相關(guān)支持:https://www.webcomponents.org/polyfills
在這篇文章中,我會(huì)演示如何創(chuàng)建帶有樣式,擁有交互功能并且在各自文件中優(yōu)雅組織的 HTML 標(biāo)簽。
介紹Web Component 是一系列 web 平臺(tái)的 API,它們可以允許你創(chuàng)建全新可定制、可重用并且封裝的 HTML 標(biāo)簽,從而在普通網(wǎng)頁及 web 應(yīng)用中使用。
定制的組件基于 Web Component 標(biāo)準(zhǔn)構(gòu)建,可以在現(xiàn)在瀏覽器上使用,也可以和任意與 HTML 交互的 JavaScript 庫和框架配合使用。
用于支持 Web Component 的特性正逐漸加入 HTML 和 DOM 的規(guī)范,web 開發(fā)者使用封裝好樣式和定制行為的新元素來拓展 HTML 會(huì)變得輕而易舉。
它賦予了僅僅使用純粹的JS/HTML/CSS就可以創(chuàng)建可重用組件的能力。如果 HTML 不能滿足需求,我們可以創(chuàng)建一個(gè)可以滿足需求的 Web Component。
舉個(gè)例子,你的用戶數(shù)據(jù)和一個(gè) ID 有關(guān),你希望有一個(gè)可以填入用戶 ID 并且可以獲取相應(yīng)數(shù)據(jù)的組件。HTML 可能是下面這個(gè)樣子:
這是一個(gè) Web Component 最基本的應(yīng)用。下面的教程將會(huì)聚焦在如何構(gòu)建這個(gè)用戶卡片組件。
Web Component 的四個(gè)核心概念HTML 和 DOM 標(biāo)準(zhǔn)定義了四種新的標(biāo)準(zhǔn)來幫助定義 Web Component。這些標(biāo)準(zhǔn)如下:
定制元素(Custom Elements): web 開發(fā)者可以通過定制元素創(chuàng)建新的 HTML 標(biāo)簽、增強(qiáng)已有的 HTML 標(biāo)簽或是二次開發(fā)其它開發(fā)者已經(jīng)完成的組件。這個(gè) API 是 Web Component 的基石。
HTML 模板(HTML Templates): HTML 模板定義了新的元素,描述一個(gè)基于 DOM 標(biāo)準(zhǔn)用于客戶端模板的途徑。模板允許你聲明標(biāo)記片段,它們可以被解析為 HTML。這些片段在頁面開始加載時(shí)不會(huì)被用到,之后運(yùn)行時(shí)會(huì)被實(shí)例化。
Shadow DOM: Shadow DOM 被設(shè)計(jì)為構(gòu)建基于組件的應(yīng)用的一個(gè)工具。它可以解決 web 開發(fā)的一些常見問題,比如允許你把組件的 DOM 和作用域隔離開,并且簡化 CSS 等等。
HTML 引用(HTML Imports): HTML 模板(HTML Templates)允許你創(chuàng)建新的模板,同樣的,HTML 引用(HTML imports)允許你從不同的文件中引入這些模板。通過獨(dú)立的HTML文件管理組件,可以幫助你更好的組織代碼。
定義定制元素我們首先需要聲明一個(gè)類,定義元素如何表現(xiàn)。這個(gè)類需要繼承 HTMLElement 類,但讓我們先繞過這部分,先來討論定制元素的生命周期方法。你可以使用下面的生命周期回調(diào)函數(shù):
connectedCallback — 每當(dāng)元素插入 DOM 時(shí)被觸發(fā)。
disconnectedCallback — 每當(dāng)元素從 DOM 中移除時(shí)被觸發(fā)。
attributeChangedCallback — 當(dāng)元素上的屬性被添加、移除、更新或取代時(shí)被觸發(fā)。
在 UserCard 文件夾下創(chuàng)建 UserCard.js:
class UserCard extends HTMLElement { constructor() { super(); this.addEventListener("click", e => { this.toggleCard(); }); } toggleCard() { console.log("Element was clicked!"); } } customElements.define("user-card", UserCard);
這個(gè)例子里我們已經(jīng)創(chuàng)建了一個(gè)定義了定制元素行為的類。customElements.define("user-card", UserCard); 函數(shù)調(diào)用告知 DOM 我們已經(jīng)創(chuàng)建了一個(gè)新的定制元素叫 user-card,它的行為被 UserCard 類定義?,F(xiàn)在可以在我們的 HTML 里使用 user-card 元素了。
我們會(huì)用到 https://jsonplaceholder.typicode.com/ 的 API 來創(chuàng)建我們的用戶卡片。下面是數(shù)據(jù)的樣例:
{ id: 1, name: "Leanne Graham", username: "Bret", email: "[email protected]", address: { street: "Kulas Light", suite: "Apt. 556", city: "Gwenborough", zipcode: "92998-3874", geo: { lat: "-37.3159", lng: "81.1496" } }, phone: "1-770-736-8031 x56442", website: "hildegard.org" }創(chuàng)建模板
現(xiàn)在,讓我們創(chuàng)建一個(gè)將在屏幕上渲染的模板。創(chuàng)建一個(gè)名為 UserCard.html 的新文件,內(nèi)容如下:
注意:我們在類名前加了一個(gè) card__ 前綴。在較早版本的瀏覽器中,我們不能使用 shadow DOM 來隔離組件 DOM。這樣當(dāng)我們?yōu)榻M件編寫樣式時(shí),可以避免意外的樣式覆蓋。
編寫樣式我們創(chuàng)建好了卡片的模板,現(xiàn)在來用 CSS 裝飾它。創(chuàng)建一個(gè) UserCard.css 文件,內(nèi)容如下:
.card__user-card-container { text-align: center; display: inline-block; border-radius: 5px; border: 1px solid grey; font-family: Helvetica; margin: 3px; width: 30%; } .card__user-card-container:hover { box-shadow: 3px 3px 3px; } .card__hidden-content { display: none; } .card__details-btn { background-color: #dedede; padding: 6px; margin-bottom: 8px; }
現(xiàn)在,在 UserCard.html 文件的最前面引入這個(gè) CSS 文件:
樣式已經(jīng)就緒,接下來可以繼續(xù)完善我們組件的功能。
connectedCallback現(xiàn)在我們需要定義創(chuàng)建元素并且添加到 DOM 中會(huì)發(fā)生什么。注意這里 constructor 和 connectedCallback 方法的區(qū)別。
constructor 方法是元素被實(shí)例化時(shí)調(diào)用,而 connectedCallback 方法是每次元素插入 DOM 時(shí)被調(diào)用。connectedCallback 方法在執(zhí)行初始化代碼時(shí)是很有用的,比如獲取數(shù)據(jù)或渲染。
小貼士: 在 UserCard.js 的頂部,定義一個(gè)常量 currentDocument。它在被引入的 HTML 腳本中是必要的,允許這些腳本有途徑操作引入模板的 DOM。像下面這樣定義:
const currentDocument = document.currentScript.ownerDocument;
接下來定義我們的 connectedCallback 方法:
// 元素插入 DOM 時(shí)調(diào)用 connectedCallback() { const shadowRoot = this.attachShadow({mode: "open"}); // 選取模板并且克隆它。最終將克隆后的節(jié)點(diǎn)添加到 shadowDOM 的根節(jié)點(diǎn)。 // 當(dāng)前文檔需要被定義從而獲取引入 HTML 的 DOM 權(quán)限。 const template = currentDocument.querySelector("#user-card-template"); const instance = template.content.cloneNode(true); shadowRoot.appendChild(instance); // 從元素中選取 user-id 屬性 // 注意我們要像這樣指定卡片: //渲染用戶數(shù)據(jù)const userId = this.getAttribute("user-id"); // 根據(jù) user ID 獲取數(shù)據(jù),并且使用返回的數(shù)據(jù)渲染 fetch(`https://jsonplaceholder.typicode.com/users/${userId}`) .then((response) => response.text()) .then((responseText) => { this.render(JSON.parse(responseText)); }) .catch((error) => { console.error(error); }); }
我們已經(jīng)定義好了 connectedCallback 方法,并且把克隆好的模板綁定到了 shadow root 上?,F(xiàn)在我們需要填充模板內(nèi)容,然后在 fetch 方法獲取數(shù)據(jù)后觸發(fā) render 方法。下面來編寫 render 和 toggleCard 方法。
render(userData) { // 使用操作 DOM 的 API 來填充卡片的不同區(qū)域 // 組件的所有元素都存在于 shadow dom 中,所以我們使用了 this.shadowRoot 這個(gè)屬性來獲取 DOM // DOM 只可以在這個(gè)子樹種被查找到 this.shadowRoot.querySelector(".card__full-name").innerHTML = userData.name; this.shadowRoot.querySelector(".card__user-name").innerHTML = userData.username; this.shadowRoot.querySelector(".card__website").innerHTML = userData.website; this.shadowRoot.querySelector(".card__address").innerHTML = `Address
${userData.address.suite},
${userData.address.street},
${userData.address.city},
Zipcode: ${userData.address.zipcode}` } toggleCard() { let elem = this.shadowRoot.querySelector(".card__hidden-content"); let btn = this.shadowRoot.querySelector(".card__details-btn"); btn.innerHTML = elem.style.display == "none" ? "Less Details" : "More Details"; elem.style.display = elem.style.display == "none" ? "block" : "none"; }
既然組件已經(jīng)完成,我們就可以把它用在任意項(xiàng)目中了。為了繼續(xù)教程,我們需要?jiǎng)?chuàng)建一個(gè) index.html 文件,然后寫入下面的代碼:
Web Component
因?yàn)椴⒉皇撬袨g覽器都支持 Web Component,我們需要引入 webcomponents.js 這個(gè)文件。注意我們用到 HTML 引用語句來引入我們的組件。
為了運(yùn)行這些代碼,你需要?jiǎng)?chuàng)建一個(gè)靜態(tài)文件服務(wù)器。如果你不清楚如何創(chuàng)建,你可以使用像 static-server 或者 json-server 這樣的簡易靜態(tài)服務(wù)。教程里,我們安裝 static-server:
$ npm install -g static-server
接著在你的項(xiàng)目目錄下,使用下面的命令運(yùn)行服務(wù)器:
$ static-server
打開你的瀏覽器并訪問localhost:3000,你就可以看到我們剛剛創(chuàng)建的組件了。
小貼士和技巧還有很多關(guān)于 Web Component 的東西沒有在這篇短文中寫到,我想簡單的陳述一些開發(fā) Web Component 的小貼士和技巧。
組件的命名定制元素的名稱必須包含一個(gè)短橫線。所以
在 HTML 添加新標(biāo)簽時(shí)需要確保向前兼容,不能重復(fù)注冊同一個(gè)標(biāo)簽。
定制元素標(biāo)簽不能是自閉合的,因?yàn)?HTML 只允許一部分元素可以自閉合。需要寫成像
創(chuàng)建組件時(shí)可以使用繼承的方式。舉個(gè)例子,如果想要為兩種不同的用戶創(chuàng)建一個(gè) UserCard,你可以先創(chuàng)建一個(gè)基本的 UserCard 然后將它拓展為兩種特定的用戶卡片。想要了解更多組件繼承的知識(shí),可以查看Google web developers’ article。
Lifecycle Callbacks生命周期回調(diào)函數(shù)我們創(chuàng)建了當(dāng)元素加入 DOM 后自動(dòng)觸發(fā)的 connectedCallback 方法。我們同樣有元素從 DOM 中移除時(shí)觸發(fā)的 disconnectedCallback 方法。 attributesChangedCallback(attribute, oldval, newval)方法會(huì)在我們改變定制組件的屬性時(shí)被觸發(fā)。
組件元素是類的實(shí)例既然組件元素是類的實(shí)例,就可以在這些類中定義公用方法。這些公用方法可以用來允許其它定制組件/腳本來和這些組件產(chǎn)生交互,而不是只能改變這些組件的屬性。
定義私有方法可以通過多種方式定義私有方法。我傾向于使用(立即執(zhí)行函數(shù)),因?yàn)樗鼈円讓懞鸵桌斫?。舉個(gè)例子,如果你創(chuàng)建的組件有非常復(fù)雜的內(nèi)部功能,你可以像下面這樣做:
(function() { // 使用第一個(gè)self參數(shù)來定義私有函數(shù) // 當(dāng)調(diào)用這些函數(shù)時(shí),從類中傳遞參數(shù) function _privateFunc(self, otherArgs) { ... } // 現(xiàn)在函數(shù)只可以在你的類的作用域中可用 class MyComponent extends HTMLElement { ... // 定義下面這樣的函數(shù)可以讓你有途徑和這個(gè)元素交互 doSomething() { ... _privateFunc(this, args) } ... } customElements.define("my-component", MyComponent); })()凍結(jié)類
為了防止新的屬性被添加,需要凍結(jié)你的類。這樣可以防止類的已有屬性被移除,或者已有屬性的可枚舉、可配置或可寫屬性被改變,同樣也可以防止原型被修改。你可以使用下面的方法:
class MyComponent extends HTMLElement { ... } const FrozenMyComponent = Object.freeze(MyComponent); customElements.define("my-component", FrozenMyComponent);
注意: 凍結(jié)類會(huì)阻止你在運(yùn)行時(shí)添加補(bǔ)丁并且會(huì)讓你的代碼難以調(diào)試。
結(jié)論這篇關(guān)于 Web Component 的教程作用非常有限。這可以部分歸咎于對(duì) Web Component 的影響很大的 React。我希望這篇文章可以提供給你足夠的信息來讓你嘗試不添加任何依賴來構(gòu)建自己的定制組件。你可以在 定制組件 API 規(guī)范(Custom components API spec) 找到更多關(guān)于 Web Component 的信息。
你可以在這里閱讀第二部分的教程:使用純粹的JS構(gòu)建 Web Component - Part 2!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/112897.html
摘要:它賦予了僅僅使用純粹的就可以創(chuàng)建可重用組件的能力。我們會(huì)用到的來創(chuàng)建我們的用戶卡片。在較早版本的瀏覽器中,我們不能使用來隔離組件。這可以部分歸咎于對(duì)的影響很大的。你可以在這里閱讀第二部分的教程使用純粹的構(gòu)建 原文鏈接:https://ayushgp.github.io/htm...譯者:阿里云 - 也樹 Web Component 出現(xiàn)有一陣子了。 Google 費(fèi)了很大力氣去推動(dòng)它更...
摘要:它賦予了僅僅使用純粹的就可以創(chuàng)建可重用組件的能力。我們會(huì)用到的來創(chuàng)建我們的用戶卡片。在較早版本的瀏覽器中,我們不能使用來隔離組件。這可以部分歸咎于對(duì)的影響很大的。你可以在這里閱讀第二部分的教程使用純粹的構(gòu)建 原文鏈接:https://ayushgp.github.io/htm...譯者:阿里云 - 也樹 Web Component 出現(xiàn)有一陣子了。 Google 費(fèi)了很大力氣去推動(dòng)它更...
閱讀 2640·2021-11-18 10:07
閱讀 1090·2021-08-03 14:04
閱讀 732·2019-08-30 13:08
閱讀 2586·2019-08-29 15:33
閱讀 1101·2019-08-29 14:07
閱讀 3002·2019-08-29 14:04
閱讀 1447·2019-08-29 11:19
閱讀 1154·2019-08-29 10:59