文档章节

Web Components简述

熊建刚
 熊建刚
发布于 2017/05/16 10:25
字数 3128
阅读 2.3K
收藏 69

前言

在开启本篇的阅读之前,先问一个问题,组件是什么?

组件,是数据和方法的一个封装,其定义了一个可重用的软件元素的功能,展示和使用,通常表现为一个或一组可重用的元素。

组件的特性是什么?通常可以总结为以下几点:

  1. 可拓展性:既然组件是针对某一特定功能或需求开发的,那它就必须易于开发和拓展;
  2. 封装性:组件作为一个独立整体供使用,应该是对内修改,对外封闭,只供使用,而不对使用环境产生副作用;
  3. 易用性:组件的目的是产生可重用的独立部件,那就必须提供一种简单快捷的方式供使用。

组件化,给前端开发带来了极大的效率提升,是近几年以来web开发发展的趋势,各种组件化的用户界面库,框架也层出不穷,如,React,Vue,Ionic等,这些框架关于组件化都有各自的实现,推崇理念,与编程规范,各大框架的支持者之间的争论也是向来不断,而若想在不同框架间切换,成本还是挺高的,因为毕竟谁都希望自己能占主流,占据绝对优势地位,就像当前IE与网景浏览器之争,延续到现在,各类浏览器标准兼容差异万千,近年来w3c不断在为web标准规范做努力,Web Components就是推出的关于组件化的一个标准,希望它能将组件化更好的带进web开发,同时尽量保证标准规范,开发者可以更好的关注于开发,而不是框架选择与争论之上。

Web Components特征

Web Components将一系列特性加入HTML和DOM规范,使得开发者可以自由创建在web应用或文档可重用的元素或部件,其由四部分组成:

  • 自定义元素(Custom Elements):定义新HTML元素的一系列API;
  • 影子DOM(Shadow DOM):组合对DOM和样式的封装;
  • HTML导入(HTML Imports):定义在文档中导入其他HTML文档的方式;
  • HTML模板(HTML Templates):HTML内的DOM模板,在<template>元素内声明。

自定义元素(Custom Elements)

自定义元素支持开发者定义一类新HTML元素,声明其行为和样式,自定义元素分两类:

  • 自定义标签元素(Autonomous custom elements):完全独立于原始HTML元素标签的新标签元素,其所有行为需要开发者定义;
  • 自定义内置元素(Customized built-in):基于HTML原始元素标签的自定义元素,以便于使用原始元素的特性,开发者只需要定义拓展行为;

支持创建自定义元素,Web Components比较好的实现了组件开发的可拓展性。

创建自定义标签元素

为了创建一个自定义标签元素,我们需要继承HTMLELement类, 如在很多页面我们经常会有一键回到页面顶部功能,我们创建一个返回顶部的组件:


	class GoTop extends HTMLElement {
		constructor() {
    		super();
  		}
	}
	customElements.define('go-top', GoTop);

在需要使用该组件的页面只需像使用正常HTML元素一样:


	<go-top>Top</go-top>

当然,该元素的一切样式,行为,事件监听,默认行为均需要开发者自行定义,无法期待它有像<button>一样的默认行为详细参考创建自定义标签元素

创建自定义内置元素

很多时候我们并不需要完全创建一个新元素,而只是需要在某些内置元素基础上进行拓展,创建自定义内置元素,需要继承该类元素类,如HTMLButtonElementHTMLDivElement


	class MenuButton extends HTMLButtonElement {
		...
	}
	customElements.define('menu-button', MenuButton);

使用也很简单,和内置元素一样的语法;不同的是,在需要使用自定义内置元素时,为内置元素添加is特性,该特性值对应创建的自定义内置元素名称:


	<button is="menu-button">menu</button>

该元素默认行为继承自<button>元素,但是我们可以为其设置拓展功能或性质。

对比

