翻譯:瘋狂的技術宅
原文: https://www.smashingmagazine....
本文首發微信公衆號:jingchengyideng
歡迎關注,天天都給你推送新鮮的前端技術文章javascript
摘要:本文着眼於使用具備內置功能和樣式的組件來擴充HTML。 咱們還將學習如何經過 NPM 使這些自定義元素在項目中獲得重用。css
即使是最簡單的組件,人力成本也可能很高。 UX 團隊要進行可用性測試。 涉及到的利益相關者必須對設計簽字確認。html
以後是開發人員進行 AB 測試,可訪問性審計,單元測試和跨瀏覽器檢查。 一旦解決了這個問題,你就不想再次重複這項工做了。 經過構建可重用的組件庫(而不是從頭開始構建全部內容),咱們就能夠不斷複用過去的工做,避免從新審視已經解決的設計和開發過程。前端
構建組件庫對於像Google這樣的公司尤其重要,他們擁有不少具備相同品牌的網站。 經過把 UI 編碼爲可組合小部件,這些大公司既能夠減小開發時間,又能夠實現跨項目的可視化和用戶交互設計的一致性。在過去幾年中,人們對樣式指南和模式庫的興趣不斷增長。因爲開發人員和設計師通常都分佈在多個團隊中,因此大公司須要尋求實現一致性的方法,好比提供簡單的顏色樣本。不過對於咱們來講,能夠比他們作得更好。 咱們須要的是易於分發的代碼。java
手動複製和粘貼代碼很容易。可是把代碼保持在最新版是維護上的噩夢。因此許多開發者依賴包管理器來跨項目重用代碼。 儘管名字是 Node Package Manager, 可是它已成爲前端包管理的獨一無二的平臺。 目前在 NPM 上註冊的包超過700,000個,每個月下載數十億次。 含有 package.json 文件的任何文件夾均可以做爲可共享包上傳到NPM。 雖然NPM主要與JavaScript相關聯,但包中也能夠包含 CSS 和標記。 NPM使重用變得很容易,這對更新代碼尤其重要:你無需在各類地方修改代碼,所作的是隻需在包中更新代碼便可。node
使用 import 語句能夠對Sass和Javascript 進行輕鬆移植。 模板語言賦予了 HTML 相同的能力 —— 模板能以局部形式導入到 HTML 的其餘片斷。 好比你能夠只需爲頁腳編寫一次標記,而後將其包含在其餘模板中便可。 另外一種方法是複製並粘貼標記,並只對樣式和 javascript 使用NPM。git
這是英國「金融時報」在 Origami 組件庫中用到的方法。Alice Bartlett在她的演講中總結道:「你不能讓它更像是 Bootstrap 嗎?」,「並無什麼好辦法能讓人們在他們的項目中包含模板」。
Ian Feather談到他在 Lonely Planet 維護組件庫的經歷,重申了這種方法存在的問題:github
「一旦複製了這些代碼,他們基本上就會削減一個須要無限期維護的版本。當複製工做組件的標記時,它具備到該點的CSS快照的隱式連接。 若是你隨後更新模板或重構CSS,則須要更新分散在你網站周圍的全部模板的版本。「
Web組件經過在 JavaScript 中定義標記來解決這個問題。 組件的做者能夠自由地修改標記、CSS 和 Javascript。 組件的使用者能夠在這些升級中受益,無需手動修改項目代碼。 只須要經過在終端的敲出簡短的 npm update
命令,就能夠在項目範圍內更新到最新版本。固然前提是組件的名稱及其 API 須要保持一致。web
安裝Web組件就像在終端中鍵入 npm install component-name
同樣簡單。 Javascript 能夠包含在 import 語句中:npm
<script type="module"> import './node_modules/component-name/index.js'; </script>
在CodePen上的代碼演示:https://codepen.io/cssgrid/pe...
在前端開發中,以組件爲中心的方法已經變得無處不在,Facebook 的 React 框架就使用了這種方法。考慮到在現代前端開發工做中框架的廣泛性,許多公司已經在用他們選擇的框架構建了組件庫。這些組件只能在該特定框架內重用。
IBM Carbon Design System的一個組件。 僅能用於 React 應用。其餘在 React 中構建的組件庫的主要案例包括Atlassian 的 Atlaskit 和 Shopify 的 Polaris。
對規模較大的公司來講,不多有統一的前端,從一個框架轉到另外一個框架的從新佈局並不罕見。各類框架你方唱罷我登場。 爲了在項目中實現最大程度的潛在重用,咱們須要與框架無關的組件。
經過在npmjs.com對組件的搜索結果揭示了一個支離破碎的Javascript生態系統。
隨着時間的推移,框架也在不斷變化。
「多年來我使用 Dojo、Mootools、Prototype、jQuery、Backbone、Thorax 和 React 構建了 Web 應用......我但願能把我開發的 Dojo 組件用到如今的 React 應用中。「—— 谷歌工程總監Dion Almaer
當咱們談論Web組件時,咱們討論的是自定義元素與 shadow DOM 的組合。 自定義元素和 shadow DOM 是W3C DOM 規範和 WHATWG DOM 標準的一部分 —— 這意味着 Web 組件是 Web 標準的一部分。自定義元素和 shadow DOM 最終會實現跨瀏覽器支持。 經過使用原生 Web 平臺的標準部分,咱們確保本身的組件可以在前端重組和不斷重構的快速變化週期中生存下來。 Web組件能夠與任何一種模板語言和前端框架一塊兒使用 —— 它們是真正交叉兼容和可互操做的。 從 Wordpress 博客到單頁應用程序,能夠在任何場合下使用。
Rob Dodson的 Custom Elements Everywhere 項目記錄了 Web 組件與各類客戶端 Javascript 框架的互操做性。 這裏面 React 出現的異常值,但願能在 React 17 中解決。
生成 made-up-tag 標記並使其內容顯示在頁面上。
<made-up-tag>Hello World!</made-up-tag>
HTML 被設計爲可以容錯。即便不是有效的HTML元素,它的內容也會被呈現。 並無一個很好的理由這樣作 —— 偏離標準化標籤在傳統上是不好勁的作法。 可是經過用自定義元素 API 定義新的標記,咱們就能夠用具備內置功能的可重用元素來擴充HTML。 建立自定義元素很像在 React 中建立一個組件 —— 但在這裏是擴展了 HTMLElement
。
class ExpandableBox extends HTMLElement { constructor() { super() } }
構造函數中的第一個語句必須是對 super()
的無參數調用。構造函數應該用於設置初始狀態和默認值,以及設置事件偵聽器。 須要使用其 HTML 標記的名稱和對應的類的元素定義新的自定義元素:
customElements.define('expandable-box', ExpandableBox)
把類名大寫是一種慣例。 HTML 標記的語法不只僅是一種約定,若是瀏覽器想要實現一個新的HTML元素,而且想把它稱爲可擴展框怎麼辦?爲了防止命名衝突,不是最新標準的 HTML 標記要包含破折號。 因此自定義元素的名稱也 必須 包含破折號。
customElements.define('whatever', Whatever) // 無效 customElements.define('what-ever', Whatever) // 有效
API 提供了四種自定義元素響應 —— 能夠在類中定義函數,這些函數會自動調用來響應自定義元素生命週期中的某些事件。
當自定義元素添加到 DOM 時會執行 connectedCallback。
connectedCallback() { console.log("custom element is on the page!") }
這包括用 Javascript 添加元素:
document.body.appendChild(document.createElement("expandable-box")) // "custom element is on the page"
以及簡單地在頁面中包含帶有 HTML 標記的元素:
<expandable-box></expandable-box> // "custom element is on the page"
全部涉及到獲取資源或渲染的工做都應該在這裏。
從 DOM 中刪除自定義元素時,將運行 disconnectedCallback。
disconnectedCallback() { console.log("element has been removed") } document.querySelector("expandable-box").remove() //"element has been removed"
當自定義元素被採用到新文檔中時,將會運行 adoptedCallback
。 你可能不須要太關心這個問題。
在添加、更改或刪除屬性時運行 attributeChangedCallback
。 它能夠用於監聽標準化本機屬性(如 disabled 或 src )的更改,以及咱們定義的任何自定義屬性。 這是自定義元素最強大的功能之一,由於它能夠建立用戶友好的 API。
由於有不少 HTML 屬性,因此當任何屬性發生變化時,瀏覽器都不會浪費時間去調用咱們的 attributeChangedCallback
,所以須要提供一個咱們想要監聽的屬性更改列表。對於這個例子,咱們只對一個感興趣。
static get observedAttributes() { return ['expanded'] }
因此如今 attributeChangedCallback
只會在咱們更改自定義元素上expanded
屬性的值時被調用,由於它是咱們列出的惟一屬性。
HTML 屬性能夠有相應的值(例如 href,src,alt,value 等),而其餘值能夠是true或false (例如 disabled, selected, required)。 對於具備相應值的屬性,咱們將在自定義元素的類定義中包含如下內容。
get yourCustomAttributeName() { return this.getAttribute('yourCustomAttributeName'); } set yourCustomAttributeName(newValue) { this.setAttribute('yourCustomAttributeName', newValue); }
對於例子中的元素,其屬性爲 true 或 false,所以定義 getter 和 setter 會有所不一樣。
get expanded() { return this.hasAttribute('expanded') } // setAttribute的第二個參數是必需的,因此咱們將用一個空字符串填充 set expanded(val) { if (val) { this.setAttribute('expanded', ''); } else { this.removeAttribute('expanded') } }
既然已經處理了樣板文件,咱們可使用 attributeChangedCallback
。
attributeChangedCallback(name, oldval, newval) { console.log(`the ${name} attribute has changed from ${oldval} to ${newval}!!`); // 每次屬性被更改時執行某些操做 }
配置 Javascript 組件會涉及將參數傳遞給 init 函數。 經過使用 attributeChangedCallback
,能夠建立一個可使用標記配置的自定義元素。
Shadow DOM 和自定義元素能夠單獨使用,你能夠找到對本身有用的自定義元素。 與 shadow DOM 不一樣,它們能夠是 Polyfill。 不過它們配合得很好。
到目前爲止,咱們已經處理了自定義元素的行爲。 可是關於標記和樣式,咱們的自定義元素至關於空的無樣式 <span>
。 要將HTML和CSS封裝爲組件的一部分,還須要附加一個shadow DOM。 最好在構造函數中執行此操做。
class FancyComponent extends HTMLElement { constructor() { super() var shadowRoot = this.attachShadow({mode: 'open'}) shadowRoot.innerHTML = `<h2>hello world!</h2>` }
不要爲理解模式的含義擔憂——你必須包含它的樣板,但你幾乎老是想要 open
。 這個簡單的例子組件將只呈現文本「hello world」。 與大多數其餘 HTML 元素同樣,自定義元素能夠包含子元素 —— 但默認狀況下不是。 到目前爲止,前面的自定義元素還不能將任何子元素渲染到屏幕上。 要顯示標記之間的內容,還須要用到 slot
元素。
shadowRoot.innerHTML = ` <h2>hello world!</h2> <slot></slot> `
咱們能夠用樣式標記將 CSS 應用於組件。
shadowRoot.innerHTML = `<style> p { color: red; } </style> <h2>hello world!</h2> <slot>some default content</slot>`
這些樣式僅適用於組件,所以咱們能夠自由地使用元素選擇器,而不會影響頁面的任何其餘內容。 這就把編寫 CSS 的過程變得很是簡單,使 BEM 這樣的命名約定變得沒必要要。
NPM 包經過命令行進行發佈。 打開一個終端窗口並切換到你想要變成可重用包的目錄中,而後在終端中鍵入如下命令:
npm init
將會引導你建立一個。npm adduser
把你的機器連接到你的 NPM 賬戶。 若是你尚未註冊,它將爲你建立一個新的帳號。npm publish
若是一切順利的話,在 NPM 列表中會出現你的組件,能夠在你本身的項目中安裝和使用 —— 並與全世界共享。
Web組件API並不完美。自定義元素目前還沒法在表單提交中包含數據。 漸進式加強並非很好,對可訪問性的處理並不怎麼容易。
儘管最初在 2011 年宣佈,但瀏覽器尚未廣泛支持。 Firefox 將在今年(2018)晚些時候提供支持。 儘管如此,一些備受矚目的網站(如 Youtube )已經在使用它們。 儘管目前還存在缺點,但對於廣泛可共享的組件而言,它們是惟一的選擇,而且在將來它們提供的使人興奮的新功能很是值得期待。