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后端,则可能 ...
随机推荐
- 第六篇 -- LINQ to XML
一.LINQ to XML常用成员 LINQ to XML的成员, 属性列表: 属性 说明 Document 获取此 XObject 的 XDocument EmptySequence 获取空的元 ...
- Elasticsearch 索引文档如何使用自动生成 Id?
一个文档的 _index . _type 和 _id 唯一标识一个文档. 我们可以提供自定义的 _id 值,或者让 index API 自动生成. 如果你的数据没有自然的 ID, Elasticsea ...
- (待做例子)问题描述: el-tab 下有2个路由,其中第1个路由设置了 beforeRouteLeave,点击 el-tab 第2个tab时,样式直接跟过去了(预期结果是:样式不到第二个tab上,beforeRouteLeave允许跳转后才到第二个tab上)
解决: 经过以上分析,强制赋值应该在前次赋值而且DOM已经刷新完毕之后进行.可以使用$nextTick,以下是代码: handleTabClick (tab) { let name = this.fi ...
- Ofbiz项目学习——阶段性小结——视图
一.简要介绍 1.按照SQL的视图概念:在 SQL 中,视图是基于 SQL 语句的结果集的可视化的表.视图包含行和列,就像一个真实的表.视图中的字段就是来自一个或多个数据库中的真实的表中的字段. 2. ...
- $.extend(obj1,obj2...,objN)小结 扩展obj1属性,有返回值,返回第一个被扩展后的对象
<script> var obj1 = { name:'liu', age:'24' } var obj2 = { name:'jinyu', sex:'m ...
- luoguP4721 【模板】分治 FFT
P4721 [模板]分治 FFT 链接 luogu 题目描述 给定长度为 \(n-1\) 的数组 \(g[1],g[2],..,g[n-1]\),求 \(f[0],f[1],..,f[n-1]\),其 ...
- P4141 消失之物
目录 链接 思路 代码 链接 P4141 消失之物 思路 f[N];//表示删掉物品后能出现容积为i的方案数 a[N];//单纯0-1背包的方案数asd 那么就先求出a[i]来,然后转移就是 if(j ...
- 埃氏筛优化(速度堪比欧拉筛) + 洛谷 P3383 线性筛素数 题解
我们一般写的埃氏筛消耗的时间都是欧拉筛的三倍,但是欧拉筛并不好想(对于我这种蒟蒻) 虽然 -- 我 -- 也可以背过模板,但是写个不会的欧拉筛不如写个简单易懂的埃氏筛 于是就有了优化 这个优化还是比较 ...
- 洛谷 CF1153B Serval and Toy Bricks
目录 题目 思路 \(Code\) 题目 CF1153B Serval and Toy Bricks 思路 自己也很懵的一道题(不知道自己怎么就对了)...只要对于所给的俯视图上值为\(1\)的位置输 ...
- SQL基础-存储过程&触发器
一.存储过程 1.存储过程简介 存储过程: 一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,然后通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它. 存储过程的创建: ...