JavaScript | 2020-09-29 20:28:52 1143次 0次
一、组件定义
定义组件,样式全局可控。
... <component-head> <style type="text/css"> .head{ background: #00739C; color: #fff; } </style> <div class="head">head</div> </component-head> ... <script type="text/javascript"> class Head extends HTMLElement { constructor() { super(); } } // 定义组件名称 window.customElements.define('component-head', Head); </script>
直接在 html 中使用自定义的组件,或者通过 js 注入的方式也可以:
... const el = customElements.get('component-head'); const myElement = new el(); document.body.appendChild(myElement);
二、template
... <component-head></component-head> <template id="temp-head"> <style type="text/css"> .head{ background: #00739C; color: #fff; } </style> <div class="head">head</div> </template> ... <script type="text/javascript"> class Head extends HTMLElement { constructor() { super(); // 获取模板中内容 var templateElem = document.getElementById('temp-head').content; this.appendChild(templateElem); } } window.customElements.define('component-head', Head); </script>
如上可以简单的使用一个模板,在自定义组件中展示,比如实现一个列表,将 template 代码快单独抽离,并且可以在组件中传入数据参数:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <link id="temp-head" rel="import" href="./temp.html"> </head> <body> <component-head name = "11111" ></component-head> <component-head name = "222222" ></component-head> </body> </html> <script type="text/javascript"> // 加载模板对象 function importTemp(linkId){ return document.querySelector("#"+linkId).import.querySelector("#"+linkId).content.cloneNode(true); } class Head extends HTMLElement { constructor() { super(); this.init(); } init(){ var templateElem = importTemp('temp-head'); // 模板赋值 templateElem.querySelector('.temp-name').innerText = this.getAttribute('name'); this.appendChild(templateElem); } } window.customElements.define('component-head', Head); </script>
上面通过 link 标签引入模板文件,temp.html 定义如下 :
<template id="temp-head"> <style type="text/css"> .temp-name{ background: #00739C; color: #fff; margin-bottom: 5px; } </style> <div class="temp-name"></div> </template>
三、slot
组件中创建插槽: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <link id="temp-head" rel="import" href="./temp1.html"> </head> <body> <component-head name = "苏哲" > <span slot="my-text">components1 is great</span> </component-head> <component-head name = "111" > <span slot="my-text">no no no</span> </component-head> </body> </html> <script type="text/javascript"> ... class Head extends HTMLElement { constructor() { super(); this.init(); } init(){ var templateElem = importTemp('temp-head'); // 需要使用 Shadow DOM const shadowRoot = this.attachShadow({mode: 'open'}) .appendChild(templateElem); } } window.customElements.define('component-head', Head); </script>
模板中定义插槽:
<div class="temp-name"> <slot name="my-text">NEED NAME</slot> </div>
四、Shadow DOM
这个方法目前被使用的最为广泛,比如实现微前端等,Shadow DOM 允许将隐藏的 DOM 树附加到常规的 DOM 树中——它以 shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的 DOM 元素一样。
Shadow host:一个常规 DOM节点,Shadow DOM 会被附加到这个节点上。
Shadow tree:Shadow DOM内部的DOM树。
Shadow boundary:Shadow DOM结束的地方,也是常规 DOM开始的地方。
Shadow root: Shadow tree的根节点。
样式隔离:
<!DOCTYPE> <html> <head> <meta charset="utf-8"/> <title>webcomponent</title> <link id="temp-head" rel="import" href="./temp1.html"> <style type="text/css"> .p1{ color: orange; } </style> </head> <body> <p class="p1">common dom</p> <my-element name="组件1"> <!----卡槽----> <span slot="title">web components 组件1 卡槽</span> </my-element> </body> </html> <script type="text/javascript"> ... // 同 slot 的创建方式 </script>
模板文件修改如下: <template id="temp-head"> <style> #container { --bg:#932; color: var(--bg); border: 1px solid darkred; } .p1{ color: #fff; background: var(--bg); } </style> <div id="container"> <!--卡槽--> <slot name="title"></slot> <p class="p1">shadow Dom</p> </div> </template>
全局的样式和 shadow Dom 中定义的样式相互不冲突,效果如下:
事件:
<!DOCTYPE> <html> <head> <meta charset="utf-8"/> <title>webcomponent</title> <link id="temp-head" rel="import" href="./temp1.html"> </head> <body> <my-element name="组件1"> <!----卡槽----> <span slot="title">组件1 卡槽</span> </my-element> <my-element name="组件2"> <!----卡槽----> <span slot="title">组件2 卡槽</span> </my-element> </body> </html> <script type="text/javascript"> // 加载模板对象 function importTemp(linkId){ return document.querySelector("#"+linkId).import.querySelector("#"+linkId).content.cloneNode(true); } class MyElement extends HTMLElement { constructor() { super(); //自定义事件 this.onclick = ()=>{ let coun = this.getAttribute('bar') || 0 this.setAttribute('bar', ++coun) } const templateElem = importTemp('temp-head'); const shadowRoot = this.attachShadow({mode: 'open'}); shadowRoot.appendChild(templateElem); this.container = shadowRoot.querySelector('#container'); this.shadow = shadowRoot } //只会监听修改这里定义的属性 static get observedAttributes() { return ['foo', 'bar']; } //组件属性改变回调监听 attributeChangedCallback(attr, oldVal, newVal) { let name = this.getAttribute('name') switch(attr) { case 'foo': console.log(name+',修改了foo属性,旧属性:'+oldVal+',新属性:'+newVal) this.container.style.background = '#808080'; break; case 'bar': console.log(name+',修改了bar属性,旧属性:'+oldVal+',新属性:'+newVal) this.container.style.background = '#516588'; break; } } connectedCallback() { console.log('被创建了') //添加需要的模板 const content = this.shadow.querySelector('#view').content; this.container.appendChild(content); } outHandle(){ console.log('我在外部被调用了') } } //等待被创建 异步 customElements.whenDefined('my-element').then(() => { const element = document.querySelector('my-element'); element.setAttribute('foo', 1) element.outHandle() //外部调用事件方法 会覆盖类中的方法 // element.onclick = ()=>{ // alert(11) // } }) window.customElements.define('my-element', MyElement); </script>
// 模板文件 <template id="temp-head"> <style> #container { --bg:#932; color: var(--bg); } .p1{ color: #fff; background: var(--bg); } </style> <!--模板功能--> <template id="view"> <p>这是模板后期添加的组件,我被显示了</p> </template> <div id="container"> <!--卡槽--> <slot name="title"></slot> <p class="p1"> 点我看打印的效果</p> </div> </template>
0人赞