本文参考《你的前端框架要被web组件替代了》。

于2011年面世的Web Components是一套功能组件,让开发者可以使用 HTML、CSS 和 JavaScript 创建可复用的组件。这意味着你无需React或Angular等框架也能创建组件。不仅如此,这些组件还都可以无缝集成到这些框架中。
有史以来头一次,我们只要使用HTML、CSS 和 JavaScript就能创建可在任何现代浏览器中运行的可复用组件了。现在,桌面平台的Chrome、Safari、Firefox 和 Opera,iOS 上的Safari和Android上的Chrome最新版本都支持Web Components。

一、学习链接

1、https://javascript.ruanyifeng.com/htmlapi/webcomponents.html

2、https://developer.mozilla.org/zh-CN/docs/Web/Web_Components

3、https://www.webcomponents.org/

4、https://github.com/webcomponents

5、https://github.com/mdn/web-components-examples

二、好处

1、原生,无需框架;
2、易于集成,无需转换;
3、真正的作用域 CSS;
4、标准化,只有 HTML、CSS 和 JavaScript。

5、互操作性

react和vue仅限于他们的生态内,不能在vue中调用react组件,也不能在react中用vue组件。但是webComponents可以超越框架而存在,可以在不同的技术栈中使用。可以在react,vue中使用,也可以在原生js使用。且不用考虑react或者vue升级,改变公共组件。
6、寿命

因为组件的互操作性,它们将有更长的寿命,基本不需要为了适应新的技术而重写。
7、移植性

组件可以在任何地方使用,因为很少甚至没有依赖,组件的使用障碍要明显低于依赖库或者框架的组件

原生代码可以为你带来与框架相同的功能,但性能更强、需要的代码更少,更加简洁。

三、坏处

1、数据绑定

2、状态管理

3、兼容性,需要引入polyfill,Shady CSS polyfill,webcomponents-loader

4、性能

5、全局注入

四、一些例子

<lazy-img
 src="path/to/image.jpg"
 width="480"
 height="320"
 delay="500"
 margin="0px"></lazy-img>
<img
 is="lazy-img"
 src="path/to/img.jpg"
 width="480"
 height="320"
 delay="500"
 margin="0px">
<material-webcomponents></material-webcomponents>

五、测试web组件

import 'path/to/my-element.js';

describe('my-element', () => {
 let element;

 beforeEach(() => {
 element = document.createElement('my-element');

 document.body.appendChild(element);
 });

 afterEach(() => {
 document.body.removeChild(element);
 });

 it('should test my-element', () => {
 // run your test here
 });
});

这里第一行导入 my-element.js 文件,该文件将我们的 Web Components 暴露为 ES6 模块。这意味着测试文件本身也需要作为 ES6 模块加载到浏览器中。这需要下面的 index.html 才能在浏览器中运行测试。除了 Mocha 之外,这个设置还加载了 WebcomponentsJS polyfill,Chai 用于测试,Sinon 用于 spy 和 mock:

<!doctype html>
<html>
 <head>
 <meta charset="utf-8">
 <link rel="stylesheet" href="../node_modules/mocha/mocha.css">
 <script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
 <script src="../node_modules/sinon/pkg/sinon.js"></script>
 <script src="../node_modules/chai/chai.js"></script>
 <script src="../node_modules/mocha/mocha.js"></script>

 <script>
 window.assert = chai.assert;
 mocha.setup('bdd');
 </script>
 <script type="module" src="path/to/my-element.test.js"></script>
 <script type="module">
 mocha.run();
 </script>

 </head>
 <body>
 <div id="mocha"></div>
 </body>
</html>

在加载了所需的脚本之后,我们将 chai.assert 作为全局变量暴露,因此我们可以在测试中使用 assert() 并设置 Mocha 来使用 BDD 接口。然后加载测试文件(在这个示例中只有一个文件),然后我们调用 mocha.run() 来运行测试。

注意,使用 ES6 模块时还需要将 mocha.run() 放在带有 type =“module”的脚本中。这是因为 ES6 模块默认是延迟的,如果 mocha.run() 放在常规脚本标签内,它将在加载 my-element.test.js 之前就执行了。

