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

目录
  • 正文
  • 三项主要技术
    • 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 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(影子 DOM)

影子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>

动态创建 webComponent 组件例子

  • 通过创建 商品 组件,并使得点击能跳转
<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使用教程的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue Class Component类组件用法

    目录 类组件 1. @component 2. Data属性 3. Methods属性 4. Computed Properties(计算属性) 5. watch 6. hooks 7. 子组件接收父组件传参 extend Mixins props 类组件 1. @component 使用@Component注解,将类转化为 vue 的组件,以下是一个示例 import vue from 'vue' import Component from 'vue-class-component' // H

  • Web componentd组件内部事件回调及痛点剖析

    目录 写在前面 WC 到底是什么? 目前存在的缺陷 1.组件内部事件的回调 2.组件样式覆盖 3.组件内部资源相对路径问题 4.form表单类组件 value 获取问题 5.其它 写在后面 写在前面 最近致力于研究 Web components(以下简称WC),并且也初有成效的拿到了一定的结果,但今天想回过头来重新审视一下 WC. WC 到底是什么? 简单的讲,Web Component 就是把组件封装成 html 标签的形式,并且在使用时不需要写额外的 js 代码. 组件是前端的发展方向,抛开

  • Vue3 构建 Web Components使用详解

    目录 引言 构建 Web Components 属性 事件 插槽 子组件样式问题 方法 总结 引言 有时候想写一个无关框架组件,又不想用原生或者 Jquery 那套去写,而且还要避免样式冲突,用 Web Components 去做刚觉就挺合适的.但是现在 Web Components 使用起来还是不够灵活,很多地方还是不太方便的,如果能和 MVVM 搭配使用就好了. 早在之前 Angular 就支持将组件构建成 Web Components,Vue3 3.2+ 开始终于支持将组建构建成 Web

  • vue3中defineComponent 的作用详解

    vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeScript 而存在的. 我都知道普通的组件就是一个普通的对象,既然是一个普通的对象,那自然就不会获得自动的提示, import { defineComponent } from 'vue' const component = { name: 'Home', props:{ data: String,

  • Web Components实现类Element UI中的Card卡片

    目录 引言 Web Components 核心组成 1. Custom Elements 2. Shadow DOM 3. templates 和 slots 完整代码 Web Components vs Vue Components Web Components 生命周期回调函数 优点 and 缺点 七.基于web components的框架 引言 Web Components 是一个浏览器原生支持的组件化方案,允许你创建新的自定义.可封装.可重用的HTML 标记.不用加载任何外部模块,直接就

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

    目录 正文 三项主要技术 1.Custom elements (自定义元素) 生命周期函数 2.HTML templates(HTML 模板) 3.Shadow DOM(影子 DOM) 动态创建 webComponent 组件例子 正文 WebComponent 是官方定义的自定义组件实现方式,它可以让开发者不依赖任何第三方框架(如Vue,React)来实现自定义页面组件:达到组件复用效果 一个简单例子,让页面显示 hello world: <body> <!-- 使用组件的方式 --&

  • Linux下JDK安装教程

    Linux下JDK安装教程,具体内容如下 1.下载 JDK Linux 版本(注意看自己安装 Linux 系统的位数) oracle 官网下载地址:jdk-7u80-linux-x64.gz 百度云盘:链接: http://pan.baidu.com/s/1eS27kum 密码: wwcv 2.进入虚拟机,查看是否有默认安装的 Open JDK 以 root 用户登录:输入 java -vesion. 如果出现如下信息,则默认安装有,需要卸载 3.卸载默认安装的 Open JDK ①.输入:rp

  • 浅析Yii2 GridView实现下拉搜索教程

    废话不多说了,先给大家展示下效果图,如果大家还很满意请继续往下阅读: 具体怎么实现喃?考虑到一张数据表要下拉效果的字段可能有很多个,我们先在其model中实现一个方法方便后续操作 /** * 下拉筛选 * @column string 字段 * @value mix 字段对应的值,不指定则返回字段数组 * @return mix 返回某个值或者数组 */ public static function dropDown ($column, $value = null) { $dropDownLis

  • mysql 5.7 zip 文件在 windows下的安装教程详解

    1.下载mysql最新版本. http://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5.7.15-winx64.zip 2.解压到文件夹. D:\software\mysql\mysql5.7a 将my-default.ini 复制为 my.ini 3.编辑my.ini # These are commonly set, remove the # and set as required. basedir ="D:/software/mysql/mysql

  • Linux下Nginx安装教程

    Linux下Nginx安装教程分享,具体内容如下 1.安装编译文件及库文件 yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel 2.安装PCRE,Nginx的rewrite的伪静态匹配规则需要用到正则表达式,PCRE就是起到这个作用. 下载地址:wgethttp://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz 如果

  • Android在linux下刷机教程

    只需要下载相应的zip包,不需装什么手机助手. 1.下载相应zip包(ROM) http://download.mokeedev.com/ 比如我在上述网站下的魔趣的对应机型的ROM包. 2.linux下载adb 直接一条命令: apt-get install android-tools-adb 3.进入手机的recovery,一般是开机键+音量上,进入sideload模式,等待发送zip包 4.将电脑上的zip包发送到手机里面去,发送完成会自动刷. 5.刷完会自动重启的,选择禁止恢复 6.重启

  • Windows下tomcat安装教程

    本文为大家分享了Windows下tomcat安装教程,供大家参考,具体内容如下 1.打开官网 2.在左侧的导航栏Download下方选择最新的Tomcat 9,点击页面下方的" 64-bit Windows zip (pgp, md5, sha1)"进行下 3.解压到某目录 4.配置环境变量 a.配置jdk的环境变量(略) b.在系统变量里新建变量名:CATALINA_BASE,变量值:D:\Program Files\apache-tomcat-9.0.6 c.在系统变量里新建变量名

  • pybind11在Windows下的使用教程

    Pybind11算是目前最方便的Python调用C++的工具了, 介绍一下在vs2019上写Python的扩展的HelloWorld 1. 去下载pybind11   https://github.com/pybind/pybind11/releases/tag/v2.3.0 这个库只要include就可以了 2. 用vs新建一个空项目 2.1 调整输出类型为dll, 调整输出文件名为pyd 2.2 include python和pybind11的头文件, 我的python使用anaconda的

  • Windows下MySQL安装教程图文详解

    MySQL安装说明MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于Oracle旗下产品. MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的RDBMS(Relational Database Management System,关系数据库管理系统) 应用软件. MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性. MySQL所使用的 SQL 语

  • WINDOWS下安装MYSQL教程详解

    1.下载安装包 -根据自己电脑系统选择合适的版本:https://dev.mysql.com/downloads/mysql/ 2.配置环境变量 2.1 解压所下载的压缩包 2.2 环境变量 win 10 电脑 这么进去 3.生成data文件 在你解压的目录下,eg:F:\Program Files\mysql-8.0.17-winx64\bin,以管理员身份运行cmd 执行mysqld --initialize-insecure --user=mysql 在eg 目录下生成data目录 4.安

随机推荐