IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 初探Web Components -> 正文阅读

[JavaScript知识库]初探Web Components

前言:对于前端来说组件并不会陌生,React和Vue中组件经常被使用,浏览器的原生组件就是Web Components,原生组件相对于框架组件来说更加简单直接,符合直觉,不用加载任何外部模块,代码量小。最近我在做一个谷歌浏览器插件需求时候,发现往页面中插入元素可能会对当前页面产生影响,为了避免这种不必要的影响,我选择使用了自定义组件。

1、自定义组件封装

创建index.js文件,定义一个类继承自HTMLElement,借助template来快速创建元素。

class FoodCard extends HTMLElement {
  constructor() {
    super();
    const templateElem = document.getElementById('myFoodCard');
    const content = templateElem.content.cloneNode(true);
    this.appendChild(content);
  }
}
window.customElements.define('food-card', FoodCard); 

2、自定义组件使用

创建index.html文件,直接在html文件中引入自定义标签即可。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>web components</title>
  </head>
  <body>
    <template id="myFoodCard">
      <style>
        section {
          padding: 10px;
          box-sizing: border-box;
          display: flex;
          align-items: center;
          flex-direction: row;
        }
        section .left{
          background-color: aquamarine;
          min-width: 20px;
          height: 100%;
        }
        section .right {
          flex: 1;
          padding-left: 10px;
        }
      </style>
      <section>
        <div class="left">图片</div>
        <div class="right">
          <div class="right-top">标题</div>
          <div class="right-bottom">描述</div>
        </div>
      </section>
    </template>
    <food-card/>
    <script src="./index.js"></script>
  </body>
</html>

此时页面可以看到如下内容:
在这里插入图片描述
我们的自定义组件已经可以使用,不过缺少传参。因此,做出如下修改
index.js

class FoodCard extends HTMLElement {
  constructor() {
    super();
    const templateElem = document.getElementById('myFoodCard');
    const content = templateElem.content.cloneNode(true);
    content.querySelector('.image').setAttribute('src', this.getAttribute('src'));
    content.querySelector('.right-top').innerHTML=this.getAttribute('title');
    content.querySelector('.right-bottom').innerHTML=this.getAttribute('description');
    this.appendChild(content);
  }
}
window.customElements.define('food-card', FoodCard); 

index.html文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>web components</title>
  </head>
  <body>
    <template id="myFoodCard">
      <style>
        section {
          padding: 10px;
          box-sizing: border-box;
          display: flex;
          align-items: center;
          flex-direction: row;
        }
        section .left{
          min-width: 20px;
          max-width: 200px;
          height: 100%;
          overflow: hidden;
        }
        .left img{
          width: 100%;
        }
        section .right {
          flex: 1;
          padding-left: 10px;
        }
      </style>
      <section>
        <div class="left">
          <img src="" class="image"/>
        </div>
        <div class="right">
          <div class="right-top">标题</div>
          <div class="right-bottom">描述</div>
        </div>
      </section>
    </template>
    <food-card
      src="http://qianlingvip.cn:3300/uploads/2021-05-13-15-13-451546.jpg"
      title="红烧猪蹄"
      description="红烧猪蹄是补充胶原蛋白的不二选择"
    />
    <script src="./index.js"></script>
  </body>
</html>

此时,我们就可以改变属性动态改变自定义组件中的内容了。
在这里插入图片描述

3、Shadow DOM

我们不希望用户能够看到的内部代码,Web Component 允许内部代码隐藏起来,这叫做 Shadow DOM,即这部分 DOM 默认与外部 DOM 隔离,内部任何代码都无法影响外部。

自定义元素的this.attachShadow()方法开启 Shadow DOM,详见下面的代码。

class FoodCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow( { mode: 'closed' } );
    const templateElem = document.getElementById('myFoodCard');
    const content = templateElem.content.cloneNode(true);
    content.querySelector('.image').setAttribute('src', this.getAttribute('src'));
    content.querySelector('.right-top').innerHTML=this.getAttribute('title');
    content.querySelector('.right-bottom').innerHTML=this.getAttribute('description');
    shadow.appendChild(content);
  }
}
window.customElements.define('food-card', FoodCard); 