通过上面实例可知,自定义标签元素与内置元素主要表现在两点不同:

  • 标签:自定义标签元素是完全独立的一个新元素,新标签,而自定义内置标签,使用的依然是已有内置标签;
  • 行为与样式:自定义内置元素,继承内置元素的默认行为,样式及语义,可以进行拓展,而自定义标签元素,完全需要开发者定义相关声明。

影子DOM(Shadow DOM)

DOM,即文档对象模型,是HTML文档的一个结构表示,以树形结构表示一个文档,文档中元素间关系按照父子,兄弟关系排列;DOM规范提供一系列API支持我们操作文档节点,即通常所说的DOM API。

前面提到Web Components指封装DOM和样式,以组件的形式在文档中使用,而不同于JavaScript中函数会形成一个单独作用域,文档DOM树的层次结构中是不存在局部作用域概念的,也就是说文档内所有定义的样式都对整个文档产生影响,文档中的样式也会影响组件内的声明样式,而不限定于元素所处位置,这样显然极大阻碍了组件的独立性和可重用性,是必须要解决的问题,不过不用担心,这都已经解决了,解决方案就是下文介绍的attachShadow()方法。

影子DOM API提供了attachShadow()方法,创建一个影子DOM,支持将封装的内容或组件作为一个独立DOM子树附加进一个HTML文档,组件内与外部隔离,样式互不影响,这也印证了组件开发的封装性需求。

创建

要创建一个影子DOM,很简单,使用attachShadow()方法即可,而需要注意的是所有影子DOM必须和一个文档中存在的元素(HTML内置元素或自定义元素)绑定,才能使用:


	var frag = document.createElement('div');
	var shadowRoot = frag.attachShadow({mode: 'open'});
	shadowRoot.innerHTML = '<p>Shadow DOM Content</p>';

影子树(shadow tree)与影子主体(shadow host)

上文使用attachShadow()方法创建的元素就是一个影子DOM,而其子内容就构成一棵影子树(shadow tree),而和影子DOM绑定,也就是包含该树的文档内元素通常称为影子主体(shadow host)。

槽位(slot)

如上,当一个元素(即影子主体)内存在影子DOM,浏览器默认只会渲染该影子DOM的影子树,而不渲染影子主体的其他子内容,如,现有某元素<div class="menus">,在文档中使用如下:


	<div class="menus">
		<h2>Menus</h2>
	</div>

给该元素绑定影子DOM:


	var menus = document.querySelector('.menus');
	var shadowRoot = menus.attachShadow({mode: 'open'});
	shadowRoot.innerHTML = '<ul>\
		<li>Home</li>\
		<li>About</li>\
	</ul>';

其影子树内容为:


	<ul>
		<li>Home</li>
		<li>About</li>
	</ul>

最后渲染结果如下:


	<div class="menus">
		<ul>
			<li>Home</li>
			<li>About</li>
		</ul>
	</div>

你好发现影子主体原本的子元素内容没有被渲染,那么是不是没办法了?当然不是,如果要保存子内容,需要使用<slot>槽位元素,相当于做一个占位符,只需要把前文影子主体内容修改为如下:


	<div class="menus">
		<h2>Menus</h2>
		<slot></slot>
	</div>

渲染结果如下, 一切符合需求:


	<div class="menus">
		<h2>Menus</h2>
		<ul>
			<li>Home</li>
			<li>About</li>
		</ul>
	</div>
命名槽(named slots)

上文显示的是只有一个槽位的实例,假如需要有多个分组怎么办呢?Web Components也有解决方案,那就是使用命名槽,即给槽位添加name属性,依然使用如上实例,修改影子主体内容:


	<div class="menus">
		<slot></slot>
		<slot name="top"></slot>
		<slot name="right"></slot>
	</div>

假如影子树内容如下:

	
	<h2>Menus</h2>
	<ul slot="top">
		<li>Home</li>
		<li>About</li>
	</ul>
	<ul slot="right">
		<li>Home</li>
		<li>Top</li>
	</ul>

渲染结果如下:


	<div class="menus">
		<h2>Menus</h2>
		<ul>
			<li>Home</li>
			<li>About</li>
		</ul>
		<ul>
		<li>Home</li>
		<li>Top</li>
	</ul>
	</div>

如上,可以发现拥有name属性的槽位由对应slot属性值相同的影子子树替换,而剩下的内容默认替换空名槽位,若不存在空名槽位,则剩余内容将被抛弃。

样式

前文已经提到,Web Components定义的组件内的样式与外部环境的样式是互不影响的,那么如何为组件设置样式呢,依然使用<style>标签:


	<head>
		<style>
			.top {margin-top: 30px;}
		</style>
	</head>
	...

	<div class="top">
		...
	</div>
	...

	<div class="menus">
		#shadow-root
		<style>
			.top {margin-top: 10px;}
		</style>
		<div class="top">
			...
		</div>
	</div>

如上实例,在组件内部top类元素margin-top值为10px,而外部top类元素margin-top值为30px,两者是独立的。

渲染

关于影子DOM树的渲染,其方式与web文档DOM树的渲染方式并无区别,均由浏览器渲染引擎进行渲染,需要注意的是,影子树的DOM渲染过程和文档DOM树的渲染是独立分别进行的。

HTML引入(HTML Imports)

如何在HTML文档中引入另一个web文档或web组件呢?像JSP或PHP语言都对HTML语法进行了拓展,我们可以使用诸如<include>标签直接引入另一个文档,然而在这之前,原生HTML规范并不支持直接引入另一文档,通常都得通过ajax请求另一文档内容,然后通过JavaScript使用DOM API将内容插入,对于组件化开发和使用,这样显然不是我们期望的结果,这与组件的易用性是背离的,所以,HTML imports定义了如何在文档内引入和重用另一文档。

在文档内直接引入外链资源的文档或web组件,语法如下,使用<link>标签:


	<link rel="import" href="components.html">

假如在components.html中定义了got-top自定义元素,则在本文档内可以直接使用:


	<go-top>GoTop</go-top>

如上,仅仅将<link>标签的rel属性设置成import即可,另外值得注意的是:为了避免重复执行引入文档内的脚本,对于已加载文档,import方式将跳过其加载和执行过程。

HTML模板(HTML Templates)

为了更友好的处理组件模板,Web Components规范,支持<template>模板标签,HTML模板定义了使用<template>标签声明可以通过脚本操作插入文档的HTML模板片段:


	<template id="menusTemplate">
		<ul>
			<li>Home</li>
			<li>About</li>
		</ul>
	</template>

使用脚本操作,该元素content属性可访问模板内容:


	var menusTemplate = document.querySelector('#menusTemplate');
	var frag = document.importNode(menusTemplate.content, true);
	document.querySelector('.menus').appendChild(frag);

template标签

<template>标签本质上与其他HTML内置标签一样,可以使用DOM API进行操作,但是需要明白,在将模板激活(生成DOM或插入文档)前:

  1. <template>标签内的内容不会被渲染;
  2. 标签内的图片,等媒体资源不会被加载;
  3. 标签不会出现在DOM树,审查元素看不到;

关于兼容性

对于Web Components规范的兼容性,目前还是需要使用webcomponentsjs polyfills的方式支持开发,总的来说,目前Safari 10, Google Chrome (53)兼容的更好;虽然兼容性并不好,还在推进过程中,但是对其进行学习还是很有必要的。

类库

推荐几个常见的Web组件类库:

  • Polymer: Google推出的web组件库,支持数据的单向和双向绑定,兼容性较好,跨浏览器性能也较好;
  • X-Tag: 微软推出的开源库,支持Web Components规范,兼容Web ComponentsAPI;
  • Slim.js:轻量级的web组件库,专注于帮助开发者更好的编写原生web组件,而不依赖于其他框架,但是也提供了良好的拓展性,开发者可以自由拓展。

