Skip to main content

Web Components

Free2017-12-01#Front-End#Web Components标准化#Web Components入门指南#Web Components spec#Web Components status

Web Components, what exactly is it?

I. What?

Web Components are a new browser feature that provides a standard component model for the Web, consisting of several pieces: Shadow DOM, Custom Elements, HTML Imports and HTML Templates.

(Extracted from w3c/webcomponents)

That is to say, Web Components is a Web component model standard, provided by browsers with native feature support, including Shadow DOM, Custom Elements, HTML Imports and HTML Templates

Specification Status

The 4 dependencies, current status (2017/12/1) is as follows:

Completed Work
2014-03-18  HTML Templates  Group Note

Drafts
2017-09-05  Shadow DOM  Working Draft
2016-10-13  Custom Elements  Working Draft
2016-02-25  HTML Imports  Working Draft

Obsolete
2014-07-24  Introduction to Web Components  Retired

(Extracted from All Standards and Drafts - W3C)

So, these 4 dependencies currently only 1 is completed (HTML Templates), and hasn't become a recommended standard yet (just NOTE, didn't become REC). The other 3 are still in Working Draft (WD) status

P.S. For more information about W3C document status like REC, NOTE, etc., please check [W3C Specification Development Process](/articles/w3c 规范制定流程/)

And Web Components specification itself had a version in 2014 (Introduction to Web Components), was retired, currently Web Components is in no specification status, github seems still active

P.S. If really curious, can take a look at the retired version

Implementation Status

Although specification is still in unclear status, but some browsers have provided varying degrees of support for Web Components dependent features:

Shadow DOM
    v0 Chrome25+ Firefox X Safari X Opera15+ Android4.4+ IOS Safari X
    v1 Chrome53+ Firefox X Safari 10+ Opera40+ Android5+ IOS Safari 10.2+
Custom Elements
    v0 Chrome33+ Firefox X Safari X Opera20+ Android4.4.4+ IOS Safari X
    v1 Chrome54+ Firefox X Safari 10.1+ Opera41+ Android5+ IOS Safari 10.3
HTML Imports
    Chrome36+ Firefox X Safari X Opera23+ Android5+ IOS Safari X
HTML Templates
    Chrome26+ Firefox 22 Safari 7.1 Opera15+ Android4.4+ IOS Safari 8

Note: Android5+ refers to Android5-6.x WebView: Chromium 62, and doesn't distinguish partial support and full support

Chrome, Opera green all the way, Safari and mobile roughly keeping up, Firefox just passing through. But from current compatibility situation, PC and mobile are both unusable

P.S. For more information about compatibility, please check Can I use... Support tables for HTML5, CSS3, etc

Additionally, there are polyfills and component libraries to play with:

Function

Web Components hopes to provide standardized component definition method, promote Web component standardization

Let browsers support environmental features needed for componentization solutions, including Shadow DOM, Custom Elements, HTML Imports and Templates

So first let's see what these dependent features can do:

  • Shadow DOM: Sandbox environment, used for component isolation. Components are not affected by external, components also don't affect each other

  • Custom Elements: Component reference method. Reference components in the form of custom elements

  • HTML Imports and Templates: Component resource loading method and component declaration method. Component declaration is placed in <template> tag, load component resources through <link rel="import" href>

We discover the core is component encapsulation, hide component details through Shadow DOM, effect similar to:

<video src="./video.mp4" controls></video>

HTML page containing this snippet will present a fully functional video player, with play button, progress bar, volume control button, etc.

Web Components usage is similar to this:

<my-app>
    <my-nav-bar>
        <my-title>Order Center</my-title>
    </my-nav-bar>
    <my-aside-menu/>
    <my-filter-panel/>
    <my-order-list/>
</my-app>

Package view structure and styles into custom HTML elements, reference in black-box component form. Component is defined in isolated environment (Shadow DOM), HTML, CSS, Script are all safe, external cannot directly change component's internal logic/view state

Of course, besides encapsulation, components at least also lack:

This part of content should be defined in Web Components' own specification (such as the previously retired Introduction), as for higher-level things, they're not within Web Components' consideration scope, after all the first step of component standardization should be from nothing to something

