本文参考《你的前端框架要被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. Swagger 学习资料

    Swagger 学习资料 网址 Spring Boot中使用Swagger2构建强大的RESTful API文档 http://blog.didispace.com/springbootswagger ...

  2. stm32软件编程的框架及注意事项——rtos篇

    0.通常,嵌入式软件(这里指单片机系统)的框架千变万化,有带rtos的,也有裸机的. 0.1.写过带系统的,也写过裸机的,这里总结一下两个类型的框架,记录下自己的心得,主要是文字描述,框架图可以后期添 ...

  3. Manjaro Linux使用1月滚粗记

    每个OIer都有对Linux的向往(雾) 这不,一个月前我便看上了Manjaro,主要原因是因为Manjaro软件包全,安装简便,下面就来说说我退回windows的原因: 1.桌面卡顿,我用的gnom ...

  4. LeetCode 1081. Smallest Subsequence of Distinct Characters

    原题链接在这里:https://leetcode.com/problems/smallest-subsequence-of-distinct-characters/ 题目: Return the le ...

  5. 再谈System.BadImageFormatException

    今天,当我们继续学习.NET异常处理系列时,我们将查看System.BadImageFormatException.System.BadImageFormatException与GIF或JPG无关,而 ...

  6. minio gataway 模式快速提供s3 兼容的文件服务

    实际很多场景我们已经有了遗留系统的文件存储方式(ftp,或者共享目录),但是这个方式可能不是很好,对于web 不是很友好 实际上minio 也提供了gateway 的模式,可以方便快速的将遗留系统的存 ...

  7. stack的简单用法总结

    stack中常见方法 top():返回一个栈顶元素的引用,类型为 T&.如果栈为空,返回值未定义. push(const T& obj):可以将对象副本压入栈顶.这是通过调用底层容器的 ...

  8. JavaScript var、let、const

    var申明的变量是有作用域的 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量: 'use strict'; function foo() { var x = 1; ...

  9. nginx 反向代理之 负载均衡

    Nginx通过upstream和proxy_pass实现了负载均衡.本质上也是Nginx的反向代理功能,只不过后端的server为多个. 案例一(简单的轮询): upstream www { serv ...

  10. nginx 反向代理之 proxy_redirect

    proxy_redirect 该指令用来修改被代理服务器返回的响应头中的Location头域和“refresh”头域. 语法结构为: proxy_redirect redirect replaceme ...