总结一下web components的使用:
在线示例
引入
单独文件引入(这样可以复用):
<link rel="import" href="./components/userCard.html">
直接写入同一个html文件内:
<template id="userCardTemplate">
<style>
...
</style>
<div>
...
</div>
</template>
生命周期
生命周期方法的执行顺序是:
定义在自定义元素的类定义中的特殊回调函数,影响其行为:
- connectedCallback: 当自定义元素第一次被连接到文档DOM时被调用。
- disconnectedCallback: 当自定义元素与文档DOM断开连接时被调用。
- adoptedCallback: 当自定义元素被移动到新文档时被调用。
- attributeChangedCallback: 当自定义元素的一个属性被增加、移除或更改时被调用。
属性/方法
1. 自定义属性
<user-card
image="https://semantic-ui.com/images/avatar2/large/kristy.png"
name="--"
email="chengzan0371@163.com"
>
</user-card>
2. slot
<user-card
image="https://semantic-ui.com/images/avatar2/large/kristy.png"
name="--"
email="chengzan0371@163.com"
>
<div slot="button" onclick="changeName();">改变name</div>
</user-card>
...
<template id="userCardTemplate">
...
<button class="button">
<slot name="button"></slot>
</button>
...
</template>
user-card 的属性 slot=“button” 对应 template 的
3. Shadow DOM
class UserCard extends HTMLElement {
constructor() {
super();
// shadow DOM 影子DOM
this.$shadow = this.attachShadow( { mode: 'open' } );
...
}
}
window.customElements.define('user-card', UserCard);
this.$shadow = this.attachShadow( { mode: ‘open’ } );
自定义元素的this.attachShadow()方法开启 Shadow DOM
this.attachShadow()方法的参数 { mode: ‘closed’ } ,表示 Shadow DOM 是封闭的,不允许外部访问。
4.样式
组件的样式应该与代码封装在一起,只对自定义元素生效,不影响外部的全局样式。所以写在里:
<template id="userCardTemplate">
<style>
:host {
display: flex;
align-items: center;
width: 450px;
height: 180px;
background-color: #d4d4d4;
border: 1px solid #d5d5d5;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
border-radius: 3px;
overflow: hidden;
padding: 10px;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
.image {
flex: 0 0 auto;
width: 160px;
height: 160px;
vertical-align: middle;
border-radius: 5px;
}
...
</style>
...
</template>
示例分析
1. html
<user-card
image="https://semantic-ui.com/images/avatar2/large/kristy.png"
name="--"
email="chengzan0371@163.com"
>
<div slot="button" onclick="changeName();">改变name</div>
</user-card>
...
<template id="userCardTemplate">
<style>
:host {
display: flex;
align-items: center;
width: 450px;
height: 180px;
background-color: #d4d4d4;
border: 1px solid #d5d5d5;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
border-radius: 3px;
overflow: hidden;
padding: 10px;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
.image {
flex: 0 0 auto;
width: 160px;
height: 160px;
vertical-align: middle;
border-radius: 5px;
}
...
</style>
<img class="image" />
<div class="container">
<p class="name"></p>
<p class="email"></p>
<button class="button"><slot name="button"></slot></button>
</div>
</template>
2. js
class UserCard extends HTMLElement {
// 观察属性
static get observedAttributes() {
return ['name', 'email'];
}
constructor() {
super();
// shadow DOM 影子DOM
this.$shadow = this.attachShadow( { mode: 'open' } );
// 获取到对应的tempalte元素
this.$templateElem = document.currentScript.ownerDocument.getElementById('userCardTemplate');
// 克隆tempalte元素内容
this.$content = this.$templateElem.content.cloneNode(true);
// 从该自定义元素获取属性
this.$content.querySelector('img').setAttribute('src', this.getAttribute('image'));
this.$content.querySelector('.container>.name').innerText = this.getAttribute('name');
this.$content.querySelector('.container>.email').innerText = this.getAttribute('email');
// 绑定按钮事件
// this.$button = shadow.querySelector('button');
// this.$button.addEventListener('click', () => {
// // do something
// });
// 该自定义元素加入内容
this.$shadow.appendChild(this.$content);
}
// 生命周期回调:当自定义元素第一次被连接到文档DOM时被调用
connectedCallback() {
// console.log('connectedCallback');
}
// 自定义元素属性改变的回调
attributeChangedCallback(attr, oldVal, newVal) {
switch(attr) {
case 'name':
// do something with 'foo' attribute
// 触发重新渲染
this.render(attr, newVal);
break;
case 'email':
// do something with 'bar' attribute
// 触发重新渲染
this.render(attr, newVal);
break;
}
}
render(attr, newVal) {
console.log('render...');
this.$shadow.querySelector(`.${attr}`).innerText = newVal;
}
}
// 定义该自定义元素
window.customElements.define('user-card', UserCard);
// 改变名字方法,在
function changeName() {
document.querySelector('user-card').setAttribute('name', '程赞');
}
参考资料
MDN:https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
阮一峰:Web Components 入门实例教程
目前我在练习中使用的就是这些,如果有错误的地方还望指正。
还有一个疑问就是:如何在 < user-card > 中传入自定义事件,比如 onChangeName=“changeName”:
已解决:使用eval() 使用setTimeout
<user-card
image="https://semantic-ui.com/images/avatar2/large/kristy.png"
name="--"
email="chengzan0371@163.com"
onchangeNameCallback="changeCallback"
>
<div slot="button" onclick="changeName();">hello</div>
</user-card>
class UserCard extends HTMLElement {
...
constructor() {
...
const changeNameCallback = this.getAttribute('onchangeNameCallback');
if (changeNameCallback) {
this.addEventListener('changeNameCallback', () => setTimeout(changeNameCallback + '()',0));
}
...
}
...
}