您的位置:首页技术文章

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

【字号: 日期:2023-02-12 11:11:17浏览:2作者:猪猪
目录
  • 正文
  • 三项主要技术
    • 1、Custom elements (自定义元素)
      • 生命周期函数
    • 2、HTML templates(HTML 模板)
      • 3、Shadow DOM(影子 DOM)
      • 动态创建 webComponent 组件例子

        正文

        WebComponent 是官方定义的自定义组件实现方式,它可以让开发者不依赖任何第三方框架(如Vue,React)来实现自定义页面组件;达到组件复用效果

        一个简单例子,让页面显示 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 elements (自定义元素)

        • 一组 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 templates(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 src="https://misc.360buyimg.com/lib/skin/e/i/error-jd.gif" />
                          <div></div>
                          <div></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 src="https://misc.360buyimg.com/lib/skin/e/i/error-jd.gif" />
            <div></div>
            <div></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>标题</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(影子 DOM)

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

        <body>
          <!--  不受外部 .container.container 的颜色影响 -->
          <my-item-s></my-item-s>
          <div>My item</div>
          <style>
            .container.container {
              color: green;
            }
          </style>
          <template id="tpl">
            <style>
              .container {
                color: pink;
              }
            </style>
            <div>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></div>
            <div></div>
          </template>
          <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>
        

        动态创建 webComponent 组件例子

        • 通过创建 商品 组件,并使得点击能跳转
        <body>
          <div id="product-list"></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>
              <img src="https://misc.360buyimg.com/lib/skin/e/i/error-jd.gif" />
              <div></div>
              <div></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使用教程的资料请关注其它相关文章!

        标签: JavaScript