上面代码中,this.attachShadow()方法的参数{ mode: ‘closed’ },表示 Shadow DOM 是封闭的,不允许外部访问。

至此,这个 Web Component 组件就完成了。

4、事件

在上边基础上如果需要添加一些事件,可以进行如下改造。

class FoodCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow( { mode: 'closed' } );
    const templateElem = document.getElementById('myFoodCard');
    const content = templateElem.content.cloneNode(true);
    const left = content.querySelector('.left');
    content.querySelector('.image').setAttribute('src', this.getAttribute('src'));
    content.querySelector('.right-top').innerHTML=this.getAttribute('title');
    content.querySelector('.right-bottom').innerHTML=this.getAttribute('description');
    // 事件添加
    left.addEventListener('click', ()=>{
      console.log('点击了');
    })
    shadow.appendChild(content);
  }
}
window.customElements.define('food-card', FoodCard); 

5、生命周期回调

注意: 生命周期回调在构造方法之外使用

5.1、connectedCallback

每次将自定义元素附加到与文档连接的元素中时调用。这将在每次移动节点时发生,并且可能在元素的内容被完全解析之前发生。

class FoodCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow( { mode: 'closed' } );
    const templateElem = document.getElementById('myFoodCard');
    const content = templateElem.content.cloneNode(true);
    const left = content.querySelector('.left');
    content.querySelector('.image').setAttribute('src', this.getAttribute('src'));
    content.querySelector('.right-top').innerHTML=this.getAttribute('title');
    content.querySelector('.right-bottom').innerHTML=this.getAttribute('description');
    left.addEventListener('click', ()=>{
      console.log('点击了');
    })
    shadow.appendChild(content);
  }
  connectedCallback(){
    console.log('触发');
  }
}
window.customElements.define('food-card', FoodCard); 
5.2、disconnectedCallbac

每次自定义元素与文档的 DOM 断开连接时调用。

5.3、adoptedCallback

每次将自定义元素移动到新文档时调用。

5.4、attributeChangedCallback

每次添加、删除或更改自定义元素的属性之一时调用

6、组件单独抽离

由于上述封装时候还需要在index.html文件里写入模板内容,这样就会导致index.html增添了一些无用的DOM元素,因此,将template元素内容抽离到组件里边去。改动如下:
index.js

const templateElemList = `
<style>
  section {
    padding: 10px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    flex-direction: row;
  }
  section .left{
    min - width: 20px;
    max-width: 200px;
    height: 100%;
    overflow: hidden;
  }
  .left img{
    width: 100%;
  }
  section .right {
    flex: 1;
    padding-left: 10px;
  }
</style>
<section>
  <div class="left">
    <img src="" class="image" />
  </div>
  <div class="right">
    <div class="right-top">标题</div>
    <div class="right-bottom">描述</div>
  </div>
</section>`;
class FoodCard extends HTMLElement {
  constructor() {
    super();
    let templateElem = document.createElement("template");
    templateElem.setAttribute("id", "myFoodCard");
    templateElem.innerHTML = templateElemList;
    const shadow = this.attachShadow({ mode: "closed" });
    const content = templateElem.content.cloneNode(true);
    const left = content.querySelector(".left");
    content
      .querySelector(".image")
      .setAttribute("src", this.getAttribute("src"));
    content.querySelector(".right-top").innerHTML = this.getAttribute("title");
    content.querySelector(".right-bottom").innerHTML =
      this.getAttribute("description");
    left.addEventListener("click", () => {
      console.log("点击了");
    });
    shadow.appendChild(content);
  }
  connectedCallback() {
    console.log("触发");
  }
}
window.customElements.define("food-card", FoodCard);

index.html中将template部分内容去掉

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>web components</title>
  </head>
  <body>
    <food-card
      src="http://qianlingvip.cn:3300/uploads/2021-05-13-15-13-451546.jpg"
      title="红烧猪蹄"
      description="红烧猪蹄是补充胶原蛋白的不二选择"
    />
    <script src="./index.js"></script>
  </body>
</html>

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-31 23:55:34  更:2022-03-31 23:58:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:06:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码