Web Components —— Web 组件

简介: Web Components —— Web 组件

image.png


前言

在重学 JavaScript 过程中,了解到了 Web 组件,而其中的一些知识点总感觉和 vuejs 中的某些概念很相似,比如 Web 组件中涉及的内容:

  • HTML 模板,即 template 标签
  • 自定义元素
  • 影子 DOM 和 slot 标签
  • 影子 DOM 实现样式隔离

那么下面就一起看看 Web 组件的这些内容和 vue 中的某些概念相似在哪吧!

image.png

Web Components

Web 组件到底是什么?

Web 组件其实就是一套用于增强 DOM 行为的工具,其内容包括 影子 DOM自定义元素HTML 模板 等.

目前 Web Components 没有得到广泛应用,主要是因为存在以下问题:

  • 没有统一的 "Web Components" 规范
  • Web 组件存在向后不兼容的版本问题
  • 浏览器实现极其不一致

由于存在这些问题,因此使用 Web 组件通常需要引入一个 Web 组件库,用于模拟浏览器中缺失的 Web 组件. 比如 PolymerLitElement,新项目更推荐使用 LitElement.

image.png

HTML 模板 —— template 标签

我们可以先思考下面的问题,并尝试给出一些解决方法。

问题:如何把对应的三个背景颜色分别为红绿蓝的 p 标签,3s 后动态渲染在指定的位置中,如 <div id="root"></div>方案一: 使用 innerHtml

let divRoot =  document.querySelector("#root");
    setTimeout(()=>{
        divRoot.innerHTML = `
         <p class="red">Make me red!</p> 
         <p class="blue">Make me blue!</p> 
         <p class="green">Make me green!</p> 
       `
    },3000);
复制代码

方案二: 使用 createElement + createDocumentFragment + appendChild

let divRoot =  document.querySelector("#root");
    let colors = ['red','blue','green'];
    let fragment =  document.createDocumentFragment();
    for (const color of colors) {
      const p = document.createElement('p');
      p.className = color;
      p.innerText = `Make me ${color}!`
      fragment.appendChild(p);
    }
    setTimeout(()=>{
        divRoot.appendChild(fragment);
     },3000);
复制代码

通过 方案一 和 方案二 都能实现对应的效果,但是对于书写 HTML 结构来讲都是不友好的,首先就是它们都无法做到像直接书写 HTML 时的友好提示,其次就是它们都只适用结构非常简单的内容,一旦结构内容有多层嵌套时,单纯设计 html 结构就变得很复杂。

使用 <template> 标签

PS: <template> 标签是 HTML 中存在的,并不是 vue 中特有的,不要混淆.

在有 Web 组件之前,一直缺少基于 HTML 解析构建 DOM 子树,然后在需要时再把这个子树渲染出来的机制.

Web 组件中,可以通过使用 <template> 标签提前在页面中写出特殊标记,让浏览器自动将其解析为 DOM 子树,但跳过渲染. 如下:

<body>
  <template id="tpl">
    <p>I'm inside a custom element template!</p>
  </template>
</body>
复制代码

image.png

在浏览器中通过开发者工具检查网页内容时,可以看到 <template> 标签中渲染的节点内容 是基于 DocumentFragment,而 DocumentFragment 也是批量向 HTML 中添加元素的高效工具,此时的 DocumentFragment 就像一个对应子树的最小化 document 对象,也就是说,如果需要操作 <template> 标签中节点,必须要先获取对应 DocumentFragment 的引用,即 document.querySelector('#tpl').content.

下面是通过 <template> 标签实现上面问题的解决方案:

<body>
  <div id="root"></div>
  <template id="tpl">
    <p class="red">Make me red!</p>
    <p class="blue">Make me blue!</p>
    <p class="green">Make me green!</p>
  </template>
  <script>
    let divRoot = document.querySelector("#root");
    let tpl = document.querySelector("#tpl").content;
    setTimeout(() => {
      divRoot.appendChild(tpl);
    }, 3000);
  </script>
</body>
复制代码

template 模板脚本

如果在 template 标签中存在对应的 js 脚本,那么脚本执行可以推迟到将 DocumentFragment 的内容实际添加到 DOM 树,即 延迟执行 js 脚本.

直接看下面的例子:

<body>
    <div id="foo"></div>
    <template id="bar">
     <script>
         console.log('Template script executed');
     </script>
    </template>
    <script>
    const fooElement = document.querySelector('#foo');
    const barTemplate = document.querySelector('#bar');
    const barFragment = barTemplate.content;
    console.log('About to add template');// 1. About to add template
    fooElement.appendChild(barFragment);//2. Template script executed
    console.log('Added template');// 3. Added template
    </script>
</body>
复制代码

影子 DOM —— shadow DOM

首先来看下面的问题,然后思考一下:

如何给 HTML 中众多相似的结构去渲染不同的样式呢?通常情况下,为了给每个子树应用唯一的样式,又不使用 style 属性,就需要给每个子树添加一个唯一的类名,然后通过相应的选择符为它们添加样式。

存在的问题:

  • 必须通过 唯一样式选择器 决定渲染对应的样式渲染
  • 样式全部作用于顶级 DOM 树中,即使当前展示的内容需要使用很少的样式
  • 没有真正实现 CSS 样式的隔离,很容易因为书写问题导致 样式冲突

理想情况下,应该能够把 CSS 限制在使用它们的 DOM 上。

影子 DOM 是什么?

通过影子 DOM 就可以将一个 完整的 DOM 树 作为节点添加到 父 DOM 树

即可以实现 DOM 封装,意味着 CSS 样式和 CSS 选择符可以限制在影子 DOM 子树中,而不是作用于整个顶级 DOM 树。

创建影子 DOM

影子 DOM 是通过 attachShadow() 方法创建并添加给有效 HTML 元素的:

  • 影子宿主(shadow host),即容纳影子 DOM 的元素
  • 影子根(shadow root),即影子 DOM 的根节点
  • attachShadow() 方法需要一个 shadowRootInit 对象,即这个对象必须包含一个 mode 属性,值为 "open" 或 "closed"
  • mode 属性值为 "open" 的影子 DOM 的引用可通过 shadowRoot 属性在 HTML 元素上获得,属性值 "closed" 影子 DOM 的引用则无法获取
document.body.innerHTML = `
    <div id="foo"></div>
    <div id="bar"></div>
`;
const foo = document.querySelector('#foo');
const bar = document.querySelector('#bar');
// 创建不同 dom 元素的影子节点,一个 dom 节点只能有一个 影子 DOM
const openShadowDOM = foo.attachShadow({ mode: 'open' });
const closedShadowDOM = bar.attachShadow({ mode: 'closed' });
// 直接访问影子根节点
console.log(openShadowDOM); // #shadow-root (open)
console.log(closedShadowDOM); // #shadow-root (closed)
// 为影子 DOM 添加内容和样式,这里的样式是完全隔离的,并不会发生样式冲突
openShadowDOM.innerHTML = `
 <p>this is red</p>
 <style>
  p{
    background: red;
  }
 </style>
`
closedShadowDOM.innerHTML = `
 <p>this is blue</p>
 <style>
  p{
    background: blue;
  }
 </style>
`
// 通过影子宿主访问影子根节点 
console.log(foo.shadowRoot); // #shadow-root (open)
console.log(bar.shadowRoot); // null
复制代码

image.png

合成与影子 DOM 槽位 slot

影子 DOM 是为自定义 Web 组件设计的,为此需要支持嵌套 DOM 片段,也就是说位于 影子宿主 中的 HTML 需要一种机制以渲染到影子 DOM中去,但这些 HTML 又不需要存在于影子 DOM 树中.

[ 影子 DOM 具有最高优先级 ]

正常情况下,影子 DOM 一添加到元素中,浏览器就会赋予它 最高优先级,优先渲染它的内容而不是原来的 dom 内容,比如下面的例子:

document.body.innerHTML = `
    <div id="foo">
      <h1>I'm foo's child</h1>
    </div>
`;
const foo = document.querySelector('#foo');
const openShadowDOM = foo.attachShadow({
  mode: 'open'
});
// 为影子 DOM 添加内容
openShadowDOM.innerHTML = `
  <p>this is openShadowDOM content</p>
`
复制代码

image.png

[ <slot> 标签 ]

为了显示影子宿主中原本存在的 HTML 内容,我们需要使用 <slot> 标签指示浏览器在哪里放置原来的 HTML 内容,将上面的例子修改成如下形式:

document.body.innerHTML = `
    <div id="foo">
      <h1>I'm foo's child</h1>
    </div>
`;
const foo = document.querySelector('#foo');
const openShadowDOM = foo.attachShadow({
  mode: 'open'
});
// 为影子 DOM 添加内容
openShadowDOM.innerHTML = `
  <p>this is openShadowDOM content</p>
   <slot></slot>
`
复制代码

image.png


目录
相关文章
|
19天前
|
XML 编解码 前端开发
【web组件库系列】封装自己的字体图标库
【web组件库系列】封装自己的字体图标库
67 0
|
19天前
|
消息中间件 druid Java
web后端-SpringCloud-Bus消息总线组件
web后端-SpringCloud-Bus消息总线组件
|
19天前
|
Java 应用服务中间件 容器
SpringBoot之Web原生组件注入
SpringBoot之Web原生组件注入
|
19天前
|
IDE API 开发工具
 鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Web组件
 鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Web组件
54 2
|
10天前
|
域名解析 缓存 网络协议
JavaEE精选-Web组件
JavaEE精选-Web组件
17 1
|
17天前
|
JavaScript 前端开发 架构师
Web Components:自定义元素与Shadow DOM的实践
Web Components是用于创建可重用自定义HTML元素的技术集合,包括Custom Elements、Shadow DOM、HTML Templates和Slots。通过Custom Elements定义新元素,利用Shadow DOM封装私有样式,&lt;slot&gt;元素允许插入内容。自定义元素支持事件处理和属性观察,可复用且样式隔离。它们遵循Web标准,兼容各前端框架,注重性能优化,如懒加载和Shadow DOM优化。
17 0
|
18天前
|
JavaScript 前端开发 API
Vue中的组件:构建现代Web应用的基石
Vue中的组件:构建现代Web应用的基石
|
19天前
|
SQL 分布式计算 资源调度
常用大数据组件的Web端口号总结
这是关于常用大数据组件Web端口号的总结。通过虚拟机名+端口号可访问各组件服务:Hadoop HDFS的9870,YARN的ResourceManager的8088和JobHistoryServer的19888,Zeppelin的8000,HBase的10610,Hive的10002。ZooKeeper的端口包括客户端连接的2181,服务器间通信的2888以及选举通信的3888。
27 2
常用大数据组件的Web端口号总结
|
19天前
|
移动开发 JavaScript 前端开发
【专栏:HTML进阶篇】HTML模板与Web组件:可复用的网页元素
【4月更文挑战第30天】HTML模板和Web组件提升网页开发效率和可维护性。HTML模板,如&lt;template&gt;元素和服务器端模板引擎,用于创建可复用的HTML结构。Web组件是自定义的HTML元素,结合影子DOM和模板,实现封装的可重用组件。两者助力构建高效、现代的网页和网站。
|
19天前
|
前端开发 JavaScript vr&ar
前端新技术探索:WebAssembly、Web Components与WebVR/AR
【4月更文挑战第12天】WebAssembly、Web Components和WebVR/AR正重塑Web应用的未来。WebAssembly允许C/C++等语言在Web上高效运行,提供接近原生的性能,如游戏引擎。Web Components通过Custom Elements和Shadow DOM实现可复用的自定义UI组件,提升模块化开发。WebVR/AR(现WebXR)则让VR/AR体验无需额外应用,直接在浏览器中实现。掌握这些技术对前端开发者至关重要。
25 3