六、扩展原生元素

class MyButton extends HTMLButtonElement {
 ...

 constructor() {
 super(); // always call super() to run the parent's constructor as well
 }

 connectedCallback() {
 ...
 }

 someMethod() {
 ...
 }
}

customElements.define('my-button', MyButton, {extends: 'button'});
<button is="my-button">

它现在将通过我们的 MyElement 类增强,如果它在不支持自定义元素的浏览器中加载,它将简单地回退到标准按钮,这就是所谓渐进式的增强!

注意,在扩展现有元素时不能使用 Shadow DOM。这只是通过继承所有现有属性、方法和事件并提供其他功能来扩展原生 HTML 元素的一种方法。

七、模版

function supportsTemplate() {
  return 'content' in document.createElement('template');
}

if (supportsTemplate()) {
  // 支持
} else {
  // 不支持
}
class MyElement extends HTMLElement {
 ...

 constructor() {
 const shadowRoot = this.attachShadow({mode: 'open'});

 this.shadowRoot.innerHTML = `
 <template id="view1">
 <p>This is view 1</p>
 </template>

 <template id="view2">
 <p>This is view 2</p>
 </template>

 <div id="container">
 <p>This is the container</p>
 </div>
 `;
 }

 connectedCallback() {
 const content = this.shadowRoot.querySelector('#view1').content.cloneNode(true);
 this.container = this.shadowRoot.querySelector('#container');

 this.container.appendChild(content);
 }
}

模板会包含 HTML 供以后使用。它不会被呈现,最初只会被解析以确保其内容是有效的。模板内的 JavaScript 不会被执行,也不会获取任何外部资源。默认情况下它是隐藏的。

八、自定义元素

class MyElement extends HTMLElement {
 constructor() {
   super();
 }

 connectedCallback() {
 // here the element has been inserted into the DOM
 }
}
window.customElement.define('my-element', MyElement);
<my-element></my-element> 

名称中的短划线( - )是必需的,以避免与任何原生 HTML 元素发生命名冲突。自定义网页元素的标签名必须含有连字符(-),一个或多个都可。这是因为浏览器内置的的HTML元素标签名,都不含有连字符,这样可以做到有效区分。

<!-- 直接使用 -->
<supper-button></supper-button>

<!-- 间接使用 -->
<button is="supper-button"></button>

js创建组件

document.createElement('supper-button')

document.createElement('button', {is: 'supper-button'});
customElements.whenDefined('my-element')
.then(() => {
 // my-element is now defined
})
const element = document.querySelector('my-element');
element.doSomething();

九、shadow dom(css作用域)

所谓Shadow DOM指的是,浏览器将模板、样式表、属性、JavaScript代码等,封装成一个独立的DOM元素。外部的设置无法影响到其内部,而内部的设置也不会影响到外部,与浏览器处理原生网页元素(比如<video>元素)的方式很像。Shadow DOM最大的好处有两个,一是可以向用户隐藏细节,直接提供组件,二是可以封装内部样式表,不会影响到外部。

