<
>

2023年了该了解下WebComponent使用教程

2023-02-25 08:56:48 来源:易采站长站 作者:

目录
正文三项主要技术1、Custom elements (自定义元素)生命周期函数2、HTML templates(HTML 模板)3、Shadow DOM(影子 DOM)动态创建 webComponent 组件例子

正文

WebComponent>

一个简单例子,让页面显示 hello world:

<body>
  <!-- 使用组件的方式 -->
  <my-text />
  <script>
    class MyText extends HTMLElement {
      constructor() {
        super();
        this.append("hello world");
      }
    }
    window.customElements.define("my-text", MyText);
  </script>
</body>

三项主要技术

1、Custom>
    一组 JavaScript API,允许您定义 custom elements 及其行为,然后可以在您的用户界面中按照需要使用它们

    分为两种形式:

    自主定制元素:是独立的元素,它不继承其他内建的 HTML 元素,可以直接把它们写成 HTML 标签的形式,来在页面上使用,例如我们刚才自定义的 <my-text>

    自定义内置元素:继承自内置的 HTML 元素。指定所需扩展的元素

      使用时需通过 is 属性指定 custom element 的名称,必须包含一个短横线注册的时候必须使用 extends 的属性
      <!-- 自定义内置元素 使用 is-->
      <body>
        <!-- 使用组件的方式 -->
        <p is="color-p" color="green">云牧</p>
        <script>
          class ColorP extends HTMLParagraphElement {
            constructor() {
              super();
              this.style.color = this.getAttribute("color");
            }
          }
          window.customElements.define("color-p", ColorP, { extends: "p" });
        </script>
      </body>
      

      推荐在 connectedCallback 生命周期函数,处理节点操作

      <!-- 自主定制元素-->
      <body>
        <my-text />
        <script>
          class MyText extends HTMLElement {
            constructor() {
              super();
            }
            connectedCallback() {
              this.append("hello world");
            }
          }
          window.customElements.define("my-text", MyText);
        </script>
      </body>
      

      生命周期函数

      connectedCallback:插入文档时,可能被多次触发,比如删除后又添加到文档

      disconnectedCallback:从文档删除时,可配置做清理工作

      adoptedCallback:被移动新文档时

      attributeChangedCallback:属性变化时

        配合>observedAttributess 属性一起使用,指定监听的属性使用 setAttribute 方法更新属性

        不同操作触发的生命周期函数:

        例子:

        <body>
          <div id="container">
            <p is="my-text" text="云牧" id="myText"></p>
          </div>
          <button id="btnUpdateText">更新属性</button>
          <button id="btnRemove">删除节点</button>
          <button id="btnRestore">恢复节点</button>
          <button id="btnAdopt">移动节点</button>
          <iframe src="./ifr.html" id="ifr"></iframe>
          <script>
            class MyText extends HTMLParagraphElement {
              constructor() {
                super();
              }
              connectedCallback() {
                console.log("生命周期:connectedCallback");
                this.append("你好:" + this.getAttribute("text"));
              }
              disconnectedCallback() {
                console.log("生命周期:disconnectedCallback");
                this.innerHTML = "";
              }
              // 监测的属性
              static get observedAttributes() {
                return ["text"];
              }
              attributeChangedCallback(name, oldValue, newValue) {
                console.log("生命周期:attributeChangedCallback", name, oldValue, newValue);
                // 最先触发是此函数,判断是不是第一次触发,第一次的话,只由 connectedCallback 处理
                if (oldValue != null) {
                  this.replaceChildren("你好:" + newValue);
                }
              }
              adoptedCallback() {
                console.log("生命周期:adoptedCallback");
              }
            }
            window.customElements.define("my-text", MyText, { extends: "p" });
            const myText = document.getElementById("myText");
            btnUpdateText.addEventListener("click", function (e) {
              myText.setAttribute("text", "黛玉");
            });
            btnRemove.addEventListener("click", function (e) {
              myText.remove();
            });
            btnRestore.addEventListener("click", function (e) {
              container.appendChild(myText);
            });
            btnAdopt.addEventListener("click", () => {
              const textNode = ifr.contentWindow.document.getElementById("myText");
              container.appendChild(document.adoptNode(textNode));
            });
          </script>
        </body>
        

        2、HTML>
          使用 JS 模板字串符的方式创建模板,提示不友好,复用性差
          <body>
            <product-item
              name="关东煮"
              img="//img10.360buyimg.com/seckillcms/s200x200_jfs/t1/121953/18/20515/175357/61e7dc79Ee0acbf20/4f4f56abd2ea2f75.jpg!cc_200x200.webp"
              price="49.8"
            ></product-item>
            <script>
              class ProductItem extends HTMLElement {
                constructor() {
                  super();
                }
                connectedCallback() {
                  const content = `
                            <img class="img" src="https://misc.360buyimg.com/lib/skin/e/i/error-jd.gif" />
                            <div class="name"></div>
                            <div class="price"></div>
                        `;
                  this.innerHTML = content;
                  this.querySelector(".img").src = this.getAttribute("img");
                  this.querySelector(".name").innerText = this.getAttribute("name");
                  this.querySelector(".price").innerText = this.getAttribute("price");
                }
              }
              window.customElements.define("product-item", ProductItem);
            </script>
          </body>
          

          template 方式

          <body>
            <!-- template -->
            <template id="tpl-product-item">
              <img class="img" src="https://misc.360buyimg.com/lib/skin/e/i/error-jd.gif" />
              <div class="name"></div>
              <div class="price"></div>
            </template>
            <product-item
              name="关东煮"
              img="//img10.360buyimg.com/seckillcms/s200x200_jfs/t1/121953/18/20515/175357/61e7dc79Ee0acbf20/4f4f56abd2ea2f75.jpg!cc_200x200.webp"
              price="49.8"
            ></product-item>
            <script>
              class ProductItem extends HTMLElement {
                constructor() {
                  super();
                }
                connectedCallback() {
                  const content = document.getElementById("tpl-product-item").content.cloneNode(true);
                  // 插入克隆的模板内容
                  this.append(content);
                  this.querySelector(".img").src = this.getAttribute("img");
                  this.querySelector(".name").innerText = this.getAttribute("name");
                  this.querySelector(".price").innerText = this.getAttribute("price");
                }
              }
              window.customElements.define("product-item", ProductItem);
            </script>
          </body>
          

          slot

          <body>
            <template id="tpl-test">
              <style>
                .title {
                  color: green;
                }
              </style>
              <div class="title">标题</div>
              <slot name="slot-des">默认内容</slot>
            </template>
            <test-item>
              <div slot="slot-des">不是默认内容</div>
            </test-item>
            <script>
              class TestItem extends HTMLElement {
                constructor() {
                  super();
                }
                connectedCallback() {
                  const content = document.getElementById("tpl-test").content.cloneNode(true);
                  const shadow = this.attachShadow({ mode: "open" });
                  shadow.append(content);
                }
              }
              window.customElements.define("test-item", TestItem);
            </script>
          </body>
          

          3、Shadow>

          影子DOM,其内部样式不共享

          <body>
            <!--  不受外部 .container.container 的颜色影响 -->
            <my-item-s></my-item-s>
            <div class="container">My item</div>
            <style>
              .container.container {
                color: green;
              }
            </style>
            <template id="tpl">
              <style>
                .container {
                  color: pink;
                }
              </style>
              <div class="container">My Item</div>
            </template>
            <script>
              class MyItemShadow extends HTMLElement {
                constructor() {
                  super();
                }
                connectedCallback() {
                  const content = document.getElementById("tpl").content.cloneNode(true);
                  const shadow = this.attachShadow({ mode: "open" });
                  shadow.append(content);
                }
              }
              window.customElements.define("my-item-s", MyItemShadow);
            </script>
          </body>
          

          影子DOM,其内部元素不可以直接被访问到

          有一个重要的参数 mode

            open: shadow root 元素通过 js 从外部访问根节点closed:拒绝 js 从外部访问关闭的 shadow root 节点
            <body>
              <template id="tpl">
                <div class="title"></div>
                <div class="des"></div>
              </template>
              <note-item class="note-item" title="标题" des="内容"></note-item>
              <script>
                class NoteItem extends HTMLElement {
                  constructor() {
                    super();
                  }
                  connectedCallback() {
                    const content = document.getElementById("tpl").content.cloneNode(true);
                    const shadow = this.attachShadow({ mode: "open" });
                    shadow.append(content);
                    // 如果是 open 则可以继续访问操作内部 dom
                    // console.log(document.querySelector(".note-item").shadowRoot.querySelector(".title"));
                    shadow.querySelector(".title").textContent = this.getAttribute("title");
                    shadow.querySelector(".des").textContent = this.getAttribute("des");
                  }
                }
                window.customElements.define("note-item", NoteItem);
              </script>
            </body>
            

            引入外部样式:

            <body>
              <template id="tpl">
                <!-- 方式一: -->
                <link rel="stylesheet" href="index.css" rel="external nofollow"  />
                <div>My Item</div>
              </template>
              <my-item></my-item>
              <script>
                class MyItem extends HTMLElement {
                  constructor() {
                    super();
                  }
                  connectedCallback() {
                    const content = document.getElementById("tpl").content.cloneNode(true);
                    const shadow = this.attachShadow({ mode: "open" });
                    shadow.append(content);
                    // 方式二:
                    const linkEl = document.createElement("link");
                    linkEl.setAttribute("rel", "stylesheet");
                    linkEl.setAttribute("href", "index.css");
                    shadow.appendChild(linkEl);
                  }
                }
                window.customElements.define("my-item", MyItem);
              </script>
            </body>
            

            动态创建>
              通过创建 商品 组件,并使得点击能跳转
              <body>
                <div id="product-list" style="display: flex"></div>
                <template id="product-item">
                  <style>
                    .product-item {
                      margin-left: 15px;
                      cursor: pointer;
                    }
                    .img {
                      width: 100px;
                    }
                    .name {
                      text-align: center;
                    }
                    .price {
                      color: #999;
                      text-align: center;
                    }
                  </style>
                  <div class="product-item">
                    <img class="img" src="https://misc.360buyimg.com/lib/skin/e/i/error-jd.gif" />
                    <div class="name"></div>
                    <div class="price"></div>
                  </div>
                </template>
                <script>
                  class ProductItemElement extends HTMLElement {
                    constructor(props) {
                      super(props);
                      this.addEventListener("click", () => {
                        window.open(`https://item.jd.com/${this.id}.html`);
                      });
                    }
                    connectedCallback() {
                      const shadow = this.attachShadow({ mode: "open" });
                      const content = document.getElementById("product-item").content.cloneNode(true);
                      content.querySelector(".img").src = this.img;
                      content.querySelector(".name").innerText = this.name;
                      content.querySelector(".price").innerText = this.price;
                      shadow.appendChild(content);
                    }
                  }
                  window.customElements.define("product-item", ProductItemElement);
                </script>
                <script>
                  const products = [
                    {
                      name: "关东煮",
                      img: "//img10.360buyimg.com/seckillcms/s200x200_jfs/t1/121953/18/20515/175357/61e7dc79Ee0acbf20/4f4f56abd2ea2f75.jpg!cc_200x200.webp",
                      id: "10026249568453",
                      price: 49.8
                    },
                    {
                      name: "土鸡蛋",
                      img: "//img11.360buyimg.com/seckillcms/s200x200_jfs/t1/172777/32/27438/130981/61fbd2e0E236000e0/7f5284367e2f5da6.jpg!cc_200x200.webp",
                      id: "10024773802639",
                      price: 49.8
                    },
                    {
                      name: "东北蜜枣粽子",
                      img: "//img20.360buyimg.com/seckillcms/s200x200_jfs/t1/129546/31/19459/110768/60b1f4b4Efd47366c/3a5b80c5193bc6ce.jpg!cc_200x200.webp",
                      id: "10035808728318",
                      price: 15
                    }
                  ];
                  const productList = document.getElementById("product-list");
                  const elList = products.map(product => {
                    // 创建组件
                    const el = document.createElement("product-item");
                    el.img = product.img;
                    el.name = product.name;
                    el.price = product.price;
                    el.id = product.id;
                    return el;
                  });
                  productList.append.apply(productList, elList);
                </script>
              </body>

              以上就是2023年了该了解下WebComponent使用教程的详细内容,更多关于WebComponent使用教程的资料请关注易采站长站其它相关文章!

暂时禁止评论

微信扫一扫

易采站长站微信账号