Web Components is a w3C-driven standard that aims to enrich HTML’s DOM features and make HTML more reusable.

One, no contrast, no harm

Looking at the development history of front-end technology, code reuse and modularity are always the topic, let’s take a look at the three front-end components (CSS, HTML, JavaScript) performance.

CSS has the @import URL syntax, sass, less, stylus, CSS Modules, and a host of other implementations. The native CSS system also supports variable definition and use:

:root {
  --first-color: #488cff;
  --second-color: #ffff8c;
}
#firstParagraph {
  background-color: var(--first-color);
  color: var(--second-color);
}
Copy the code

JavaScript is no less impressive in that IIFE mode and prototype mode are fundamental to its logic reuse capabilities. In recent years, CMD, AMD, CommonJS and other specifications of JS modular continue to increase, ES6 version is the official father strongly support. HTML, by comparison, is a bit bleak.

There seems to be an early HTML syntax that supports importing HTML fragments , but I haven’t used it and won’t discuss it too much. This syntax is no longer supported by MDN. HTML Imports

Of course, HTML is not so out of place in the trend of reuse and componentalization, after all, the most popular Vue, React, Ng are powerful frameworks for implementing custom HTML components. However, different frameworks, different grammar and usage styles, and different learning costs are not a long-term solution.

Just as the saying goes, the world trend, long will be divided, divided will be.

Whether Web Components will usher in a new era of unity, let’s take a closer look.

Second, Web Components foundation

1. SanBanFu

The first thing to be clear about is that Web Components is a standard that currently includes three main techniques that can be used together to create custom elements that encapsulate functionality and can be reused anywhere you like without worrying about code conflicts.

The three technologies are (extracted from MDN) :

  • Custom Elements: A set of JavaScript apis that allow you to define Custom elements and their behavior, and then use them as needed in your user interface.
  • Shadow DOM: A set of JavaScript apis for attaching a wrapped “Shadow” DOM tree to an element (rendered separately from the main document DOM) and controlling its associated functionality. This way, you can keep the functionality of elements private so that they can be scripted and styled without fear of running afoul of the rest of the document.
  • HTML Templates: <template><slot>The element lets you write tag templates that are not displayed on rendered pages. They can then be reused many times as a basis for custom element structures.

Without further ado, let’s see how to use them.

2. See chestnuts 🌰

Suppose we want to implement a blog list page that looks something like this:

Following the idea of componentization, we want each blog post to be a single component that can be used independently. First we use template to construct the blog component content structure:

<template id="tpl">
  <style>
    article {width: 20%; margin: 20px auto; border: solid 1px gray; padding: 8px; }header {
      background: lightblue; color: #fff;
      font-size: 24px; border: solid 1px lightblue;
    }
  </style>
  <article>
    <header>Blog title</header>
    <section>Blog Content Blog Content Blog Content Blog Content Blog Content Blog Content...</section>
  </article>
</template>
Copy the code

Then use the customElement to create a custom component:

class SelfDiv extends HTMLElement {
  constructor() {
    super(a)document.body.appendChild(tpl.content.cloneNode(true))
  }
}
customElements.define('my-blog', SelfDiv)
Copy the code

Finally, use the
tag on the page to get the content we want. Please click here for detailed code. At this point, HTML native built-in custom component functions fully meet the needs, the market a variety of heady front-end framework can be put behind, the end of the Sa flowers, (°▽°) Blue ✿!!

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = a reasonable line = = = = = = = = = = = = = = = = = = = = = =

However, it’s not that simple. A closer look at the SelfDiv component shows that we’re simply hanging the template content on top of the body, which is not elegant at all; And I haven’t seen shadow DOM anywhere. No hurry, let’s take our time to enrich this example.

First, consider these two questions:

1. How to separate components from the real DOM and make them internally independent?

2. How does a component customize its content?

3. The shadow DOM

In response to the first question, the core appeal is that you want custom components to be internally independent (styles, events, etc.), which can be achieved by using shadow. AttachShadow (shadowRootInit), where shadowRootInit is a dictionary object with two values: mode and delegatesFocus; For details, see attachShadow.

Let’s transform the components we just created:

