Web Components的前世今生

引言

发展历史

Web Components 技术的发展历程可以分为以下几个阶段:

  1. 2011 年,Google 开始开发 Polymer 项目,旨在创建一种基于 Web 标准的新型 Web 应用开发框架。
  2. 2013 年,W3C 开始制定 Web Components 标准,包括 Custom Elements、HTML Templates 和 Shadow DOM 等技术。
  3. 2015 年,Web Components 1.0 草案发布。
  4. 2017 年,Web Components 1.0 正式标准发布。
  5. 2018 年,Chrome 63 正式支持 Web Components 技术。
  6. 2019 年,Web Components 技术开始逐渐被更多的前端开发人员使用,成为前端开发的基础设施之一

谁在使用

1. youtube

2. github

3.quark-design(哈啰)

4.Vue也支持web Components

目标

了解web Components是什么。本文不是全面教程,只是一个简单演示,如果需要全面学习的话可以点我 进行全面学习

Web Components的基本概念

Web Component 是一套不同的技术,允许你创建可重用的定制元素(它们的功能封装在你的代码之外)并且在你的 web 应用中使用它们。

如图, Web Components主要包括了下面的三个技术

Custom Elements

概念

由 Web 开发人员定义行为的 HTML 元素,扩展了浏览器中可用的元素集

实现方式有两种

自定义内置元素例子

如果你有vue的基础的话,vue的语法中有一个动态组件有一个attribute is ,代表的是哪个组件,这个自定义内置元素和vue的语法很像

可以先看下方的自定义元素的例子,我们对下面的代码做个解释

  1. html中 p 标签的is 后有一个word-count 代表的就是我们使用了word-count的自定义元素
  2. script脚本中定义了一个wordCount类继承自HTMLParagraphElement
  3. 在这个类中定义了一单词统计的功能
  4. 最后注册了自定义元素(通过customElements)
js 复制代码
<!DOCTYPE html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Document</title>
 </head>
 <body>
   <div>
       <article contenteditable="">
           <p>my name is  a du</p>
           <p is="word-count"></p>
       </article>
   </div>
   <script>
     class WordCount extends HTMLParagraphElement {
       constructor() {
         super();
         // 得到父亲节点
         var wcParent = this.parentNode;
         // 创建影子节点
         var shadow = this.attachShadow({ mode: "open" });
         var count = "Words: " + countWords(wcParent);
         // 创建一个span节点
         var text = document.createElement("span");
         text.textContent = count;
         // 将创建的节点添加到影子节点
         shadow.appendChild(text);
         //200ms 更新字数统计
         setInterval(function () {
           var count = "Words: " + countWords(wcParent);
           text.textContent = count;
         }, 200);
         //定义统计字数的方法
         function countWords(node) {
           var text = node.innerText || node.textContent;
           return text.split(/\s+/g).length;
         }
       }
     }
     customElements.define("word-count", WordCount, { extends: "p" });
   </script>
 </body>
</html>

上方的代码的预览效果

生命周期

当然自定义元素也有有生命周期的,我们对上方的代码进行扩展

改动点为

  1. 添加了个删除的button
  2. 在constuctor下方的节点添加了两个事件connectedCallback (当自定义元素第一次被连接到文档 DOM 时被调用。)、disconnectedCallback(当自定义元素与文档 DOM 断开连接时被调用)

下方为demo的预览gif

当然Custom element 还有更多的生命周期(adoptedCallback、attributeChangedCallback),我们就不介绍了。

Shadow DOM

概念

对标签和样式的一层 DOM 包装,你将一个 DOM 树附加到一个元素上,并且使该树的内部对于在页面中运行的 JavaScript 和 CSS 是隐藏的。

设置浏览器

这个概念也许有点模糊,我们先不写代码,先打开你的浏览器设置,我们来看个设置

shadow Dom 也许你不注意,平常使用的input,video等其实都是由shadow dom开发的

首先打开浏览器控制台的设置选项

然后再找到Preference -> Elements,把show user anent shadow dom勾上

)

浏览器的video,为什么我们只是引入了,就自动生成了暂停、音量、进度条,其实这些都是shadow Dom的的功劳,我们可以看到有一个shadow-root的节点,我们稍后会解释

