Web Components入门教程

移动开发
今天 ,Web 组件已经从本质上改变了HTML。初次接触时,它看起来像一个全新的技术。Web组件最初的目的是使开发人员拥有扩展浏览器标签的能力,可以自由的进行定制组件。

今天 ,Web 组件已经从本质上改变了HTML。初次接触时,它看起来像一个全新的技术。Web组件最初的目的是使开发人员拥有扩展浏览器标签的能力,可以自由的进行定制组件。面对新的技术,你可能会觉得无从下手。那这篇文章将为你揭开Web组件神秘的面纱。如果你已经熟知HTML标签和DOM编程,已经拥有了大量可用 的Web组件,那么你已经是Web组件专家了。

Web组件的现状

随着各式各样的用户需求,浏览器的原生组件已经无法满足需求。Web组件也就变得越来越重要。

我们将以自定义一个传统三方插件为例来介绍Web组件。

首先,需要引用插件的CSS和JavaScript资源:

<link rel="stylesheet" type="text/css" href="my-widget.css" /> 
 
<script src="my-widget.js"></script> 
  • 1.
  • 2.
  • 3.

接下来,我们需要向页面中添加占位符。

<div data-my-widget></div> 
  • 1.

***,我们需要使用脚本来找到并且实例化这个占位符为Web组件。

// 使用 jQuery 初始化组件 
 
$(function() { 
 
    $('[data-my-widget]').myWidget(); 
 
}); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

通过以上是三个基本步骤。已经完成了在页面中添加了自定义插件,但是浏览器无法确定自定义组件的生命周期,如果通过以下方式声明则使自定义组件生命周期变得清晰了。

el.innerHTML = '<div data-my-widget></div>'
  • 1.

因为这不是一个内置的组件,我们现在必须手动实例化新组件,

$(el).find('[data-my-widget]').myWidget(); 
  • 1.

避免这种复杂设置方法的有效方式是完全抽象DOM交互。不过,这个动作也比较复杂,需要创建框架或者库来自定义组件。

面临的问题

组件一旦被声明,占位符已经被替代为原生的HTML标记:

<div data-my-widget> 
 
    <div class="my-widget-foobar"> 
 
        <input type="text" class="my-widget-text" /> 
 
        <button class="my-widget-button">Go</button> 
 
    </div> 
 
</div> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

这样做的弊端是,自定义组件的标记和普通HTML组件的标记混杂在一起,没有清晰的分割和封装。这就不可避免的会出现命名及样式等冲突。

Web组件的产生

随着三方Web组件的发展,它已经成为了Web开发不可或缺的部分:

<!—导入: --> 
 
<link rel="import" href="my-widget.html" /> 
 
<!—使用:--> 
 
<my-widget /> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

在这个实例中,我们通过导入HTML来添加组件并且立即使用。

更重要的是,因为<my-widget />是浏览器原生支持的组件,它直接挂在浏览器的生命周期中,允许我们像添加原生组件一样添加三方组件。

el.innerHTML = '<my-widget />'
 
// 插件当前已经被实例化 
  • 1.
  • 2.
  • 3.

当查看这个组件的HTML 源码,你会发现它仅仅是一个单一的标签。如果启用浏览器Shadow DOM 特性,才可以查看标签内的组件,你将会发现一些有趣的事情,

clip_image001[1]

当我们谈论Web组件时,我们不是在谈论一门新技术。Web组件最初的目的是给我们封装能力,它可以通过自定义组件和Shadow DOM 技术来实现。所以,接下来,我们将着重介绍下这两项技术。介绍以上两个技术之前,我们***先梳理下已知浏览器原生组件。

已知的HTML组件

我们知道组件可以通过HTML标记或JavaScript来实例化:

使用标记实例化:

<input type="text" /> 
document.createElement('input'); 
el.innerHTML = '<input type="text" />'
  • 1.
  • 2.
  • 3.

