Web API之Web Components
本文参考《你的前端框架要被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的更多相关文章
- Web APi之Web Host消息处理管道(六)
前言 我们知道Web API本身是无法提供请求-响应的机制,它是通过Web Host以及Self Host的寄宿的宿主方式来提供一个请求-响应的运行环境.二者都是将请求和响应抽象成HttpRespon ...
- 返璞归真 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 上传文件, ...
- http服务 WCF、Web API、Web service、WCF REST之间的区别
http服务 WCF.Web API.Web service.WCF REST之间的区别 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web ...
- 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 ” ...
- Web API和Web Service
首先,Web API是由Web Service演变而来,它们两者关系就是所有Web Service都是API,但并非所有API都是Web Service.其次,两者都有利于信息的传输,但Web API ...
- ASP.NET Web API——选择Web API还是WCF
WCF是.NET平台服务开发的一站式框架,那么为什么还要有ASP.NET Web API呢?简单来说,ASP.NET Web API的设计和构建只考虑了一件事情,那就是HTTP,而WCF的设计主要是考 ...
- Web API学习——Web API 强势入门指南
Web API是一个比较宽泛的概念.这里我们提到Web API特指ASP.NET Web API. 这篇文章中我们主要介绍Web API的主要功能以及与其他同类型框架的对比,最后通过一些相对复杂的实例 ...
- different between web api and web service
https://stackoverflow.com/questions/19336347/what-is-the-difference-between-a-web-api-and-a-web-ser ...
- 使用BaiDu Java Script Web Api 在Web开发中嵌入地图使用步骤
前言 很多做企业网站的朋友,都喜欢有一个关于我们.联系我们的栏目,那么这个栏目放什么内容才能饱满那,只有放个地图才显得有点高大上. 一.产生并复制访问Api的密钥(AK) 1.首先我们需要注册一个百度 ...
- 使用ASP.NET Web API和Web API Client Gen使Angular 2应用程序的开发更加高效
本文介绍“ 为ASP.NET Web API生成TypeScript客户端API ”,重点介绍Angular 2+代码示例和各自的SDLC.如果您正在开发.NET Core Web API后端,则可能 ...
随机推荐
- Game of Cards Gym - 101128G (SG函数)
Problem G: Game of Cards \[ Time Limit: 1 s \quad Memory Limit: 256 MiB \] 题意 题意就是给出\(n\)堆扑克牌,然后给出一个 ...
- Docker网络配置、Docker部署分布式项目
目标 1.Docker网络配置 2.Docker部署SpringCloud项目 Docker网络配置 Docker网络模式介绍 Docker在创建容器时有四种网络模式:bridge/host/cont ...
- UFUN函数 UF_ATTR函数(UF_ATTR_read_value 函数用法)
//此函数的功能是输入tag值,返回与属性标题对应的属性值 static string read_attr(tag_t object_tag) { UF_initialize(); ]="零 ...
- ent 基本使用十七 分页与排序
ent 提供了方便的数据分页以及排序处理 limit 分页 users, err := client.User. Query(). Limit(n). All(ctx) offset 分页 users ...
- cloudevents js sdk 简单试用
cloudevents 目前官方提供了不同语言的sdk,以下是js 的简单学习试用,从目前来说更新不是很好 clone 代码 git clone https://github.com/cloudeve ...
- vuex传递数据的流程
当组件修改数据的时候必须通过store.dispatch来调用actions中的方法,当actions中的方法被触发的时候通过调用commit的方法来触发mutations里面的方法,mutation ...
- PHP常用的魔术方法及规则
1. __construct 具有构造函数的类会在每次创建新对象时先调用此方法;初始化工作执行.2. __desstruct 对象的所有引用都被删除或者当对象被显式销毁时执行.3.__call()在对 ...
- gym102222 G. Factories
gym102222 G. Factories 地址 题目大意: 给一棵n个点的树,选m个点,这m个点只能在叶子节点上,问着m个点中两两之间到达其余各点的距离和最小值是多少题解:任意两点的树上距离和问题 ...
- 传统IT容量估算思路
参考: https://www.cnblogs.com/zhangweizhong/p/5844961.html https://www.cnblogs.com/zhangweizhong/p/577 ...
- TopK问题及优化
腾讯面试题:有100W个战斗力,取前100名的算法. 经典topK问题,结论是:随机选择算法 + 快排思想,通过随机选择算法,找到第k大的数,再进行一次快排中的partition,就能得到TopK的结 ...