初探 web components
通过使用 Custom Elements 创建内联的 CSS 和 JavaScript 的自定义元素。需要说明的是它不是 React, Vue 或者 Angular 的框架的替代方案,它是一个全新的概念。
CustomElementRegistry 对象
在 window 全局对象下暴露了 customElements 属性,可以通过此属性访问到 CustomElementRegistry
对象。CustomElementRegistry
对象下的几种方法用于注册 Custom Elements和查询 Custom Elements:
define()
用于定义新的 Custom Elementget()
用于获取 Custom Element 的 constructor,如果不存在,返回 undefinedupgrade()
用于升级 Custom ElementwhenDefined()
用于获取 Custom Element 的 constructor,类似 get(),不同之处是返回值是 promise,有可用值是返回 resolves 状态
如何创建一个 Custom Element
在调用 window.customElements.define()
方法前,首先要定义一个新的HTML元素
class CustomTitle extends HTMLElement {
// ToDo...
}
在 CustomTitle 类的构造器中使用 Shadow DOM 关联自定义 CSS,JavaScript 和 HTML到新的元素,这样就可以在这个新元素中封装各种功能,首先初始化构造器
class CustomTitle extends HTMLElement {
constructor() {
super()
//...
}
}
然后调用 attachShadow(),传入参数 { mode: 'open' }
,这个属性设置了 shadow DOM的封装模式,如果设置值为 open
,可以访问元素的 shadowRoot 属性,如果值为 close
,则不可以。
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
//...
}
}
接下来设置元素的HTML
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<h1>Hello WC!</h1>
`
}
}
这里的 innerHTML 可以写入多个 html tag
定义了内置元素后,即可使用 window.customElements
window.customElements.define('custom-title', CustomTitle)
以上即可在页面中使用
加入CSS
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<style>
h1 {
font-size: 40px;
color: #000;
}
</style>
<h1>Hello WC!</h1>
`
}
}
简写语法
window.customElements.define('custom-title', class extends HTMLElement {
constructor() {
...
}
})
加入 JavaScript
在加入JavaScript处理方式上与 CSS 上略有区别,不能再模板字符串中直接写入,需要加入 addEventListener处理
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<style>
h1 {
font-size: 40px;
color: #000;
}
</style>
<h1>Hello WC!</h1>
`
this.addEventListener('click', e => {
console.log('clicked')
})
}
}
使用 template
可以使用 template
,通过id指定到
<template id="custom-title-template">
<style>
h1 {
font-size: 40px;
color: #000;
}
</style>
<h1>Hello WC!</h1>
</template>
<custom-title></custom-title>
然后在 Custom Element 构造器中引用到 shadow DOM 上
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
const tmpl = ducument.querySelector('#custom-title-template')
this.shadowRoot.appendChild(tmpl.content.cloneNode(true))
}
}
window.customElements.define('custom-title', CustomTitle)
生命周期
除 constructor 外,还可以定义生命周期函数,用于在特定时期执行特定的方法
connectedCallback
当元素插入到DOM中时执行disconnectedCallback
当DOM中删除元素时执行attributeChangedCallback
当检测到属性发生更改或添加或删除时执行(传入3个参数)- attrName: 变化了的属性名
- oldVal: 属性改变前的值
- newVal: 属性改变后的值
adoptedCallback
当元素已移至新文档时
class CustomTitle extends HTMLElement {
constructor() {
//...
}
connectedCallback() {
//...
}
disconnectedCallback() {
//...
}
attributeChangedCallback(attrName, oldVal, newVal) {
//...
}
}
上面提到attributeChangedCallback
会监听属性值的变化,这里要定义监听属性值的范围,必须定义一个静态方法用于返回监听属性的数组
class CustomTitle extends HTMLELement {
constructor() {
//...
}
static get observedAttributes() {
return ['diabled']
}
attributeChangedCallback() {
//...
}
}
这里我定义了 disabled
属性用于被监听,当它的值变化了,比如它的值为 true
document.querySelector('custom-title').disabled = true
此时,attributeChangedCallback
被触发,三个参数值分别为 'disabled', false, true
定义自定义属性
在自定义元素上定义自定义属性,通过添加 getter 和 setter 函数向自定义元素上定义自定义属性
class CustomTitle extends HTMLElement {
static get observedAttributes() {
return ['nbattribute']
}
get nbattribute() {
return this.getAttribute('nbattribute')
}
set nbattribute(value) {
this.setAttribute('nbattribute', value)
}
}
如果是定义类似 disabled
这样的布尔属性值,如果值为 true
时展示这个属性值,false
时不展示
class CustomTitle extends HTMLElement {
static get observedAttributes() {
return ['boolattribute']
}
get boolattribute() {
return this.hasAttribute('boolattribute')
}
set boolattribute(value) {
if (value) {
this.setAttribute('boolattribute', '')
} else {
this.removeAttribute('boolattribute')
}
}
}
设置尚未定义的自定义元素的样式
在页面加载过程中,JavaScript的加载需要时间,此时自定义元素还未定义,当页面加载完成后,页面的的重新布局过程可能不是那么友好,为了解决这个问题,加入 :not(:defined)
伪类,设置一个大致的高度和渐变效果
custom-title:not(:defined) {
display: block;
height: 400px;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
浏览器支持情况 caniuse
最新版的 FireFox,Safari,Chrome,使用 Chromium内核 重写的Edge,IE(就别想了),另外可以加入 polyfill 兼容老版本的浏览器
Web Components 相关的库
- Hybrids is a UI library for creating Web Components with simple and functional API.
- LitElement uses lit-html to render into the element’s Shadow DOM and adds API to help manage element properties and attributes.
Polymer provides a set of features for creating custom elements. - Slim.js is an opensource lightweight web component library that provides data-binding and extended capabilities for components, using es6 native class inheritance.
- Stencil is an opensource compiler that generates standards-compliant web components.

