【譯】Web Components簡介

CoyPan發表於2019-03-26

本文翻譯自:css-tricks.com/an-introduc…

前端開發正在以驚人的速度發展。曾經的前端開發,技術棧都是很簡單的,如今卻越來越複雜。這一點從無數的文章、教程和Twitter上就可以看出來。在本文中,我將討論為什麼Web Component是一個很好的工具,可以在不使用複雜框架或構建步驟的情況下提供高質量的使用者體驗,而且不存在過時的風險。在這個由五部分組成的系列的後續文章中,我們將深入研究每個規範。

本系列假設您基本瞭解HTML、CSS和JavaScript。如果您在其中一個領域感到軟弱,不用擔心,構建自定義元素實際上簡化了許多前端開發中的複雜性。

系列文章:

  1. An Introduction to Web Components (本文)
  2. Crafting Reusable HTML Templates
  3. Creating a Custom Element from Scratch
  4. Encapsulating Style and Structure with Shadow DOM
  5. Advanced Tooling for Web Components (This post)

到底什麼是Web Components

Web Components 由三種技術組成。這三種技術會一起使用。

  1. Custome Elements。自定義元素十分簡單,他們都是合法的HTML元素,擁有自定義的模板,表現以及標籤名(例如:)。可以通過一系列的JavaScript API來建立自定義元素。自定義元素是在標準HTML規範中定義的
  2. Shadow DOM。像一個Iframe一樣,可以隔離CSS和JavaScript。這是在標準DOM規範中定義的
  3. HTML templates。使用者在HTML中定義的模板,只有呼叫的時候才會被渲染。<template>標籤是在標準HTML規範中定義的

上述三種技術組成了Web Components規範。

HTML Modules 有可能會成為第四種技術。但是四大瀏覽器都還沒有實現HTML Modules。Chrome團隊已經宣告將在未來的版本中實現HTML Modules

Web Components 在目前所有的主流瀏覽中都是可以使用的,除了微軟的Edge和IE11,但是也有polyfill可以使用。

將上述的三種技術稱為Web Components在技術上來說是準確的,Web Components這個術語本身也有一些超載。因此,每種技術都可以獨立使用或與其他技術結合使用。換句話說,它們並不相互排斥。

讓我們快速看一下上述三種技術。我們將在其他文章中,深入瞭解他們。

Custome elements

就像名字所顯示的那樣,custome elements就是HTML元素,比如</div></section><article>,但是我們通過瀏覽器的API自己給元素命名。自定義元素與標準HTML元素(尖括號中的名稱)一樣,只是它們中總是有一個破折號,如<news-slider><bacon-cheesburger>。更進一步,瀏覽器廠商們都已經承諾不會再新增帶有破折號的內建元素,避免和自定義元素產生衝突。

自定義元素包含自己的語義、行為、標記,並且可以跨框架和瀏覽器共享。

示例可以參考這裡:

codepen.io/calebdwilli…

在這個例子中,我們定義了我們自己的HTML元素。不可否認的是,這個元素做的事情並不多,但是這就是最基本的自定義元素構建方法。所有的自定義元素都必須以某種方式上繼承HTMLElement,這樣才能在瀏覽器上註冊上該元素。

自定義元素不依賴與第三方框架。瀏覽器廠商們正致力於規範的向後相容性,不過都保證只要按照規範編寫的元件都不會受到API大改的影響。更重要的是,這些自定義元素在當今的主流框架(Angular、React、Vue)中,只需要稍做修改,就可以做到開箱即用。

Shadow DOM

Shadow DOM是對DOM的一個封裝。這使得作者能夠有效地將DOM片段彼此隔離開來,包括任何可以用作CSS選擇器的東西以及與之關聯的樣式。通常情況下,document範圍內的內容都被稱為light DOM,shadow root中的內容被稱為shadow DOM。