II. Starting from video

Just write one line of video tag:

<video src="./foo.webm" controls></video>

Page can present a fully functional video player, where are the structure definitions and style declarations for play button, progress bar hidden? Could it be like radio buttons and other form elements, rendered by system platform controls? If so, then what about platform-independent parts, such as textbox's placeholder, how is it implemented?

Actually, textbox's placeholder is similar to video, some visible but (in structured document) unfindable elements are all hidden in Shadow DOM:

video, input are equivalent to browser's built-in components, component view structure and default styles are hidden in Shadow DOM, component logic is completely hidden, only exposing autoplay, oninput and other state/behavior Hooks to communicate with external

At this point, we discover it's very similar to Web Components concept, so-called Web Components is nothing more than exposing browser's component mechanism, for Web developers to use, this way we can also happily define "native controls" (components)

III. Shadow DOM

Previously always emphasized hiding things in Shadow DOM, because Shadow DOM's function is equivalent to sandbox, provides component isolation environment

Can play with this feature in Chrome:

After opening Shadow DOM display switch, let's take a look at textbox's hidden part:

<input class="nav-search column-07 start-18" name="s" type="text" placeholder="Hmm. I've seen your little bear.">
  #shadow-root (user-agent)
    <div pseudo="-webkit-input-placeholder" id="placeholder" style="display: block !important; text-overflow: clip;">Hmm. I've seen your little bear.</div>
    <div id="inner-editor"></div>

placeholder quietly stays here, #inner-editor is used to display input text

Using Shadow DOM feature provided by browser, we can create our own Shadow Root:

document.body.innerHTML = '<div class="container"></div>';
var host = document.querySelector('.container');
var root = host.createShadowRoot();
root.innerHTML = '<p>How <em>you</em> doin?</p>'

At this point node structure is like this:

<div class="container"></div>
  #shadow-root (open)
    <p>How <em>you</em> doin?</p>

Page displays "How you doin?", because content under Shadow Root will be presented on page

Shadow Root refers to Fragment returned by createShadowRoot():

host.createShadowRoot() instanceof DocumentFragment === true

Can do DOM operations on Fragment, equivalent to an independent HTML parsing environment, not interfered by external

Additionally there are 2 concepts: Shadow Host and Shadow Boundary

Shadow Host

Shadow Root's "host", connection point between Shadow DOM and DOM, plainly speaking it's where Shadow DOM hangs (the host in above example)

Since there's hosting relationship, let's try:

host.parentElement.removeChild(host)

Find nothing left in body, Shadow DOM is taken down along with host (return value is游离的 div node, at this time Shadow DOM still hangs on div, can append node back to verify)

Shadow Boundary

An abstract concept, refers to the "barrier" layer outside Shadow DOM, it can isolate HTML and CSS under Shadow Root, mutually unaffected with styles in document where Shadow Host is located, and external cannot get node objects under Shadow Root through JS, similar to iframe's isolation effect

This is exactly what Web development has always wanted for module isolation, although can be filled through namespace and other engineering solutions, but there are always some defects that cannot be remedied, fundamental reason is HTML and CSS finally presented on page have no scope concept, restriction means in development stage all become moral constraints here. On the other hand, constraining with engineering means, there may also be problems of too many restrictions causing business to be tied up

Insertion Points

Additionally, it's also necessary to know basic support for component composition:

<div class="breaking">
    <ul>
        <slot name="breaking"></slot> <!-- slot for breaking news -->
    </ul>
</div>
<div class="other">
    <ul>
        <slot></slot> <!-- slot for the rest of the news -->
    </ul>
</div>