参考

  1. Component
  2. Web Components Specifications
  3. Web Components WiKi
  4. Web Components MDN

欢迎访问我的个人博客 - 熊建刚的博客

© 著作权归作者所有

熊建刚
粉丝 64
博文 21
码字总数 88314
作品 0
海淀
程序员
私信 提问
加载中

评论(8)

熊建刚
熊建刚 博主

引用来自“BruceWan”的评论

报错

回复@BruceWan : 是啊,网站前端代码报错了
熊建刚
熊建刚 博主

引用来自“kernel64”的评论

加载不出来,评论也失败

回复@kernel64 : 我看所有博客都加载不了,他们前端代码报错了,欢迎访问我的个人博客http://blog.codingplayboy.com/2017/05/15/web-components/
开源机器猫
开源机器猫
报错
kernel64
kernel64
加载不出来,评论也失败
kernel64
kernel64
加载不出来
kernel64
kernel64
加载不出来
kernel64
kernel64
加载不出来
kernel64
kernel64
加载不出来
yii2搭建完美后台并实现rbac权限控制

作者:白狼 出处:http://www.manks.top/article/yii2framerbac_template 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律...

白狼栈
2016/03/30
1K
2
运维人员笔试题[李宝财]

1:请简述lvs的三种工作方式,以及其特点和区别? 2:请描述一下lvs的端口亲缘性,以及可持久性连接? 3:请简述一下linux下常用入侵检测和审计系统(audit属于哪个原理上简述一下)? 4:请谈一下目前常...

多厘
2015/04/02
4
0
使用CLI 3 创建发布Web Components

本文翻译自:codementor 翻译不当之处,欢迎指正交流 Web Components是web平台的未来吗?关于这一问题支持和反对的观点有很多。事实上浏览器对Web Components的支持正在逐渐形成,并有越来越...

RachelehcaR
2018/09/13
0
0
在angular项目中使用web-component ----How to use Web Components with Angular

原文: https://medium.com/@jorgecasar/how-to-use-web-components-with-angular-41412f0bced8 ------------------------------------------------------------- I already told you about ......

osc_qef6nxve
2019/10/17
2
0
Taro Next H5 跨框架组件库实践

Taro 是一款多端开发框架。开发者只需编写一份代码,即可生成各小程序端、H5 以及 React Native 的应用。 Taro Next 近期已发布 beta 版本,全面完善对小程序以及 H5 的支持,欢迎体验! 背景...

cjj
04/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何用数据结构解释计算机系统 常用数据结构

详细:https://www.cnblogs.com/morui/p/10726864.html 数据结构(计算机存储、组织数据方式) 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元...

osc_rnx2cje5
3分钟前
7
0
黑客你咋这么牛掰,不屑用鼠标?

如需快速跳转,请戳以下蓝色字条 01 前情提要 02 有苦说得出 1、黑客开发绝大多数工具是没有图形化界面的 2、命令行更有助于批量操作 3、图形化界面消耗系统资源量大,增加计算机性能负担 4、...

osc_m6gaz63w
4分钟前
7
0
计算机网络TCP/IP模型复习笔记(随时补充)

看到一篇大佬的博客,刚好前段时间也有简单了解了一点计算机网络的TCP/IP,就顺便总结一下。 大佬文章链接: https://blog.csdn.net/ThinkWon/article/details/104903925 计算机网络的自己理...

osc_boqyoaed
6分钟前
7
0
IDEA使用技巧-->查看类的继承关系图

IDEA使用技巧-->查看类的继承关系图 简单实用(很实用) 转自 ☞https://www.cnblogs.com/deng-cc/p/6927447.html 最近忙,有用的直接拿来给大家分享,但凡分享的都是我亲测有效的!...

宇宝
7分钟前
13
0
浏览器同源政策及其规避方法

自己以思维导图的形式梳理了一遍 浏览器同源政策及其规避方法

酒窝yun过去了
7分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部