使用 Shadow DOM 时,自定义元素的 HTML 和 CSS 会完全封装在组件内部。这意味着该元素将在文档的 DOM 树中显示为单个 HTML 标签,其内部 HTML 结构则放在一个 #shadow-root 中。
其实 Shadow DOM 也用在几个原生 HTML 元素上。例如当你的网页中有<video>元素时,它会显示为单个标签;但它也会显示视频的播放控件,这个控件是不会显示在浏览器开发工具中的<video>元素上的。
这些控件实际上是<video>元素的 Shadow DOM 的一部分,因此默认情况下是隐藏的。要在 Chrome 中显示 Shadow DOM,请转到开发工具设置中的“首选项”,然后选中“显示用户代理 Shadow DOM”复选框。当你在开发工具中再次检查视频元素时就能看到并检查元素的 Shadow DOM 了。

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<p>Hello world</p>`;

这里定义了一个带有 mode:'open'的影子根,这意味着它可以在开发工具中检查,并通过查询、配置任何公开的CSS属性或监听它抛出的事件来交互。也可以用mode:'closed'定义影子根,但这里不推荐这样做,因为它不允许组件的使用者以任何方式与它交互;你甚至无法监听到它抛出的事件。
要将HTML添加到影子根,你可以为其innerHTML属性分配HTML字符串或使用<template>元素。

:host {
   display: block;
}

默认情况下,自定义元素会从周围的 CSS 继承一些属性,例如 color 和 font 等。但是如果你希望以纯净状态开始并将所有 CSS 属性重置为组件内的默认值,请使用:

:host {
 all: initial;
}

从外部对组件本身定义的样式优先于 Shadow DOM 中使用:host 定义的样式。

my-element {
 display: inline-block;
}

会覆盖

:host {
 display: block;
}

如果用户定义了自定义组件,并且想从外面设置属性。

#container {
 background-color: var(--background-color);
}

my-element {
 --background-color: #ff0000;
}

如果用户未定义组件,那么在组件内设置默认值。

:host {
 --background-color: #ffffff;
}

#container {
 background-color: var(--background-color);
}

css变量必须用--开头,名称可以自己取。

十、shadow dom(html注入web组件)

组合(Composition)是将Shadow DOM树与用户提供的标记组合在一起的过程。这是通过<slot>元素完成的,该元素本质上是Shadow DOM中的占位符,其中呈现用户提供的标记。用户提供的标记称为Light DOM,组合会将Light DOM 和Shadow DOM组成一个新的 DOM 树。

<image-gallery>
 <img src="foo.jpg" slot="image">
 <img src="b.arjpg" slot="image">
</image-gallery>
<div id="container">
 <div class="images">
 <slot name="image"></slot>
 </div>
</div>

实际渲染的dom树

<div id="container">
 <div class="images">
 <slot name="image">
   <img src="foo.jpg" slot="image">
   <img src="bar.jpg" slot="image">
 </slot>
 </div>
</div>
slot.addEventListener('slotchange', e => {
 const changedSlot = e.target;
 console.log(changedSlot.assignedNodes());
});

十一、shadow dom 事件

1、事件从根节点出发

event.composedPath() 来检索事件所经过的节点数组。但是,事件的 target 属性将始终指向自定义元素本身。

你可以使用 CustomEvent 从自定义元素中抛出所需的任何事件。

class MyElement extends HTMLElement {
 ...

 connectedCallback() {
   this.dispatchEvent(new CustomEvent('custom', {
      detail: {message: 'a custom event'}
   }));
 }
}

// on the outside
document.querySelector('my-element').addEventListener('custom', e => console.log('message from event:', e.detail.message));

2、事件来自子节点

当从 Shadow DOM 内的节点而不是自定义元素本身抛出一个事件时,除非它使用 composition:true 创建,否则它不会从 Shadow DOM 中弹出。

class MyElement extends HTMLElement {
 ...

 connectedCallback() {
   this.container = this.shadowRoot.querySelector('#container');

   // dispatchEvent is now called on this.container instead of this
   this.container.dispatchEvent(new CustomEvent('custom', {
       detail: {message: 'a custom event'},
       composed: true
    }));
  }
} 

Web API之Web Components的更多相关文章

  1. Web APi之Web Host消息处理管道(六)

    前言 我们知道Web API本身是无法提供请求-响应的机制,它是通过Web Host以及Self Host的寄宿的宿主方式来提供一个请求-响应的运行环境.二者都是将请求和响应抽象成HttpRespon ...

  2. 返璞归真 asp.net mvc (11) - asp.net mvc 4.0 新特性之自宿主 Web API, 在 WebForm 中提供 Web API, 通过 Web API 上传文件, .net 4.5 带来的更方便的异步操作

    原文:返璞归真 asp.net mvc (11) - asp.net mvc 4.0 新特性之自宿主 Web API, 在 WebForm 中提供 Web API, 通过 Web API 上传文件, ...

  3. http服务 WCF、Web API、Web service、WCF REST之间的区别

      http服务 WCF.Web API.Web service.WCF REST之间的区别 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web ...

  4. NET Web API和Web API Client Gen使Angular 2应用程序

    使用ASP.NET Web API和Web API Client Gen使Angular 2应用程序的开发更加高效 本文介绍“ 为ASP.NET Web API生成TypeScript客户端API ” ...

  5. Web API和Web Service

    首先,Web API是由Web Service演变而来,它们两者关系就是所有Web Service都是API,但并非所有API都是Web Service.其次,两者都有利于信息的传输,但Web API ...

  6. ASP.NET Web API——选择Web API还是WCF

    WCF是.NET平台服务开发的一站式框架,那么为什么还要有ASP.NET Web API呢?简单来说,ASP.NET Web API的设计和构建只考虑了一件事情,那就是HTTP,而WCF的设计主要是考 ...

  7. Web API学习——Web API 强势入门指南

    Web API是一个比较宽泛的概念.这里我们提到Web API特指ASP.NET Web API. 这篇文章中我们主要介绍Web API的主要功能以及与其他同类型框架的对比,最后通过一些相对复杂的实例 ...

  8. different between web api and web service

     https://stackoverflow.com/questions/19336347/what-is-the-difference-between-a-web-api-and-a-web-ser ...

  9. 使用BaiDu Java Script Web Api 在Web开发中嵌入地图使用步骤

    前言 很多做企业网站的朋友,都喜欢有一个关于我们.联系我们的栏目,那么这个栏目放什么内容才能饱满那,只有放个地图才显得有点高大上. 一.产生并复制访问Api的密钥(AK) 1.首先我们需要注册一个百度 ...

  10. 使用ASP.NET Web API和Web API Client Gen使Angular 2应用程序的开发更加高效

    本文介绍“ 为ASP.NET Web API生成TypeScript客户端API ”,重点介绍Angular 2+代码示例和各自的SDLC.如果您正在开发.NET Core Web API后端,则可能 ...

随机推荐

  1. Game of Cards Gym - 101128G (SG函数)

    Problem G: Game of Cards \[ Time Limit: 1 s \quad Memory Limit: 256 MiB \] 题意 题意就是给出\(n\)堆扑克牌,然后给出一个 ...

  2. Docker网络配置、Docker部署分布式项目

    目标 1.Docker网络配置 2.Docker部署SpringCloud项目 Docker网络配置 Docker网络模式介绍 Docker在创建容器时有四种网络模式:bridge/host/cont ...

  3. UFUN函数 UF_ATTR函数(UF_ATTR_read_value 函数用法)

    //此函数的功能是输入tag值,返回与属性标题对应的属性值 static string read_attr(tag_t object_tag) { UF_initialize(); ]="零 ...

  4. ent 基本使用十七 分页与排序

    ent 提供了方便的数据分页以及排序处理 limit 分页 users, err := client.User. Query(). Limit(n). All(ctx) offset 分页 users ...

  5. cloudevents js sdk 简单试用

    cloudevents 目前官方提供了不同语言的sdk,以下是js 的简单学习试用,从目前来说更新不是很好 clone 代码 git clone https://github.com/cloudeve ...

  6. vuex传递数据的流程

    当组件修改数据的时候必须通过store.dispatch来调用actions中的方法,当actions中的方法被触发的时候通过调用commit的方法来触发mutations里面的方法,mutation ...

  7. PHP常用的魔术方法及规则

    1. __construct 具有构造函数的类会在每次创建新对象时先调用此方法;初始化工作执行.2. __desstruct 对象的所有引用都被删除或者当对象被显式销毁时执行.3.__call()在对 ...

  8. gym102222 G. Factories

    gym102222 G. Factories 地址 题目大意: 给一棵n个点的树,选m个点,这m个点只能在叶子节点上,问着m个点中两两之间到达其余各点的距离和最小值是多少题解:任意两点的树上距离和问题 ...

  9. 传统IT容量估算思路

    参考: https://www.cnblogs.com/zhangweizhong/p/5844961.html https://www.cnblogs.com/zhangweizhong/p/577 ...

  10. TopK问题及优化

    腾讯面试题:有100W个战斗力,取前100名的算法. 经典topK问题,结论是:随机选择算法 + 快排思想,通过随机选择算法,找到第k大的数,再进行一次快排中的partition,就能得到TopK的结 ...