(Extracted from Shadow DOM W3C Editor's Draft 14 November 2017)

Two insertion methods, respectively representing named slot and default slot, looks very familiar, Vue templates are written this way, because:

For example, Vue components implement the Slot API and the is special attribute.

As for relationship between Vue and Web Components, we'll talk about it later

IV. Custom Elements

Create custom elements, icing on the cake small thing. Without custom element feature, we need to do this:

<ul class="stories">
    <li><a href="//example.com/stories/1">A story</a></li>
    <li><a href="//example.com/stories/2">Another story</a></li>
    <li class="breaking" slot="breaking"><a href="//example.com/stories/3">Also a story</a></li>
    <li><a href="//example.com/stories/4">Yet another story</a></li>
    <li><a href="//example.com/stories/5">Awesome story</a></li>
    <li class="breaking" slot="breaking"><a href="//example.com/stories/6">Horrible story</a></li>
</ul>

With it, write like this:

<stories>
    <li><a href="//example.com/stories/1">A story</a></li>
    <li><a href="//example.com/stories/2">Another story</a></li>
    <breaking slot="breaking"><a href="//example.com/stories/3">Also a story</a></breaking>
    <li><a href="//example.com/stories/4">Yet another story</a></li>
    <li><a href="//example.com/stories/5">Awesome story</a></li>
    <breaking slot="breaking"><a href="//example.com/stories/6">Horrible story</a></breaking>
</stories>

From semantics perspective, custom elements have stronger expressiveness, and more concise

Custom tags have 2 constraints:

  • Tag name must contain hyphen

  • Prototype must inherit from HTMLElement

Similarly can play with:

document.body.innerHTML = '<template id="tmpl"><p>How <em>you</em> doin?</p><style>em {font-size: 200%;}</style></template><div class="container"></div>';

// Grab our template full of markup and styles
var tmpl = document.querySelector('#tmpl');

// Create a prototype for a new element that extends HTMLElement
var HowYouDoinProto = Object.create(HTMLElement.prototype);

// Setup our Shadow DOM and clone the template
HowYouDoinProto.createdCallback = function() {
var root = this.createShadowRoot();
root.appendChild(document.importNode(tmpl.content, true));
};

// Register our new element
var HowYouDoin = document.registerElement('how-you-doin', {
prototype: HowYouDoinProto
});

document.querySelector('.container').innerHTML = '<how-you-doin/>';

Can get:

<div class="container">
    <how-you-doin>
      #shadow-root (open)
        <p>How <em>you</em> doin?</p>
        <style>em {font-size: 200%;}</style>
    </how-you-doin>
</div>

Presented content is also as we wished: a large-sized you

document.registerElement in above example is Custom Elements feature. As for importNode(), it's just a normal DOM API (not part of Custom Elements), used to clone nodes, otherwise template is one-time use

As for HTML Imports, it's even less important, also has CORS, simple component loading solution, not much different from ajax manually loading components. Before HTTP2.0 era truly arrives, production environment better not split into multiple files

P.S. If interested in HTML Imports, can check online Demo and reference materials

V. Vue and Web Components

Up to now, all examples mentioned above, however you look at it seems like Vue component definition, yes, because Vue follows partial Web Components specification in implementation, such as slot in Shadow DOM:

You may have noticed that Vue components are very similar to Custom Elements, which are part of the Web Components Spec. That's because Vue's component syntax is loosely modeled after the spec. For example, Vue components implement the Slot API and the is special attribute.

Mainly 2 differences:

  1. The Web Components Spec is still in draft status, and is not natively implemented in every browser. In comparison, Vue components don't require any polyfills and work consistently in all supported browsers (IE9 and above). When needed, Vue components can also be wrapped inside a native custom element.
  1. Vue components provide important features that are not available in plain custom elements, most notably cross-component data flow, custom event communication and build tool integrations.

Because Web Components specification is not yet mature, and support is not optimistic, cannot be put into production without polyfills, Vue relies on build tools to cross environment compatibility problems, doesn't rely on browser feature support, but at the same time abandons Shadow DOM encapsulation and other Web Components core advantages

Additionally, Web Components is relatively low-level component specification, Vue besides defining component specification, also provides component communication, data binding and other higher-level solutions

VI. Online Demo

Address: http://www.ayqy.net/temp/web-components/image-slider.html

P.S. A very cleverly implemented pure CSS image slider component with indicators, very interesting, refreshed author's view on general adjacent sibling selector (E ~ F)

Reference Materials

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment