web component
浏览器发展至今,很多浏览器已经很好的支持web component了,如果实在需要兼容IE、Edge还可以用pollyfill来增强一下浏览器
最全的使用文档:
chrome开发者相关:https://developers.google.com/web/fundamentals/web-components/
下面我们来大概看看怎么使用
test.html:
<!DOCTYPE html> <html lang="en"> <head> <style> /* * 可以外部定义web component样式,但是内部的shadow dom,选择器无法命中 * 这里定义的样式优先级高于:host定义的样式,就是如果定义display:inline;会覆盖掉:host定义的display:block; * 这里也可以定义css变量,在shadow dom里面使用 **/ app-drawer { --font-color: grey; line-height: 50px; } /* slot进去的可以命中 */ app-drawer div { font-style: italic } /* shadow dom的没法命中 */ app-drawer p { font-style: italic } </style> </head> <body class="little"> <!-- 原生支持的template,用于web component的shadow dom --> <template id="temp"> <!-- 这里的style是跟外界独立开的,相当于vue的scoped --> <style> :host { line-height: 20px; display: block; cursor: pointer; color: var(--font-color, #ff0); /* 通常,网络组件的布局/样式/绘制相当独立。在 :host 中使用 CSS containment 可获得更好性能 */ contain: content; } /* host-context表示父级如果命中括号里面的选择器则应用里面的样式,如果命中多个,且有冲突则看优先级 */ :host-context(.big) { font-size: 32px; font-weight: bold; } :host-context(.little) { font-size: 16px; } p { color: orange; } /* 给插槽引进来的元素添加样式 */ ::slotted(span) { color: var(--font-color, #f00); } </style> <!-- 在这里引入样式表,也是同样只适用于组件内部 --> <link rel="stylesheet" href="./style.css"> <p> 我是template啦~ </p> <!-- 插槽 --> <slot name="slot1"></slot> <p>**<slot></slot>**</p> <!-- 这里使用script和在template外使用是一样的,this都是window --> <script> console.log('来自shadow dom的问候') console.log(this) </script> </template> <app-drawer class="big"> <div slot="slot1">我从是slot1插槽进来的div1啦~</div> <span>我从是默认插槽进来的span啦~</span> <div slot="slot1">我从是slot1插槽进来的div2啦~</div> </app-drawer> <p>12312</p> <!-- 定义自定义web component --> <script src="./test.js"></script> </body> </html>
test.js:
class AppDrawer extends HTMLElement { // 定义组件需要同步更新到DOM的属性 get open() { return this.hasAttribute('open'); } set open(val) { if (val) { this.setAttribute('open', ''); } else { this.removeAttribute('open'); } this.toggleDrawer(); } // 相当于vue component的beforeCreate constructor() { super(); /** * 创建shadow dom * mode可以时open和closed (建议任何时候都别使用closed,不必要的限制造成很多不便) * delegatesFocus 在shadow dom元素focus时,是否也使容器也focus */ let shadowRoot = this.attachShadow({mode: 'closed', delegatesFocus: true}); // 复制一份template const temp = document.querySelector('#temp').content.cloneNode(true) // 添加进shadow dom shadowRoot.appendChild(temp) // 添加事件 this.addEventListener('click', e => { this.open = !this.open }); shadowRoot.querySelector('p').addEventListener('click', e => { console.log(e.target) }); // 初始化先执行一次 this.toggleDrawer() } /** * 生命钩子有: * connectedCallback:插入到 DOM 时,相当于vue component的created * disconnectedCallback: 从DOM中移除时,相当于vue component的beforeDestroy * attributeChangedCallback:属性添加、移除、更新或替换时,有点像vue component的beforeUpdate(不完全等同) * adoptedCallback:被移入新的 document时 */ // 定义组件的方法 toggleDrawer() { this.style.backgroundColor = this.open ? 'red' : 'pink' } } // 定义自定义组件,名字一定要有"-", 不然浏览器不认为它是web component, 会认为是一个HTMLUnknownElement customElements.define('app-drawer', AppDrawer);