惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
Apple Machine Learning Research
Apple Machine Learning Research
云风的 BLOG
云风的 BLOG
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
量子位
GbyAI
GbyAI
腾讯CDC
T
Tailwind CSS Blog
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
D
Docker
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
Stack Overflow Blog
Stack Overflow Blog
Hugging Face - Blog
Hugging Face - Blog
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
N
Netflix TechBlog - Medium
Jina AI
Jina AI
IT之家
IT之家
Y
Y Combinator Blog

vivaxy's Blog

SameSite Cookies 解读 - vivaxy's Blog VSCode Conventional Commits 插件 - vivaxy's Blog VSCode Conventional Commits Extension - vivaxy's Blog How Babel Is Built - vivaxy's Blog 一步一步解码 PNG 图片 - vivaxy's Blog Babel 的工程化实现 - vivaxy's Blog 浏览器图像转换手册 - vivaxy's Blog Decoding A PNG Image with JavaScript Step by Step Comprehensive Image Processing on Browsers Use npm Registry to Restric Installing Client Alfred 4 Workflow Open in VSCode Git Tag And Push Git Tag JavaScript PNG 图片编码和解码 - vivaxy's Blog 如何在多个模块中共享异步数据 - vivaxy's Blog JavaScript 函数式编程初窥 - vivaxy's Blog 用纯 CSS 实现弹窗 - vivaxy's Blog 在浏览器中使用 JavaScript 模块 - vivaxy's Blog Karabiner Elements Hyper 改键设置 - vivaxy's Blog Levenshtein 距离 - vivaxy's Blog Levenshtein 距离 - vivaxy's Blog
基于 Custom Elements 的组件化开发 - vivaxy's Blog
vivaxy · 2018-09-17 · via vivaxy's Blog

customElements 是 Web Components 规范下的新 API,可以用来实现组件化开发。

如果你的应用只用兼容最新的 Chrome 浏览器,那么用它来替代 React 或者 Vue 是一个不错的选择。

基本用法

组件声明在一个 HTML 文件中。组件包括样式(Style),节点(DOM)和交互逻辑(Script)。一个组件文件的基本结构如下:

<template>
  <style></style>
  <div>DOM 节点</div>
</template>
<script>
  const componentDocument = document.currentScript.ownerDocument;

  class Component extends HTMLElement {

    static get TAG_NAME() {
      return 'component-tag-name';
    };

    constructor() {
      super();
      const shadow = this.attachShadow({ mode: 'closed' });
      const content = componentDocument.querySelector('template').content.cloneNode(true);
      shadow.appendChild(content);
    }
  }

  customElements.define(Component.TAG_NAME, Component);
</script>

其中 template 节点下包含样式(Style)和节点(DOM)。交互逻辑在 script 标签中。

组件文件通过 <link rel="import" href="./component.html"> 的方式引入 HTML 文件中。在 HTML 文件中使用组件的方式就是直接写组件标签。比如:

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
  <title>HTML</title>
  <link rel="import" href="./component.html">
</head>
<body>
<component-tag-name></component-tag-name>
</body>
</html>

组件注册

customElements.define API 用来组册组件,API 接受三个参数:组件标签名称、组件的类和组件继承的标签类型。如:

customElements.define('expanding-list', ExpandingList, { extends: "ul" });

上面声明了一个标签为 expanding-list 的组件,组件的构造类是 ExpandingList 需要声明,组件继承 ul 标签的特性。

组件构造类

组件的构造类需要继承 HTMLElement 类,或者可以继承 HTMLParagraphElementHTMLElement 的子类,如果继承了 HTMLParagraphElement 这个类,那么组件将拥有 p 标签的特性。

class Component extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'closed' });
    const content = componentDocument.querySelector('template').content.cloneNode(true);
    shadow.appendChild(content);
  }
}

组件内的构造函数是必须的,在构造函数内,我们先需要调用父类的构造函数,然后创建一个 Shadow DOM 节点,再将组件模版内容添加到节点内。

使用 Shadow DOM 可以做到组件内的样式和组件外的样式不互相干扰,可以让组件封装更彻底。

我们可以通过 document.currentScript.ownerDocument; 来拿到模版自身的跟节点。

组件属性

组件可以像 HTML 标签一样使用属性。在组件中可以获取属性。

<component-tag-name attr-name="attr-value"></component-tag-name>
class Component extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'closed' });
    const content = componentDocument.querySelector('template').content.cloneNode(true);
    shadow.appendChild(content);
    const attrValue = this.getAttribute('attr-name');
  }
}

组件生命周期

  • connectedCallback 组件挂载,组件初始化后和移动时会触发
  • disconnectedCallback 组件卸载
  • adoptedCallback 组件被移动到一个新的文档树
  • attributeChangedCallback 组件属性变化

组件事件

可以触发自定义事件。

<template>
  <style></style>
  <button>组件事件</button>
</template>
<script>
  const componentDocument = document.currentScript.ownerDocument;

  class Component extends HTMLElement {

    static get TAG_NAME() {
      return 'component-tag-name';
    };

    static get BUTTON_CLICK() {
      return 'button-click';
    }

    constructor() {
      super();
      const shadow = this.attachShadow({ mode: 'closed' });
      const content = componentDocument.querySelector('template').content.cloneNode(true);
      shadow.appendChild(content);
      const button = shadow.querySelector('button');
      button.addEventListener('click', () => {
        this.dispatchEvent(new CustomEvent(Component.BUTTON_CLICK, { button }));
      });
    }
  }

  customElements.define(Component.TAG_NAME, Component);
</script>

例子