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>

注意:

  1. 这两种添加方法效果是不一样的,importNode是克隆;而直接添加,就是把它掏空了,移走了。再想克隆/'拿'就是个空的
    第一种效果:tmeplate里的内容不可展开了

    第二种效果:tmeplate里的内容可展开,可见

影子DOM

放影子DOM的"容器"(标签)叫"宿主",一个宿主里只能放一个影子DOM,若不小心放多了,就是"先到先得",谁在前就放谁,后面的不管
功能:

  1. 普通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,目的是为了安全,但是在"有心人"面前还是显得鸡肋

  1. 影子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里保存了下来

  1. 改变宿主中元素的渲染顺序
<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

posted on 2022-02-24 00:43  In-6026  阅读(245)  评论(0)    收藏  举报

导航