前言
webComponent 是 HTML5 推出的新特性,为组件化推广奠定基础。
webComponent 基本使用
原生组件,性能较好,但存在兼容性问题。其核心技术有:Custom elements, Shadow DOM, HTML Templates。
Custom elements
JavaScript API,用于定义 custom elements 及其行为。
<m-button type="primary">webComponent</m-button> // 调用
<script type="text/javascript">
class MButton extends HTMLElement {
constructor() {
super();
}
}
window.customElements.define('m-button', MButton) // 关联 HTML 和 JS,自定义组件
</script>
HTML Templates
<template>
, <slot>
元素标记元素结构,并重用元素。
<m-button type="primary">webComponent</m-button>
<template id="m-btn">
<button class="m-button">
<slot>Default</slot>
</button>
</template>
<script type="text/javascript">
class MButton extends HTMLElement {
constructor() {
super();
let btnTmpl = document.getElementById('m-btn') // 定义模板并获取模板
}
}
</script>
Shadow DOM
JavaScript API,用于封装 Shadow DOM 附加到元素上,并关联其功能,以此保证元素的功能私有,不会与其他元素冲突。
<m-button type="primary">webComponent</m-button>
<template id="m-btn">
<button class="m-button">
<slot>Default</slot>
</button>
</template>
<script type="text/javascript">
class MButton extends HTMLElement {
constructor() {
super();
let btnTmpl = document.getElementById('m-btn') // 定义模板并获取模板
let shadow = this.attachShadow({ mode: 'open' }) // 配置 devtools 是否可查看 DOM 结构,open / close
let cBtnTmpl = btnTmpl.content.cloneNode(true) // copy 模板便于重用
shadow.appendChild(cBtnTmpl) // 模板挂载 Shadow DOM
}
}
</script>
webComponent 高阶使用
webComponent 高阶使用主要包括属性设置,样式设置,事件绑定等
webComponent 属性设置
webComponent 在调用组件时进行属性传值,在声明组件时通过 DOM API 获取属性值进行操作。
<m-button type="primary">webComponent</m-button>
<script type="text/javascript">
class MButton extends HTMLElement {
constructor() {
super();
let btnTmpl = document.getElementById('m-btn') // 定义模板并获取模板
let shadow = this.attachShadow({ mode: 'open' })
let cBtnTmpl = btnTmpl.content.cloneNode(true) // copy 模板便于重用
let type = this.getAttribute('type') // 读取属性值,进行操作
shadow.appendChild(cBtnTmpl) // 模板挂载 Shadow DOM
}
}
</script>
webComponent 样式设置
-
template 设置样式
<m-button type="primary">webComponent</m-button> <template id="m-btn"> <style type="text/css"> // 定义模板样式 .m-button { border: none; outline: none; border-radius: 4px; padding: 5px 20px; } </style> <button class="m-button"> <slot>Default</slot> </button> </template> class MButton extends HTMLElement { constructor() { super(); let btnTmpl = document.getElementById('m-btn') // 定义模板并获取模板 let shadow = this.attachShadow({ mode: 'open' }) let cBtnTmpl = btnTmpl.content.cloneNode(true) // copy 模板便于重用 let type = this.getAttribute('type') // 读取属性值,进行操作 shadow.appendChild(cBtnTmpl) // 模板挂载 Shadow DOM } }
-
Shadow DOM 设置样式
<m-button type="primary">webComponent</m-button> <template id="m-btn"> <style type="text/css"> // 定义模板样式 .m-button { border: none; outline: none; border-radius: 4px; padding: 5px 20px; } </style> <button class="m-button"> // 具名插槽使用:模板内<slot name="btn-text"></slot>,模板外调用时<span slot="btn-text"></span> <slot>Default</slot> </button> </template> class MButton extends HTMLElement { constructor() { super(); let btnTmpl = document.getElementById('m-btn') // 定义模板并获取模板 let shadow = this.attachShadow({ mode: 'open' }) let cBtnTmpl = btnTmpl.content.cloneNode(true) // copy 模板便于重用 let type = this.getAttribute('type') // 读取属性值,进行操作 const sheets = { 'primary': { background: '#409EFF', color: '#FFF' }, 'success': { background: '#67C23A', color: '#FFF' }, 'warning': { background: '#E6A23C', color: '#FFF' }, 'danger': { background: '#F56C6C', color: '#FFF' }, 'default': { background: '#909399', color: '#FFF' }, } const style = document.createElement('style') style.textContent = ` .m-button { background: ${sheets[type].background}; // 属性 配置css 样式 color: ${sheets[type].color}; } ` shadow.appendChild(style) shadow.appendChild(cBtnTmpl) // 模板挂载 Shadow DOM } }
注意:webComponent 通过 Shadow DOM 隔离以后,外界无法修改样式,只能通过属性或者设置 CSS 变量的方式修改。
-
css 变量设置样式
<style type="text/css"> :root { --text-color: #fff; } </style> <m-button type="primary">webComponent</m-button> <template id="m-btn"> <style type="text/css"> // 定义模板样式 .m-button { border: none; outline: none; border-radius: 4px; padding: 5px 20px; } </style> <button class="m-button"> <slot>Default</slot> </button> </template> class MButton extends HTMLElement { constructor() { super(); let btnTmpl = document.getElementById('m-btn') // 定义模板并获取模板 let shadow = this.attachShadow({ mode: 'open' }) let cBtnTmpl = btnTmpl.content.cloneNode(true) // copy 模板便于重用 let type = this.getAttribute('type') // 读取属性值,进行操作 const sheets = { 'primary': { background: '#409EFF', color: '#FFF' }, 'success': { background: '#67C23A', color: '#FFF' }, 'warning': { background: '#E6A23C', color: '#FFF' }, danger': { background: '#F56C6C', color: '#FFF' }, 'default': { background: '#909399', color: '#FFF' }, } const style = document.createElement('style') style.textContent = ` .m-button { background: ${sheets[type].background}; // 属性 配置 css 样式 color: var(--text-color, ${sheets[type].color}); // css 变量配置 css 样式 } ` shadow.appendChild(style) // 样式挂载至 Shadow DOM shadow.appendChild(cBtnTmpl) // 模板挂载至 Shadow DOM } }
注意:通过 templates 设置样式的优先级是高于 css 变量和 Shadow DOM 的。
webComponent 事件绑定
<m-button type="primary">webComponent</m-button>
<template id="m-btn">
<button class="m-button">
<slot>Default</slot>
</button>
</template>
<script type="text/javascript">
class MButton extends HTMLElement {
constructor() {
super();
let btnTmpl = document.getElementById('m-btn') // 定义模板并获取模板
let shadow = this.attachShadow({ mode: 'open' }) // 配置 devtools 是否可查看 DOM 结构,open / close
let cBtnTmpl = btnTmpl.content.cloneNode(true) // copy 模板便于重用
cBtnTmpl.querySelector('.m-button').addEventListener('click', this.onClick)
shadow.appendChild(cBtnTmpl) // 模板挂载 Shadow DOM
}
onClick() {
alert('click')
}
}
</script>