浏览器原理 27 # WebComponent

简介: 浏览器原理 27 # WebComponent

说明

浏览器工作原理与实践专栏学习笔记



什么是组件化?

特点:对内高内聚,对外低耦合。

对内各个元素彼此紧密结合、相互依赖,对外和其他组件的联系最少且接口简单。



阻碍前端组件化的因素

比如:在页面中嵌入第三方内容时,需要确保第三方的内容样式不会影响到当前内容,同样也要确保当前的 DOM 不会影响到第三方的内容。

<style>
p {
      background-color: brown;
      color: cornsilk
   }
</style>
<p>time.geekbang.org</p>
<style>
p {
      background-color: red;
      color: blue
   }
</style>
<p>time.geekbang</p>


显然,CSS 的全局属性会阻碍组件化

DOM 也是阻碍组件化的一个因素,因为在页面中任何地方都可以直接读取和修改 DOM。



WebComponent 组件化开发


上面我们了解到了:CSS 和 DOM 是阻碍组件化的两个因素,WebComponent 给出的解决思路


WebComponent 提供了对局部视图封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中,这样就使得局部的 CSS 和 DOM 不会影响到全局。


WebComponent 是怎么实现组件化的


MDN:Web Components


Web Components 概念

Web Components旨在解决这些问题 — 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。


   Custom elements(自定义元素):一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们。


   Shadow DOM(影子DOM):一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。


   HTML templates(HTML模板): <template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。


Web Components 使用


实现 web component 的基本方法通常如下所示:


   创建一个类或函数来指定web组件的功能,如果使用类,请使用 ECMAScript 2015 的类语法。


   使用 CustomElementRegistry.define() 方法注册您的新自定义元素 ,并向其传递要定义的元素名称、指定元素功能的类、以及可选的其所继承自的元素。


   如果需要的话,使用 Element.attachShadow() 方法将一个 shadow DOM 附加到自定义元素上。使用通常的 DOM 方法向 shadow DOM 中添加子元素、事件监听器等等。


   如果需要的话,使用 <template> 和 <slot> 定义一个HTML模板。再次使用常规DOM方法克隆模板并将其附加到您的shadow DOM中。


   在页面任何您喜欢的位置使用自定义元素,就像使用常规HTML元素那样。


Web Components 例子


要使用 WebComponent 需要实现下面三步:


1.使用 template 属性来创建模板。


   利用 DOM 可以查找到模板的内容,但是模板元素是不会被渲染到页面上的,在模板的内部定义样式信息。


<!-- 定义模板样式 -->
<template id="kxm-template">
  <style>
    p {
      background-color: green;
      color: white;
    }
    div {
      width: 200px;
      background-color: blue;
      border: 3px solid red;
      border-radius: 10px;
    }
  </style>
  <div>
      <p>kxm</p>
      <p>kaimo313</p>
  </div>
  <script>
    function foo() {
      console.log('template log');
    }
  </script>
</template>


2.创建一个类

  • 查找模板内容;
  • 创建影子 DOM;
  • 再将模板添加到影子 DOM 上。


可以把影子 DOM 看成是一个作用域,其内部的样式和元素是不会影响到全局的样式和元素的,而在全局环境下,要访问影子 DOM 内部的样式或者元素也是需要通过约定好的接口的。

<!-- 创建一个类 -->
<script>
  class Kaimo313 extends HTMLElement {
    constructor() {
      super()
      // 获取组件模板
      const content = document.querySelector('#kxm-template').content;
      // 创建影子DOM节点
      const shadowDOM = this.attachShadow({ mode: 'open' });
      // 将模板添加到影子DOM上
      shadowDOM.appendChild(content.cloneNode(true));
    }
  }
  // 使用 customElements.define 来自定义元素
  customElements.define('kaimo-313', Kaimo313);
</script>


3.使用该元素

<!-- 使用该元素 -->
<kaimo-313></kaimo-313>
<div>
  <p>kxm</p>
  <p>kaimo313</p>
</div>
<kaimo-313></kaimo-313>


完整的代码展示:

<!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>12.Web Components 例子</title>
</head>
<body>
  <h1>kxm 测试 Web Components 例子</h1>
  <!-- 定义模板样式 -->
  <template id="kxm-template">
    <style>
      p {
        background-color: green;
        color: white;
      }
      div {
        width: 200px;
        background-color: blue;
        border: 3px solid red;
        border-radius: 10px;
      }
    </style>
    <div>
        <p>kxm</p>
        <p>kaimo313</p>
    </div>
    <script>
      function foo() {
        console.log('template log');
      }
    </script>
  </template>
  <!-- 创建一个类 -->
  <script>
    class Kaimo313 extends HTMLElement {
      constructor() {
        super()
        // 获取组件模板
        const content = document.querySelector('#kxm-template').content;
        // 创建影子DOM节点
        const shadowDOM = this.attachShadow({ mode: 'open' });
        // 将模板添加到影子DOM上
        shadowDOM.appendChild(content.cloneNode(true));
      }
    }
    // 使用 customElements.define 来自定义元素
    customElements.define('kaimo-313', Kaimo313);
  </script>
  <!-- 使用该元素 -->
  <kaimo-313></kaimo-313>
  <div>
    <p>kxm</p>
    <p>kaimo313</p>
  </div>
  <kaimo-313></kaimo-313>
</body>
</html>


使用影子 DOM 的输出效果

20210529111831747.png


浏览器如何实现影子 DOM


影子 DOM 的作用


  1. 影子 DOM 中的元素对于整个网页是不可见的;
  2. 影子 DOM 的 CSS 不会影响到整个网页的 CSSOM,影子 DOM 内部的 CSS 只对内部的元素起作用。



影子 DOM 示意图


影子 DOM 都有一个 shadow root 的根节点


2021052911252045.png


浏览器怎么实现 DOM API 无法直接查询到影子 DOM 的内部元素?


   当通过 DOM 接口去查找元素时,渲染引擎会去判断 kaimo-313 属性下面的 shadow-root 元素是否是影子 DOM,如果是,那么就直接跳过 shadow-root 元素的查询操作。


浏览器怎么实现渲染出来的效果就是影子 DOM 内部定义的样式?


   当生成布局树的时候,渲染引擎也会判断 kaimo-313 属性下面的 shadow-root 元素是否是影子 DOM,如果是,那么在影子 DOM 内部元素的节点选择 CSS 样式的时候,会直接使用影子 DOM 内部的 CSS 属性。





目录
相关文章
|
2月前
|
存储 缓存 前端开发
浏览器缓存工作原理是什么?
浏览器缓存工作原理是什么?
|
Web App开发 消息中间件 监控
浏览器原理 39 # 页面性能工具:如何使用 Performance?
浏览器原理 39 # 页面性能工具:如何使用 Performance?
377 0
浏览器原理 39 # 页面性能工具:如何使用 Performance?
|
2月前
|
存储 安全 前端开发
浏览器跨窗口通信:原理与实践
浏览器跨窗口通信:原理与实践
44 0
|
2月前
|
消息中间件 JavaScript 前端开发
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
|
3月前
|
消息中间件 前端开发 Java
【面试题】前端必修-浏览器的渲染原理
【面试题】前端必修-浏览器的渲染原理
|
4月前
|
Web App开发 JavaScript 前端开发
从浏览器原理出发聊聊Chrome插件
本文从浏览器架构演进、插件运行机制、插件基本介绍和一些常见的插件实现思路几个方向聊聊Chrome插件。
767 0
|
7月前
|
安全 算法 网络协议
浏览器基础原理-安全: HTTPS
浏览器基础原理-安全: HTTPS
61 0
|
7月前
|
Web App开发 存储 监控
浏览器基础原理-安全: 渲染进程-安全沙盒
浏览器基础原理-安全: 渲染进程-安全沙盒
37 0
|
7月前
|
安全
浏览器基础原理-安全: CSRF攻击
浏览器基础原理-安全: CSRF攻击
54 0
|
7月前
|
JavaScript 安全 前端开发
浏览器基础原理-安全: 同源策略
浏览器基础原理-安全: 同源策略
36 0

热门文章

最新文章