class SelfDiv extends HTMLElement {
  constructor() {
    super(a)let shadow = this.attachShadow({ mode: 'open' })
    shadow.appendChild(tpl.content.cloneNode(true))}}Copy the code

The key point is to create a shadowRoot object with attachShadow and then hang the template content on shadowRoot. To verify the isolation of shadow, we fill in something else on the page:

<my-blog />
<article>Shadow Outside the Article element</article>
Copy the code

You can see the page looks like this:

Careful friends will notice that the article outside the custom element does not apply the top border and background style effects. Because the entire shadowRoot object is completely isolated from the document object, the structure is roughly as follows:

At this point, our components are out of the clutches of the native DOM and can be configured freely and flexibly anywhere on the page.

4. Great use of template and slot

The second question is: how does a component customize its content? . To be clear, our current component does not have custom blog titles and content. It is completely unavailable. That’s when slot slots come out. Slots have a name attribute as an identifier, usually defined in template, and can be filled with any HTML element based on that identifier when used. The definition is slightly obscure, so look directly at the code:

<template id="tpl">
  <style>
    article {width: 20%; margin: 20px auto; border: solid 1px gray; padding: 8px; }header {
      background: lightblue; color: #fff;
      font-size: 24px; border: solid 1px lightblue;
    }
  </style>
  <article>
    <header><slot name='title'>Blog title</slot></header>
    <section><slot name='cont'>Blog Content Blog Content Blog Content Blog Content Blog Content Blog Content...</slot></section>
  </article>
</template>
Copy the code

The template structure is the same, except that the title is wrapped in the named slot name=’title’, while the blog content is in the slot name=’cont’. We use custom components on the page like this:

<my-blog>
  <span slot='title'>First post</span>
  <p slot='cont'>Here's the first post. Here's the first post. Here's the first post.</p>
</my-blog>

<my-blog>
  <span slot='title'>Second post</span>
  <p slot='cont'>This is the second post...</p>
</my-blog>
Copy the code

The page looks like this, and you can see that the component is fully customized. Codepen. IO /xutaogit/ PE…

Third, Web Components advanced

By the end of this example, we have a pretty good idea of the basic functionality of Web Compoents. But when it comes to putting them into production, it’s not always satisfactory. You might quibble:

  • Today’s templates are too simple, I want to manipulate the content of the template and add events to it…
  • Custom components do exist, but I want to do different things at different stages of the component.
  • Slot looks useful, but it uses a dead, inflexible subchild defined by rules
  • .

Problems seem to be a lot, but in the final analysis, or did not understand the “three plate axe”, next we will carry it on the furnace, together to “refining” look.

Custom elements

In the previous section, we only talked about how to create a custom element and then mount it to the real DOM or shadow DOM. Let’s take a closer look at what other special features it has.

1. Definition and Usage

You can use the CustomElementRegistry. Define () returns the binding on the window object instance customElements custom elements. The syntax is customElements. Define (name, constructor, options);

  • Name: element name. English characters that can only be connected by short lines cannot be single words. Example: name = ‘my – comp
  • Constructor: Element constructor. You must inherit from HTMLElement or its subclasses (for example: HTMLParagraphElement, etc.)
  • Options: Control how elements are defined to support only one optionextendsforSpecifies what type element the defined element inherits from. (example: {extends: ‘p’})

The first two arguments should be easy to understand, but the third argument is particularly important: the extends specifies the same element as the constructor inherits. The code is shown as follows:

class ExtendP extends HTMLParagraphElement {
  constructor () {super(a)let iRoot = this.attachShadow({mode:'open'})
    let span = document.createElement('span')
    span.innerHTML = "Extended from HTMLParagraphElement"
    iRoot.appendChild(span)
  }
}

customElements.define('extend-p', ExtendP, {extends: 'p'})
Copy the code

Here extends specifies an element that HTMLParagraphElement, and extends must be specified as P when the define method is called, and a component can only be used for a P element, in a way that is different from normal custom elements:

<p is="extend-p"></p>
Copy the code

Above, two ways to use custom components are summarized:

  • A custom element that extends from HTMLElement and is used directly< element name ></ element name >
  • Elements extending from a specific subclass Element, accessed using the IS attribute</ Extend Element is=" Element name "></ extend Element>

