Web组件

 开发者使用Vue、React等框架来使用及创建定制的组件,Web组件是浏览器原生支持的替代这些框架的特性,主要涉及相对比较新的三个Web标准。这些Web标准允许JS使用新标签扩展HTML,扩展后的标签就是自成一体的、可重用的UI组件。

 1 HTML模版

DocumentFragment是一种Node类型,可以临时充当一组同辈节点的父节点,方便将这些同辈节点作为一个单元来使用。可以使用document.createDocumentFragment()来创建DocumentFragment节点。创建DocumentFragment节点后,可以像使用Element一样。

DocumentFragment与Element的区别是在于它没有父节点,当向文档中插入DocumentFragment节点时,DocumentFragment本身并不会被插入,实际上插入的是它的子节点。

1.1 <template>标签

<template>标签及其子元素永远不会被浏览器渲染,其对应的是一个HTMLTemplateElement对象,这个对象只定义了一个content属性,该属性值是包含<template>所以子节点的DocumentFragment。

可以深度克隆这个DocumentFragment,然后把克隆的副本插入文档中需要的地方。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<template id="tep">
  <div class="grid"></div>
</template>
<div class="block">
  <span>hello block</span>
</div>
</body>
<script>
  let tep = document.querySelector("#tep");
  let block = document.querySelector(".block");
  block.append(tep.content.cloneNode(true));//深度克隆
  block.append(tep.content.cloneNode(true));
</script>
</html>
<style>
  .grid{
    width: 50px;
    height: 50px;
    background: blue;
    margin: 10px;
  }
</style>

2 自定义元素

customElement.define()方法用来自定义元素。第一个参数是标签名(必须包含一个连字符),第二个参数是HTMLElement的子类。

浏览器会自动调用自定义元素类的特定“生命期方法”,当自定义元素被插入文档时,会调用connectedCallback()方法;当自定义元素从文档中被移除时会调用disconnectedCallback()方法。

如果自定义元素定义了静态的observedAttributes属性,其值为一个属性名的数组,且如果任何这些命名属性在这个自定义元素的一个实例上被设置(或修改),浏览器会调用attributeChangeCallback()方法。这个回调可以根据属性值的变化采取必要的步骤以更新组件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button onclick="createCus()">添加元素</button>
<button onclick="removeCus()">移除元素</button>
<button onclick="addSize()">增加尺寸</button>
<button onclick="changeColor()">修改颜色属性</button>
<div id="block"></div>
</body>
</html>

<script>
    class CustomCircle extends HTMLElement {
        connectedCallback() {
            this.style.borderRadius = "50%";
            this.style.border = "solid black 1px"
            this.style.display = "block"
            this.style.width = "50px";
            this.style.height = "50px";
            this.classList.add("custom")
            this.customSize = 50;
            console.log("元素被添加到文档");
        }

        disconnectedCallback() {
            console.log("元素被移除");
        }

        static get observedAttributes() {
            return ["cus"];
        }

        get size() {
            return this.customSize;
        }

        set size(val) {
            this.customSize = val;
            this.style.width = val + "px";
            this.style.height = val + "px";
        }

        attributeChangedCallback(name,oldVal,newVal) {
            this.style.background = newVal;
            console.log(name,oldVal,newVal);
        }
    }

    customElements.define("custom-circle",CustomCircle);

    function createCus() {
        let cus = new CustomCircle();
        document.querySelector("#block").append(cus);
    }

    function removeCus() {
        let cus = document.querySelector(".custom")
        if (cus)  cus.remove();
    }

    function addSize() {
        let cus = document.querySelector(".custom")
        if (cus) {
            cus.size = cus.size + 10;
        }
    }

    function changeColor() {
        let cus = document.querySelector(".custom")
        cus.setAttribute("cus","yellow");
    }

</script>

2.1 组件渲染过程

浏览器在解析HTML文档时,当在Web组件还没有定义就遇到其标签时,浏览器会向DOM树中添加一个通用的HTMLElement,即便它们不知道要对它做什么。之后,当自定义元素有定义之后,这个通用元素会被“升级”,从而具备预期的外观与行为。

如果Web组件包含子元素,那么在组件有定义之前它们可能会被不适当地显示出来。可以使用下面的CSS将Web组件隐藏到它们有定义为止。(:not(:defined))。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<cus-ele>
    <span>hello</span>
</cus-ele>
<cus-ele class="cus">
    <span>hello</span>
</cus-ele>
</body>
</html>

<style>
.cus:not(:defined) {
    opacity: 0;
}
</style>

3 影子DOM

Shadom DOM为封装而生,它可以让一个组件拥有自己的“影子”DOM树,这个DOM树不能在主文档中被任意访问,可能拥有局部样式规则,还有其他特性。

3.1 在浏览器中查看组件的影子DOM

浏览器在内部使用DOM/CSS来绘制影子DOM,这个DOM结构一般对用户是隐藏的,但我们可以开发者工具中看见它。在Chrome中,需要打开“Show user agent shadow DOM”选项。

图 <video>标签的影子DOM

我们不能使用一般的JS调用或者选择器来获取shadow DOM 元素,它们不是常规的子元素,而是一种封装手段。

3.2 为元素添加影子DOM

影子DOM允许把一个“影子根节点”附加给一个常规的HTML元素(或自定义元素),后者被称为“影子宿主”(shadow host)。

attachShadow方法是为常规元素附加一个影子根节点,其参数为一个ShadowRootInit类型的对象,其返回一个影子根节点。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="block"></div>
</body>
</html>

<script>
    class CustomEle extends HTMLElement {
        connectedCallback() {
            this.style.display = "block";
            this.style.width = "100px";
            this.style.height = "100px";
            this.style.border = "solid black 1px";
            let shadow = this.attachShadow({mode: 'closed'})
            shadow.innerHTML = "<div>hello shadow</div>"
        }
    }
    customElements.define("custom-ele",CustomEle);
    let cus = new CustomEle();
    document.querySelector(".block").append(cus);
    console.log(cus.shadowRoot); //如果mode 为open则有值,否则为空
</script>

3.2.1 影子DOM封装

1,在创建影子根节点并将其附加于影子宿主时,可以指定其模式开放还是关闭,关闭的影子根节点将被完全封闭,不可访问。如果开放,意味着影子宿主会有一个shadowRoot属性,js可以通过这个属性来访问影子根节点元素。

2,影子根节点下定义的样式不会影响外部的阳光DOM元素,类似地,影子宿主元素的阳光DOM样式也不会影响影子根节点。

3,影子DOM中发生的某些事件(如”load”)会被封闭在影子DOM中,另一些事件,如focus、mouse和键盘事件则会向上冒泡,穿透影子DOM。当一个发源于影子DOM内的事件跨过边界向阳光DOM传播时,其target属性会变成影子宿主元素。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值