當我們使用light DOM的時候,我們可以通過document.querySelector('selector')來選中某個DOM,或者通過element.querySelector('selector')來獲取每個元素的子元素。同樣的,shadow root的子元素可以通過shadowRoot.querySelector來獲取,這裡的shadowRoot是一個document fragment。不同的地方是,shadow root的子元素無法在light dom中被選中。例如,如果我們的shadow root中有一個<button>,使用shadowRoot.querySelector('button')可以得到我們的button,但是呼叫document的querySelector就取不到這個button,因為這個button屬於不同的DocumentOrShadowRoot例項。樣式選擇器也是一樣的。

從某種意義上來說,shadow dom有點像一個iframe,其中的內容與document的其他部分隔開了。不過,當我們建立一個shadow root的時候,我們仍然可以完全控制頁面中這部分的內容,只是說需要在一定的作用域下。這就是我們所說的封裝。

如果你曾經寫過一個重用相同的id或者依賴CSS-in-JS工具,CSS命名策略(比如BEM)的元件,shadow dom或許提升你的開發體驗。

想象一下如下的場景:

<div>
  <div id="example">
    <!-- Pseudo-code used to designate a shadow root -->
    <#shadow-root>
      <style>
      button {
        background: tomato;
        color: white;
      }
      </style>
      <button id="button">This will use the CSS background tomato</button>
    </#shadow-root>
  </div>
  <button id="button">Not tomato</button>
</div>
複製程式碼

除了<#shadow-root>的虛擬碼(沒有HTML元素,用來分割shadow的邊界),HTML是完全合法的。為了實現上面的HTML,我們需要執行下面的程式碼:

const shadowRoot = document.getElementById('example').attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `<style>
button {
  color: tomato;
}
</style>
<button id="button">This will use the CSS color tomato <slot></slot></button>`;
複製程式碼

shadow root也可以使用元素來從其內部的文件內容中包含內容。使用slot可以把外部文件中的使用者內容放到shadow root目錄中的指定位置。

示例可以參考這裡:

codepen.io/calebdwilli…

HTML templates

適當命名的HTML<template>元素允許我們在正常HTML流中消除程式碼的可重用模板。這些模板不會立即被渲染,但可以在以後使用。

<template id="book-template">
  <li><span class="title"></span> &mdash; <span class="author"></span></li>
</template>

<ul id="books"></ul>
複製程式碼

上面的例子不會渲染任何的東西,直到我們使用script操作了這個模板,例項化程式碼並告訴瀏覽器如何操作它。

const fragment = document.getElementById('book-template');
const books = [
  { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },
  { title: 'A Farewell to Arms', author: 'Ernest Hemingway' },
  { title: 'Catch 22', author: 'Joseph Heller' }
];

books.forEach(book => {
  // Create an instance of the template content
  const instance = document.importNode(fragment.content, true);
  // Add relevant content to the template
  instance.querySelector('.title').innerHTML = book.title;
  instance.querySelector('.author').innerHTML = book.author;
  // Append the instance ot the DOM
  document.getElementById('books').appendChild(instance);
});
複製程式碼

請注意,此示例建立了一個模板(<template id=“book template”>),而不使用任何其他Web Components技術,再次說明Web Components中的三種技術可以獨立使用或共同使用。

表面上,可以使用模板API書寫一個任意結構的模板,並且在後續的程式碼中建立這個模板。站點上的另外一個頁面可能會使用相同的服務,但是使用另外的結構來建立模板:

<template id="book-template">
  <li><span class="author"></span>'s classic novel <span class="title"></span></li>
</template>

<ul id="books"></ul>
複製程式碼

可以點選這裡檢視示例:

codepen.io/calebdwilli…

總結

隨著Web開發越來越複雜,像我們這樣的開發者開始將越來越多的開發推遲到Web平臺本身,而Web平臺本身也在不斷成熟。Web Components規範是一組低階API,隨著開發者的發展,這些API將隨著我們的需求不斷增長和發展。

相關文章