web组件——template标签,影子DOM,自定义组件
template标签
innerHTML会带来安全问题,document.createElemt()又太过麻烦,template就很方便且安全
<div id="foo"></div>
<template id="bar">
<p>I am inside template</p>
</template>
<script>
const $ = name => document.getElementById(name)
/*
添加template进div的两种方法
*/
const fragment = $('bar').content;
//1
$('foo').appendChild(fragment )
//2;true or false,书上说是深浅克隆,但是false好像什么都没有
$('foo').appendChild(document.importNode(fragment , true))
</script>
注意:
- 这两种添加方法效果是不一样的,importNode是克隆;而直接添加,就是把它掏空了,移走了。再想克隆/'拿'就是个空的
第一种效果:tmeplate里的内容不可展开了
![]()
第二种效果:tmeplate里的内容可展开,可见
![]()
影子DOM
放影子DOM的"容器"(标签)叫"宿主",一个宿主里只能放一个影子DOM,若不小心放多了,就是"先到先得",谁在前就放谁,后面的不管
功能:
- 普通html中为了不同的样式,要避免class名一样,而影子DOM有自己的class,即使与外界class重名了,也不影响
<!-- 两个宿主 -->
<div id="host1"></div>
<div id="host2"></div>
<script>
const $ = name => document.getElementById(name)
const d1 = $('host1').attachShadow({ mode: 'open' })
//影子DOM内容
d1.innerHTML = `
<p class="color">color: red</p>
<style>
.color {
color: red;
}
</style>
`
const d2 = $('host2').attachShadow({ mode: 'closed' })
//影子DOM内容
d2.innerHTML = `
<p class="color">color: gray</p>
<style>
.color {
color: gray;
}
</style>
`
</script>
两个同名class,互不影响


注意:
{ mode: 'open' }中的open与closed适用于创建开放与保密的影子DOM。log(d1)打印d1的详细信息,log(d2)null,目的是为了安全,但是在"有心人"面前还是显得鸡肋
- 影子DOM会具有更高的优先级:宿主里的内容会被影子DOM的内容覆盖,除非影子DOM提供插槽
<slot></slot>,把宿主原本的内容放在slot中保存
<div id="host1">
<p>I am host</p>
</div>
<script>
const $ = name => document.getElementById(name)
const d1 = $('host1').attachShadow({ mode: 'closed' })
d1.innerHTML = `
<p class="color">color: red</p>
<style>
.color {
color: red;
}
</style>
`
</script>
效果:原内容被替代


加上slot
<p class="color">color: red</p>
<slot></slot>
<style>
.color {
color: red;
}
</style>
效果:宿主的内容被放到slot里保存了下来


- 改变宿主中元素的渲染顺序
<div id="host1">
<div slot="a">a</div>
<div slot="b">b</div>
</div>
<script>
const $ = name => document.getElementById(name)
const d1 = $('host1').attachShadow({ mode: 'closed' })
d1.innerHTML = `
<slot name="b"></slot>
<slot name="a"></slot>
`
</script>
效果:先渲染了b,后a


自定义组件
html的标签名时可以自己随便起的,唯一的要求就是自定义的组件中间要有横线:-
如:<x-menu></x-menu>
最简单的用法就是直接写在html中慢慢改style,但是它搭配template或影子DOM有更高级的用法
<x-menu class="m"></x-menu>
<script>
const $ = name => document.getElementById(name)
class MenuElement extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
.innerHTML = `
<ul>
<li>首页</li>
<li>运动</li>
<li>圈子</li>
<li>我的</li>
</ul>
<style>
ul {
padding: 0;
display: flex;
}
li {
flex: 1;
list-style: none;
text-align: center;
line-height: 20px;
}
</style>
`
}
}
customElements.define('x-menu', MenuElement)
</script>
如此后,在html'中只需要一个<x-menu></x-menu>,就能达到下面的效果:

template也是一样,要注意的时通过影子DOM实现自定义标签内部的样式只能在影子DOM内部写style,外部不会生效。
这样的标签也是一个组件,我们可以通过在构造函数中通过constructor()和其他JS的API监听组件的属性变化等,来实现组件的生命周期:
//1. 创建元素可以通过:
constructor() {
super()
log('beforeCreated')
this.attachShadow({ mode: 'open' })
.innerHTML = `....`
log('created')
}
//2.组件被添加进某个容器时 <=> mounted
connected
//3.组件被从某容器移除时 <=> unmounted
discounted
//4.组件属性改变时 <=> updated
attributeChanged


浙公网安备 33010602011771号