IO /xutaogit/ PE…

2. Life cycle

In the constructor of a Custom Element, you can specify several different callback functions that will be called at different lifetimes of the element:

  • ConnectedCallback: Called when a Custom Element is first inserted into the document DOM.
  • DisconnectedCallback: Called when a Custom Element is removed from the DOM of the document.
  • AdoptedCallback: Called when a Custom Element is moved to a new document.
  • AttributeChangedCallback: when a custom element increase, delete and modify their own property, is invoked.

Specific usage method can refer to the official website documents, example code handling here, you can click experience: life cycle. In particular, attributeChangedCallback depends on the observedAttributes implementation of the GET function.

attributeChangedCallback(name, oldValue, newValue) {
  console.log('Name is the attribute name that needs to be triggered by observedAttributes');
  // do something
}
static get observedAttributes() {return ['attr1'.'attr2']; } // return returns an array that can be used to listen for multiple properties
Copy the code

Shadow DOM

This “axe” is one of the most important features of WebComponents, as it allows for true HTML reuse — hiding markup structure, style, and behavior from the rest of the code on the page. Looking at the previous post example, you can see that the Shadow DOM interface returns a shadow-root node, where the internal style and behavior do not affect the external elements, and can be used as an alternative to style isolation between micro-front terminal applications. This is a separate topic for the moment.

1. The shadow mode

You can attach a shadow root to any Element using the element.attachShadow () method, which takes as an argument a configuration object with a mode attribute that can be either open or closed:

let shadow = elementRef.attachShadow({mode: 'open'});
let shadow = elementRef.attachShadow({mode: 'closed'});
Copy the code

Open means that the Shadow DOM can be retrieved from JavaScript methods within the page and manipulated using the DOM APIs.

class SelfDiv extends HTMLElement {
  constructor() {
    super(a)let shadow = this.attachShadow({mode: 'open'}) 
    shadow.appendChild(tpl.content.cloneNode(true))
    
    console.log('===shadow root==='.this.shadowRoot)
  }
}
Copy the code

2. Binding type

It’s also important to note that not any type of element can be attached to shadow root. For security reasons, these are currently the only type elements that can be attached to the Shadow DOM (valid custom elements are also available) : appending elements

The template and slot.

As mentioned in the previous section, slot usage rules are rigid and inflexible. In fact, there are two types of slot: named and unnamed, which can be used in more flexible ways. Here are a few key points to note:

  • If you calltemplateIs not explicitly used for named slot, which displays the default content.
  • When named slots are used, non-named slots are not displayed by default
  • If an unnamed slot is defined in the template, the content between the tags when using the template replaces the unnamed slot portion directly.

Suppose the template has two slots as follows:

<template id="tpl">
  <header>
    <slot name='title'>Named slot: Blog title</slot>
  </header>
  <slot>Not to be named slot.</slot>
  <section>Blog Content Blog content Blog content</section>
</template>
Copy the code

This template has a named title slot and an unnamed slot. If you use this template for component customization:

class MyBlog extends HTMLElement {
  constructor(){
    super(a)let shadow = this.attachShadow({mode:'open'})
    shadow.appendChild(tpl.content.cloneNode(true))
  }
}
customElements.define('my-blog', MyBlog)
Copy the code

Scenario 1: When used on a page, access
and display both named slot and unnamed slot directly:

<! -- call directly, without slot, display both named and non-named -->
<my-blog></my-blog>
Copy the code

Scenario 2: If the named slot is accessed and nothing else is passed in the template, only the named Slot portion is displayed:

<! -- Use named slot, non-named slot is not displayed -->
<my-blog>
  <h2 slot='title'>Custom title</h2>
</my-blog>
Copy the code

Scenario 3: Assigning values to both named and unnamed slots:

<! -- template non-slot content replaces unnamed content -->
<my-blog>
  <h2 slot='title'>Custom title</h2>
  <span>Customize the non-named part</span>
</my-blog>
Copy the code

Specific code can be jabbered into custom component slots.

4. Ecology and surroundings

There are several component libraries based on the WebComponent standard: Omiu, XY-UI. There is also an interesting CSS library called CSS-doodle, which uses WebComponent to create cool CSS effects for personal websites.

Thanks for reading.