例子

关键代码解释

  1. attachShadow() 来创建影子 DOM
  2. mode 设置为 "open" 时,页面中的 JavaScript 可以通过影子宿主的 shadowRoot 属性访问影子 DOM 的内部。

很简单的代码,我们就创建了一个shadow dom

js 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="host"></div>
    <span>我是普通dom</span>
    <script>
      const host = document.querySelector("#host");
      const shadow = host.attachShadow({ mode: "open" });
      const span = document.createElement("span");
      span.textContent = "我是shadow dom";
      shadow.appendChild(span);
    </script>
  </body>
</html>

运行效果如下

对JS上来说基本上是隐藏的

通过querySelector是获取不到元素的,{ mode: "open" } 传入 attachShadow()。当 mode 设置为 "open" 时,页面中的 JavaScript 可以通过影子宿主的 shadowRoot 属性访问影子 DOM 的内部

js 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="host"></div>
    <span>我是普通dom</span>
    <button onClick="onLogSpanLength()">打印Span的长度</button>
    <script>
      const host = document.querySelector("#host");
      const shadow = host.attachShadow({ mode: "open" });
      const span = document.createElement("span");
      span.textContent = "我是shadow dom";
      shadow.appendChild(span);
      function onLogSpanLength() {
        console.log(document.querySelectorAll("span").length);
        console.log(document.querySelector("#host").shadowRoot.querySelectorAll("span").length)
      }
    </script>
  </body>
</html>
 

运行效果如下

CSS封闭性

保持上方的代码不变,我们添加一个css的样式,

css 复制代码
    <style>
      span {
        color: blue;
        border: 1px solid black;
      }
    </style>

运行效果

shadow dom 的特点

从前面的介绍,我们知道shadow dom是游离在 DOM 树之外的节点树,但是它是基于普通 DOM 元素(非 document)创建的,并且创建后的 Shadow-dom 节点可以从界面上直观的看到。最重要的一点是Shadow-dom 具有良好的密封性。

Template和Slot

template

概念

template 顾名思义是模板,他有以下的特点 HTML 内容模板<template>)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以在运行时使用 JavaScript 实例化。

上面的代码不会展示在你的页面中,直到你用 JavaScript 获取它的引用,然后添加到 DOM 中,如下面的代码:

slot

熟悉 Vue 的同学应该都知道"插槽(slot)"的概念,通过使用插槽可以让页面内容的组织更加灵活。

的确,vue的设计灵感也来源此,下方的为官方的一个截图

我们来写一个例子

xml 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 定义一个模板 -->
    <template id="templateId">
      <slot name="element-name">插槽的默认值</slot>
    </template>
    <element-test> </element-test>
    <element-test>
      <span slot="element-name">测试值</span>
    </element-test>
    <script>
      customElements.define(
        "element-test",
        class extends HTMLElement {
          constructor() {
            super();
            // 获取模板的内容
            const template = document.getElementById(
              "templateId"
            ).content;
            // 创建一个shadow节点
            const shadowRoot = this.attachShadow({ mode: "open" }).appendChild(
              //将模板的拷贝到阴影的根结点上
              template.cloneNode(true)
            );
          }
        }
      );
    </script>
  </body>
</html>

效果如下图

调试

使用方式基本同vuedevtool相同 下载地址 下方的为github的截图

Inspector的Tab

我们可以看到Proprrties、attriubutes、methods

Source的Tab

在实际开发中的应用

图片添加header

当然这个还有不同的应用场景 比如用来给图片添加自定义的header

方便调试样式

假如设计师设计了placeholder为不同颜色,当我们不开启浏览器的默认样式的时候,看不到placeholder的内容,开启后,可以方便的看到内容,并且进行修改。

下方的截图为element ui的input的placeholder样式

更好的理解不同的框架的配置

比如我们公司使用的是qiankun

下方的为qiankun的截图,当你理解了shadowDom,你就可以更快的知道这个配置项的作用

相关推荐
杨荧1 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
minDuck3 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!24 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。29 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼35 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k093339 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning1 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人1 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱0011 小时前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js