公众号:妙蛙种子前端
文章原文地址:Web Components笔记一: Web Components是什么?解决了什么问题? | 妙蛙种子 - 记录WEB前端技术学习成长过程的博客
Web Components是HTML5提供的一种原生组件封装集成的方式,我们可以通过这种技术封装一些类似Vue自定义组件的元素,在html中直接使用。
提到Web Components我们优先会提到组件化,什么是组件化?指的是将相似的业务抽离成固定的交互形式,然后通过html、JavaScript、css去实现这个交互,并且将这个交互的html、JavaScript、css合并在一个独立、封闭的空间内,对外暴露功能接口。用面向对象的思维来看就是:高内聚、低耦合。
前端组件化的阻碍
在jQuery的时代,我们都会遇到这种场景:引入一个幻灯片的插件,js、css都正常引入了,但样式却和官方demo不一致。细查后发现其实是我们全局的css影响了插件的样式。 当然第三方插件也有可能影响到我们的主页面,举个例子:
组件一:
<div>组件一</div>
<style>
div {
color: red;
}
</style>
组件二:
<div>组件二</div>
<style>
div {
color: blue;
}
</style>
以上两个组件分别定义了自己的div标签,并且对自己的div设置了独立的样式。如果将两个组件放在同一个页面的时,就会发现其中一个组件的样式影响到了另一个组件。同时,如果我们在组件内通过JavaScript获取所有的div进行dom操作,也会影响到其他组件的div。
为什么会这样?因为css是影响全局的,并且在页面的任何js中,我们都能通过api操作页面的dom。
Vue是如何解这类问题的?
Vue在定义组件时,可以给组件的 <style>
标签增加了 scoped
属性,来表示标签中的内容为组件局部的样式。如:
<template>
<div>组件一</div>
</template>
<style scoped>
div {
color: red;
}
</style>
在编译时使用 vue-loader
编译这个组件时,会给这个组件生成一个独一无二的ID,作为这个组件的tag。
- 在编译
<template>
时会为每个标签打上data-v-{组件ID}
的属性,如:
<div data-v-73637230>组件一</div>
- 在编译样式时也会将带有
scoped
属性的css
也通通打上data-v-{组件ID}
的属性选择器,如:
div[data-v-73637230] {
color: red;
}
Web Components如何解决组件作用域问题的?
基于以上的问题,现代浏览器提供了两个特性让我们能自由的创建一些可复用的组件,并且组件的状态与外部是完全隔离开的。
Custom Element 特性
创建一个继承 HTMLElement 的自定义dom元素,可以自定义dom元素的各种生命周期,实现dom的自定义功能,如:
class CustomElement extends HTMLElement {
constructor() {
// 必须首先调用 super方法
super();
// 元素的功能代码写在这里
...
}
}
Shadow DOM 特性
允许将隐藏的 DOM 树附加到常规的 DOM 树中。它以 shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的 DOM 元素一样。
我们可以使用js的api来操作 Shadow DOM
,就和操作常规 DOM 一样。例如添加子节点、设置属性,以及为节点添加自己的样式(例如通过 element.style
属性),或者为整个 Shadow DOM 添加样式(例如在 <style>
元素内添加样式)。不同的是,Shadow DOM 内部的元素始终不会影响到它外部的元素(除了 :focus-within
),这为封装提供了便利。
当然 Shadow DOM
也不算是很新的东西,很早前就已经有实际的用途,例如 <video>
标签实际上就是运行在 Shadow DOM
中,包含了一系列的按钮和其他控制器。Shadow DOM 标准允许你为你自己的元素(custom element)维护一组 Shadow DOM。
Web Components学习笔记系列:
Web Components 学习笔记一: Web Components是什么?解决了什么问题?
Web Components 学习笔记二:使用 Custom Elements 创建可复用的自定义组件