Web Component 是Html5 推出的一个新特征,即web 组件。提到web组件有一定前端开发经验的人对这个词汇并不陌生,在这个严重依赖前端框架来开发项目的时代,我们推崇使用组件化的思路来编写我们的页面,通过这些低耦合的组件我们可以像搭积木一样组织出我们的页面。
从我们的日常所见到的类似table select 这些耳熟能详的基础标签,更有后来更复杂的video raido等,都是web组件化的一种体现。可惜的是兼容性问题以及api的丰富度不够。目前主流还是使用框架来模拟组件的实现。随着浏览器兼容的越来越好,如果能完全使用web component来组织我们的项目,我们可以少引入很多第三方的框架来编排我们的页面
本文我们将来探究 Web Component
自定义 Web Component
关键的api,它是挂载在window上的api,并非document。
window.customElements.define()
// 参数类型
// name 是组件名称
// constructor 自定义组件的构造类
// options 更多的属性
(method) CustomElementRegistry.define(name: string, constructor: CustomElementConstructor, options?: ElementDefinitionOptions): void
- 自定义内置元素
自定义内置元素的本质是扩展已有的内置元素的逻辑。他的好处是可以直接继承默认内置元素的例如语义、默认交互。用户只需要关注修改自定义交互即可。
我们来自定义一个button 实现一个点击默认的操作。
class MyButton extends HTMLButtonElement {
constructor () {
// 必须加super 否则this 无法指向button
super()
this.addEventListener('click', function () {
alert('this is my button')
})
}
}
customElements.define('my-button', MyButton, { extends: "button" })
申明的时候,需要通过extends传入一个已经存在的内置元素,调用的时候通过内置元素的is来扩展,
<button is="my-button">Click me</button>
- 定义一个完全自治的元素
完全自治的元素,不依赖现有元素。是一个完全从view 到数据 到交互都自我管理的自定义元素。如上述他的缺点就在于开发者需要处理所有的语义和交互,写法比较繁琐。如果处理不好,会导致违背html语义标签的一些标准。
class TextIcon extends HTMLElement {
constructor() {
super()
this._text = null;
}
static observedAttributes = ['text'];
attributeChangedCallback(name, oldValue, newValue) {
this._text = newValue;
this.updateRender();
}
connectedCallback() {
this.updateRender();
}
get text () {
return this._text;
}
set text(v) {
console.log(v, 'ss')
this.setAttribute('text', v)
}
updateRender () {
this.innerText=this._text
}
}
customElements.define('text-icon', TextIcon)
调用方式1
<text-icon text="test1"></text-icon>
调用方式2
const textIcon = document.createElement('text-icon')
textIcon.setAttribute('text', 'sddd')
document.body.appendChild(textIcon)
const textIcon1 = new TextIcon()
textIcon1.text = 12
document.body.appendChild(textIcon1)
- 自定义元素升级
由于可以先创建一个元素,然后再定义该元素。然后再升级它
涉及API
window.customElements.upgrade(elementNode)
注意看代码中的注释
// 通过createElemnt创建自定义元素节点,注意此时text-icon 还定义
const testUpgradeDom = document.createElement('text-icon')
// 开始定义text-icon 元素
class TextIcon extends HTMLElement {
constructor() {
super()
this._text = null;
}
static observedAttributes = ['text'];
attributeChangedCallback(name, oldValue, newValue) {
this._text = newValue;
this.updateRender();
}
connectedCallback() {
this.updateRender();
}
get text () {
return this._text;
}
set text(v) {
console.log(v, 'ss')
this.setAttribute('text', v)
}
updateRender () {
this.innerText=this._text
}
}
customElements.define('text-icon', TextIcon)
// 此时我们可以看到testUpgradeDom 这个节点非自定义元素
console.log(testUpgradeDom instanceof TextIcon); //false
// 调用升级api
customElements.upgrade(testUpgradeDom)
// 此时testUpgradeDom 就升级成了自定义元素创建的节点
console.assert(testUpgradeDom instanceof TextIcon); // true
- 获取自定义元素构造器
customElements.get('text-icon')
- 监测自定义元素的定义
customElements.whenDefined('text-icon').then(function() {
// 自定义操作
})
返回的是一个promise 当监测到text-icon 被定义后,我们可以去做一些操作,比如上面提到的升级操作等。
- attachInternals
我们都知道form内置标签能自动关联内置的表单元素的值。该功能让我们能够扩展更多的自定义表单元素。它核心需要处理的是通过internals暴露的属性和方法来实现和form之间的交互和值的关系。通过该api来获取form元素的一些内置属性,来让自定义元素能够和from元素一样来处理例如通过name 来获取值等表单的特点。
class MyCheckbox extends HTMLElement{
// 这个标示来控制是否是form关联的元素
static formAssociated = true
static observedAttributes = ['checked'];
constructor() {
super()
this._internals = this.attachInternals()
this.addEventListener('click', this._onClick.bind(this));
}
get form () {
return this._internals.form;
}
get name () {
return this._internals.name;
}
get type() {
return this._internals.type
}
get checked () {
return this.getAttribute('checked')
}
set checked(tag) {
console.log(tag,'xx')
this.toggleAttribute('checked', Boolean(tag))
}
attributeChangedCallback() {
this._internals.setFormValue(this.checked? 'on' : 'off')
// this._internals.ariaChecked = this.checked;
}
_onClick (event) {
debugger
this.checked = !this.checked
}
}
customElements.define('my-checkbox', MyCheckbox)
调用
<form method="post" action="">
<label><my-checkbox name="agreed"></my-checkbox> I read the agreement.</label>
<input type="submit">
</form>
- 一些生命周期
// 初始化
constructor()
// 组件第一次关联到文档
connectedCallback()
// 组件断开和文档的连接
disconnectedCallback()
// 组件关联到新的文档
adoptedCallback()
// 组件属性变化回调
attributeChangedCallback()
// 组件和表单关联的变化回调
formAssociatedCallback()
// 自定义关联表单组件的表单发生了reset操作的回调
formResetCallback()
// 自定义form元素 被设置为 disabled时的回调
formDisabledCallback()
// form restore时候触发
formStateRestoreCallback()
总结
几个注意点
- constructor里的super是必须的,否则就失去了this的关联属性
- 组件的命名要遵循规则
- 定义的类构造器中途不要出现return
- 构造器中不要出现document.write 或者window.open
- 在实际使用中,我们还可以将自定义元素 挂载在shadowdom上来做到类似沙箱一样的隔离能力
参考链接
web component
更多推荐
Web Component 详解
发布评论