引言
发展历史
Web Components 技术的发展历程可以分为以下几个阶段:
- 2011 年,Google 开始开发 Polymer 项目,旨在创建一种基于 Web 标准的新型 Web 应用开发框架。
- 2013 年,W3C 开始制定 Web Components 标准,包括 Custom Elements、HTML Templates 和 Shadow DOM 等技术。
- 2015 年,Web Components 1.0 草案发布。
- 2017 年,Web Components 1.0 正式标准发布。
- 2018 年,Chrome 63 正式支持 Web Components 技术。
- 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 元素,扩展了浏览器中可用的元素集
实现方式有两种
- 自定义内置元素 继承自标准的 HTML 元素,例如
HTMLParagraphElement
- 独立自定义元素继承自 HTML 元素基类
HTMLElement
自定义内置元素例子
如果你有vue的基础的话,vue的语法中有一个动态组件有一个attribute is ,代表的是哪个组件,这个自定义内置元素和vue的语法很像
可以先看下方的自定义元素的例子,我们对下面的代码做个解释
- html中 p 标签的is 后有一个word-count 代表的就是我们使用了word-count的自定义元素
- script脚本中定义了一个wordCount类继承自HTMLParagraphElement
- 在这个类中定义了一单词统计的功能
- 最后注册了自定义元素(通过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>
上方的代码的预览效果
生命周期
当然自定义元素也有有生命周期的,我们对上方的代码进行扩展
改动点为
- 添加了个删除的button
- 在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的节点,我们稍后会解释
例子
关键代码解释
attachShadow()
来创建影子 DOMmode
设置为"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,你就可以更快的知道这个配置项的作用