使用JaveScript实例化:

document.createElement('input')  
 
document.createElement('div'
  • 1.
  • 2.
  • 3.

添加带有属性的HTML标签:

// 创建带有属性的input标签... 
 
el.innerHTML = '<input type="text" value="foobar" />'
 
//这时value属性已经同步 
 
el.querySelector('input').value; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

组件可以响应属性的变化:

// 如果我们更改value 属性值 
input.setAttribute('value', 'Foobar'); 
 
//属性值会立即更改 
input.value === 'Foobar'; // true 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

组件可以有内部隐藏的DOM结构:

<!—使用一个input实现复杂的日历功能--> 
<input type="date" /> 
  
 // 尽管其内部结构比较复杂,但是已经封装成为一个组件 
dateInput.children.length === 0; // true 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

组件可以使用子组件:

<!—可以给组件提供任意个 'option' 标签--> 
 
<select> 
 
<option>1</option> 
 
<option>2</option> 
 
<option>3</option> 
 
</select> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

组件可以为其子组件提供样式:

dialog::backdrop { 
 
    background: rgba(0, 0, 0, 0.5); 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

***,组件可以有内置样式。和自定义插件不同,我们不需要为浏览器的原生控件引用CSS文件。

有了以上的了解,我们已经具备了解Web组件的基础。使用自定义组件和Shadow DOM,我们可以在我们的插件中定义所有这些标准行为。

#p#

自定义组件

注册一个新组件也比较简单:

// 'document.register' 返回一个构造函器 
var MyElement = document.register('my-element'); 
  • 1.
  • 2.

你也许注意到上面的自定义组件名称包含一个连接符。这是为了确保自定义组件名称不和浏览器内置组件不冲突。

现在<my-element />这个组件具备了原生组件的特性,

所以,自定义组件也同样可以进行普通的DOM操作:

document.create('my-element'); 
 
el.innerHTML = '<my-element />'
 
document.create('my-element'); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

构建自定义组件

当前,这个自定义组件仅仅有框架,而没有内容,下面让我们向其中添加一些内容:

//我们将提供'document.register'的第二个参数: 
document.register('my-element', { 
  prototype: Object.create(HTMLElement.prototype, { 
    createdCallback: { 
      value: function() { 
        this.innerHTML = '<h1>ELEMENT CREATED!</h1>'
      } 
    } 
  }) 
}); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

在这个例子中,我们设置自定义组件的prototype,使用Object.create 方法创建一个继承于HTMLElement的对象。在这个方法中修改该组件的属性 innerHTML。

我们定义了createdCallback方法,在每次声明实例时调用。你同样可以有选择性的定义attributeChangedCallback、 enteredViewCallback 和leftViewCallback等方法。

目前为止我们实现了动态修改自定义组件内容的功能,我们仍然需要提供自定义组件的封装方法,用于隐藏其内部组件。

#p#

使用Shadow DOM实现封装

我们需要完善下createdCallback方法。本次,除了修改innerHTML之外,我们添加一些额外的操作:

createdCallback: { 
 
value: function() { 
 
var shadow = this.createShadowRoot(); 
 
shadow.innerHTML = '<h1>SHADOW DOM!</h1>'
 

 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

在这个例子中, 你会注意到‘SHADOW DOM!’,但是查看源码时你会发现只有空白的<my-element /> 标签而已。这里使用创建Shadow Root 方法替代了直接修改页面。

Shadow Root中的任何组件,是肉眼可见的,但是和当前页面的样式和DOM API相隔离。这样就实现了自定义组件是一个独立组件的假象。

添加“轻量级DOM”

目前为止,我们的自定义组件是空标签,但是如果向其中添加内部组件会出现什么现象呢?

我们假设自定义组件包含的节点如下,

<my-element> 
 
这是一个轻量级 DOM。 
 
<i>hello</i> 
 
<i>world</i> 
 
</my-element> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

一旦针对于这个组件的 Shadow Root 被创建,它的子节点不再存在。我们这些隐藏的子节点封装为轻量级DOM节点。

如果禁用了 Shadow DOM,上面这个例子仅仅会显示为:这是一个轻量级 DOM‘hello world’。

当我们在createdCallback方法中设置 Shadow DOM后,我们可以使用新增内容分配轻量级DOM组件到Shadow DOM 中。

createdCallback: { 
 
value: function() { 
 
var shadow = this.createShadowRoot(); 
 
// 子组件'i' 标签现在已经消失了 
 
shadow.innerHTML = 
 
‘轻量级 DOM 中的 "i" 标签为: ' + 
 
'<content select="i" />'
 
//现在,在 Shadow DOM 中只有 'i' 标签是可以见的。 
 

 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

封装样式

Shadow DOM 最重要的作用是创建了和当前页面隔离的Web组件,使Web组件不受当前页面样式和JaveScript脚本的影响。

createdCallback: { 
 
value: function() { 
 
var shadow = this.createShadowRoot(); 
 
shadow.innerHTML = 
 
"<style>span { color: green }</style>" + 
 
"<span>I'm green</span>"
 

 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

反之,在 Shadow DOM 中定义的样式也不会影响之外的标签样式。

<my-element /> 
 
<span>I'm not green</span> 
  • 1.
  • 2.
  • 3.

#p#

揭露钩子的秘密

当隐藏自定义组件内部标记,有时也需要在当前页面对组件中的内部特定组件进行样式设置。

例如,如果我们自定义一个日历插件,在不允许用户控制整个插件的情况下,允许最终用户去定义按钮的样式。

这是其中的部分特性和伪组件:

createdCallback: { 
 
value: function() { 
 
var shadow = this.createShadowRoot(); 
 
shadow.innerHTML = 'Hello <em part="world">World</em>'
 

 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

这是在当前页面设置自定义组件内部组件样式的方法:

my-element::part(world) { 
 
color: green; 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

这部分内容介绍了封装web组件的基本方式。Shadow DOM 是我们可以任意修改Web组件中的标签。在例子中,我们设置了“World”的样式,但是使用者却无法判断它是<em>标签。

在你尝试自定义Web组件之前,需要确保浏览器的相关特性已经打开。如果使用 Chrome,在 Chrome 中打开chrome://flags ,并且开启“experimental Web Platform features”。

clip_image003[1]

这仅仅是个开始

所有本文中介绍的内容,都是模拟一些简单的浏览器标准行为。我们已经习惯于和原生的浏览器组件进行交互,因此自定义组件的步骤并不是想象中的那个难。Web组件最终提供我们一种实现简单、一致、可复用、封装和组合部件的方法,这是一个有意义的开始。

责任编辑:徐川 来源: cnblogs
相关推荐

2009-07-08 15:12:48

Java Servle

2010-08-03 13:06:15

Flex Builde

2013-08-29 14:12:52

Storm分布式实时计算

2024-02-26 09:13:35

WebComponents开源项目

2011-09-02 10:59:10

jQuery Mobi

2010-07-20 16:19:54

Perl

2010-06-18 16:56:50

UML建模语言

2018-03-22 14:59:13

Docker入门容器

2013-06-24 13:38:34

HTML5 DataList

2010-06-13 09:45:35

Widget开发

2010-05-21 12:50:45

Subversion快

2012-05-10 08:29:46

XcodeiOSPhoneGap

2011-07-21 10:29:18

iPhone 开发

2010-08-03 14:37:30

Flex入门教程

2013-08-29 14:28:09

StormHadoop

2010-07-27 15:53:15

2024-11-12 15:46:37

2022-06-16 07:31:41

Web组件封装HTML 标签

2022-09-19 16:08:31

Dapr发布订阅

2010-07-23 14:41:30

Perl Hash
点赞
收藏

51CTO技术栈公众号