本文参考《你的前端框架要被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. 《剑指Offer》-005 -用两个栈实现队列

    如题 (总结要点) 用两个栈实现队列 栈; 先进后出 队列: 先进先出 两个栈, 相等于两个杯子; 把一本水倒来倒去, 取到杯子底部的元素,并且删除,再倒回去 原文链接 : 借鉴学习文章列表 链接1: ...

  2. Beta 冲刺随笔汇总

    作业要求 这个作业属于哪个课程 软件工程1916-W(福州大学) 这个作业要求在哪里 项目Beta冲刺(团队) 团队名称 基于云的胜利冲锋队 作业目标 汇总随笔 团队信息 团队名称:基于云的胜利冲锋队 ...

  3. POJ - 1981 :Circle and Points (圆的扫描线) hihocoder1508

    题意:给定N个点,然后给定一个半径为R的圆,问这个圆最多覆盖多少个点. 思路:在圆弧上求扫描线. 如果N比较小,不难想到N^3的算法. 一般这种覆盖问题你可以假设有两个点在圆的边界上,那么每次产生的圆 ...

  4. C++对象内存模型2 (虚函数,虚指针,虚函数表)(转)

    class A { public: virtual void vfunc1(); virtual void vfunc2(); void func1(); void func2(); virtual ...

  5. 命令式&函数式:把大象关进冰箱里问题

    面向过程:把大象关进冰箱里: 把冰箱门打开=> 大象放冰箱里=> 冰箱门关上 面向对象: 冰箱.开门() 冰箱.放入(大象) 冰箱.关门() 函数式: 关进(冰箱,大象): 关门(放入(开 ...

  6. es6 函数解构的用途

    es6的变量解构赋值很方便,那具体有哪些用途呢? 1.变换变量的值 let n = 10; let m = 20; [n, m] = [m, n] 这样n , m 的值 会互换, 即:n = 20, ...

  7. 洛谷 P2577 [ZJOI2005]午餐 题解

    每日一题 day56 打卡 Analysis 算法:贪心+dp 容易想到贪心:吃饭慢的先打饭节约时间, 所以先将人按吃饭时间从大到小排序. 然后就是dp了: 首先,应该想到f[i][j][k]:前i个 ...

  8. 爬虫 -- UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe6 in position 301: unexpected end of data

     errors参数有3个值:strict,  ignore,  replace html.decode("utf-8"),这种形式有时会报错,那么修改为下面形式,将decode函数 ...

  9. cpu的发现

    system.cpu.discovery 检测到的CPU/CPU内核列表.用于低级发现 返回的cpu从0开始编号,其他关于cpu的监控项就可以使用cpu的id进行单个cpu的资源监控

  10. loj2058 「TJOI / HEOI2016」求和 NTT

    loj2058 「TJOI / HEOI2016」求和 NTT 链接 loj 思路 \[S(i,j)=\frac{1}{j!}\sum\limits_{k=0}^{j}(-1)^{k}C_{j}^{k ...