Web Components
Web Components是不是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>
接下来,我们需要向页面中添加占位符。
<div data-my-widget></div>
最后,我们需要使用脚本来找到并且实例化这个占位符为Web组件。

// 使用 jQuery 初始化组件
$(function() {
$('[data-my-widget]').myWidget();
});

通过以上是三个基本步骤。已经完成了在页面中添加了自定义插件,但是浏览器无法确定自定义组件的生命周期,如果通过以下方式声明则使自定义组件生命周期变得清晰了。
el.innerHTML = '<div data-my-widget></div>';
因为这不是一个内置的组件,我们现在必须手动实例化新组件,
$(el).find('[data-my-widget]').myWidget();
避免这种复杂设置方法的有效方式是完全抽象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>

这样做的弊端是,自定义组件的标记和普通HTML组件的标记混杂在一起,没有清晰的分割和封装。这就不可避免的会出现命名及样式等冲突。
Web组件的产生
随着三方Web组件的发展,它已经成为了Web开发不可或缺的部分:

<!—导入: --> <link rel="import" href="my-widget.html" /> <!—使用:--> <my-widget />

在这个实例中,我们通过导入HTML来添加组件并且立即使用。
更重要的是,因为<my-widget />是浏览器原生支持的组件,它直接挂在浏览器的生命周期中,允许我们像添加原生组件一样添加三方组件。
el.innerHTML = '<my-widget />'; // 插件当前已经被实例化
当查看这个组件的HTML 源码,你会发现它仅仅是一个单一的标签。如果启用浏览器Shadow DOM 特性,才可以查看标签内的组件,你将会发现一些有趣的事情,

当我们谈论Web组件时,我们不是在谈论一门新技术。Web组件最初的目的是给我们封装能力,它可以通过自定义组件和Shadow DOM 技术来实现。所以,接下来,我们将着重介绍下这两项技术。介绍以上两个技术之前,我们最好先梳理下已知浏览器原生组件。
已知的HTML组件
我们知道组件可以通过HTML标记或JavaScript来实例化:
使用标记实例化:
<input type="text" />
document.createElement('input');
el.innerHTML = '<input type="text" />';
使用JaveScript实例化:
document.createElement('input')
document.createElement('div')
添加带有属性的HTML标签:

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

组件可以响应属性的变化:
// 如果我们更改value 属性值
input.setAttribute('value', 'Foobar'); //属性值会立即更改
input.value === 'Foobar'; // true
组件可以有内部隐藏的DOM结构:
<!—使用一个input实现复杂的日历功能-->
<input type="date" /> // 尽管其内部结构比较复杂,但是已经封装成为一个组件
dateInput.children.length === 0; // true
组件可以使用子组件:

<!—可以给组件提供任意个 'option' 标签--> <select> <option>1</option> <option>2</option> <option>3</option> </select>

组件可以为其子组件提供样式:
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
}
最后,组件可以有内置样式。和自定义插件不同,我们不需要为浏览器的原生控件引用CSS文件。
有了以上的了解,我们已经具备了解Web组件的基础。使用自定义组件和Shadow DOM,我们可以在我们的插件中定义所有这些标准行为。
自定义组件
注册一个新组件也比较简单:
var MyElement = document.register('my-element');
// 'document.register' 返回一个构造函器
你也许注意到上面的自定义组件名称包含一个连接符。这是为了确保自定义组件名称不和浏览器内置组件不冲突。
现在<my-element />这个组件具备了原生组件的特性,
所以,自定义组件也同样可以进行普通的DOM操作:
document.create('my-element');
el.innerHTML = '<my-element />';
document.create('my-element');
构建自定义组件
当前,这个自定义组件仅仅有框架,而没有内容,下面让我们向其中添加一些内容:

//我们将提供'document.register'的第二个参数:
document.register('my-element', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
this.innerHTML = '<h1>ELEMENT CREATED!</h1>';
}
}
})
});

在这个例子中,我们设置自定义组件的prototype,使用Object.create 方法创建一个继承于HTMLElement的对象。在这个方法中修改该组件的属性 innerHTML。
我们定义了createdCallback方法,在每次声明实例时调用。你同样可以有选择性的定义attributeChangedCallback、 enteredViewCallback 和leftViewCallback等方法。
目前为止我们实现了动态修改自定义组件内容的功能,我们仍然需要提供自定义组件的封装方法,用于隐藏其内部组件。
使用Shadow DOM实现封装
我们需要完善下createdCallback方法。本次,除了修改innerHTML之外,我们添加一些额外的操作:

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

在这个例子中, 你会注意到‘SHADOW DOM!’,但是查看源码时你会发现只有空白的<my-element /> 标签而已。这里使用创建Shadow Root 方法替代了直接修改页面。
Shadow Root中的任何组件,是肉眼可见的,但是和当前页面的样式和DOM API相隔离。这样就实现了自定义组件是一个独立组件的假象。
添加“轻量级DOM”
目前为止,我们的自定义组件是空标签,但是如果向其中添加内部组件会出现什么现象呢?
我们假设自定义组件包含的节点如下,

<my-element> 这是一个轻量级 DOM。 <i>hello</i> <i>world</i> </my-element>

一旦针对于这个组件的 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' 标签是可以见的。
}
}

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

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

反之,在 Shadow DOM 中定义的样式也不会影响之外的标签样式。
<my-element /> <span>I'm not green</span>
揭露钩子的秘密
当隐藏自定义组件内部标记,有时也需要在当前页面对组件中的内部特定组件进行样式设置。
例如,如果我们自定义一个日历插件,在不允许用户控制整个插件的情况下,允许最终用户去定义按钮的样式。
这是其中的部分特性和伪组件:

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

这是在当前页面设置自定义组件内部组件样式的方法:
my-element::part(world) {
color: green;
}
这部分内容介绍了封装web组件的基本方式。Shadow DOM 是我们可以任意修改Web组件中的标签。在例子中,我们设置了“World”的样式,但是使用者却无法判断它是<em>标签。
在你尝试自定义Web组件之前,需要确保浏览器的相关特性已经打开。如果使用 Chrome,在 Chrome 中打开chrome://flags ,并且开启“experimental Web Platform features”。

这仅仅是个开始
所有本文中介绍的内容,都是模拟一些简单的浏览器标准行为。我们已经习惯于和原生的浏览器组件进行交互,因此自定义组件的步骤并不是想象中的那个难。Web组件最终提供我们一种实现简单、一致、可复用、封装和组合部件的方法,这是一个有意义的开始。
Knockout与Require框架同时使用时的visible绑定的问题,造成的影响,以及解决的方法。
Knockout 可以将 visible 绑定到DOM 元素上,使得该元素的hidden 或visible 状态取决于绑定的值。
查看以下knockout的描述,http://knockoutjs.com/documentation/visible-binding.html
When the parameter resolves to a false-like value (e.g., the boolean value false, or the numeric value 0, or null, or undefined), <br>the binding sets yourElement.style.display to none, causing it to be hidden. This takes priority over any display style you’ve defined using CSS。当这个参数是一个假值时(举例来说,布尔值的false , 数值0,或者null,或者undefined),绑定时候设置你的元素的style.display是none,从而使之隐藏起来。这个优先级要高于CSS中定义的.When the parameter resolves to a true-like value (e.g., the boolean value true, or a non-null object or array), the binding removes the yourElement.style.display value, <br>causing it to become visible。当这个参数是一个真值时(举例来说,布尔值是true,或者非空对象与数组),绑定时候移除你display的值,从来显示出来 |
当单独使用knockout框架时,这个visible绑定运行起来相当好,但是,当Knockout和Require两个框架同时使用时,就出问题了。
先看一个例子,这个例子里有有两个div,第一次加载页面时显示div1,隐藏div2,当按下一步按钮时,隐藏div1,显示div2,最后点返回按钮时,显示div1,隐藏div2。
ko_visible.htm代码,
<html><head> <script src="../lib/require/require.js" data-main="ko_visible"></script></head><body > <div id="div1" data-bind="visible: showVisibleDiv1"> <p>First name: <span ></span></p> <p>Last name: <span ></span></p> <p>Full name: <span ></span></p> <input type="text" id="inputAddress" /> * <input type="text" id="inputMoney" /> * <input type="button" id="btnSave" value="下一步" data-bind="click: SaveClick" /> </div> <div id="div2" data-bind="visible: showVisibleDiv2"> <table> <tr> <td>标题</td><td>内容</td> </tr> <tr> <td></td><td></td> </tr> </table> <input type="button" id="btnNext" value="返回" data-bind="click: NextClick" /> </div></body></html> |
ko_visible.js
require.config({ paths: { "knockout": "../lib/knockout/knockout-2.3.0", "jquery": "../lib/jquery/jquery-1.9.1.min" }});require(['jquery', 'knockout'], function ($, ko) { //数据绑定 $(document).ready(function () { var viewModel = { showVisibleDiv1: ko.observable(true), showVisibleDiv2: ko.observable(false), SaveClick: function () { viewModel.showVisibleDiv1(false); viewModel.showVisibleDiv2(true); }, NextClick: function () { viewModel.showVisibleDiv1(true); viewModel.showVisibleDiv2(false); } }; ko.applyBindings(viewModel); });}); |
当运行此页面时,效果如下图,div1,div2同时显示,然后, div2因为visible=false的缘故,又迅速消失。

当页面中存在多个步骤的div,想一步步执行并控制某些div显示时, 这个效果是让人不能接受的.
当页面初始加载时,因为Require的延迟加载特性(也就是不先加载js,而是先加载元素,异步加载js),当knockout代码还没有执行时,元素是没有被隐藏的,这一点是致命的。
所以,修改的方法有两个:
1.不使用Require框架。
例子如下:
ko_visible1.htm
<html><head> <script type="text/javascript" src="../lib/knockout/knockout-2.3.0.js"></script> <script type="text/javascript" src="../lib/jquery/jquery-1.9.1.min.js"></script> <script type="text/javascript" src="ko_visible1.js"></script></head><body > <div id="div1" data-bind="visible: showVisibleDiv1"> <p>First name: <span ></span></p> <p>Last name: <span ></span></p> <p>Full name: <span ></span></p> <input type="text" id="inputAddress" /> * <input type="text" id="inputMoney" /> * <input type="button" id="btnSave" value="下一步" data-bind="click: SaveClick" /> </div> <div id="div2" data-bind="visible: showVisibleDiv2"> <table> <tr> <td>标题</td><td>内容</td> </tr> <tr> <td></td><td></td> </tr> </table> <input type="button" id="btnNext" value="返回" data-bind="click: NextClick" /> </div></body></html> |
ko_visible1.js
//数据绑定$(document).ready(function () { var viewModel = { showVisibleDiv1: ko.observable(true), showVisibleDiv2: ko.observable(false), SaveClick: function () { viewModel.showVisibleDiv1(false); viewModel.showVisibleDiv2(true); }, NextClick: function () { viewModel.showVisibleDiv1(true); viewModel.showVisibleDiv2(false); } }; ko.applyBindings(viewModel); }); |
2.如果必须使用Require框架,那在这种场合,建议不要使用visible,还是使用css的样式控制。
例子如下:
ko_display.htm
<html><head> <script src="../lib/require/require.js" data-main="ko_display"></script></head><body > <div id="div1" data-bind="visible: showVisibleDiv1"> <p>First name: <span ></span></p> <p>Last name: <span ></span></p> <p>Full name: <span ></span></p> <input type="text" id="inputAddress" /> * <input type="text" id="inputMoney" /> * <input type="button" id="btnSave" value="下一步" data-bind="click: SaveClick" /> </div> <div id="div2" style="display:none"> <table> <tr> <td>标题</td><td>内容</td> </tr> <tr> <td></td><td></td> </tr> </table> <input type="button" id="btnNext" value="返回" data-bind="click: NextClick" /> </div></body></html> |
ko_display.js
require.config({ paths: { "knockout": "../lib/knockout/knockout-2.3.0", "jquery": "../lib/jquery/jquery-1.9.1.min" }});require(['jquery', 'knockout'], function ($, ko) { //数据绑定 $(document).ready(function () { var viewModel = { showVisibleDiv1: ko.observable(true), SaveClick: function () { viewModel.showVisibleDiv1(false); $("#div2").show(); }, NextClick: function () { viewModel.showVisibleDiv1(true); $("#div2").hide(); } }; ko.applyBindings(viewModel); });}); |
以上是本人在项目中遇到的问题总结,如有谬误之处,还请大家指正!
Web Components的更多相关文章
- 【shadow dom入UI】web components思想如何应用于实际项目
回顾 经过昨天的优化处理([前端优化之拆分CSS]前端三剑客的分分合合),我们在UI一块做了几个关键动作: ① CSS入UI ② CSS作为组件的一个节点而存在,并且会被“格式化”,即选择器带id前缀 ...
- Web Components初探
本文来自 mweb.baidu.com 做最好的无线WEB研发团队 是随着 Web 应用不断丰富,过度分离的设计也会带来可重用性上的问题.于是各家显神通,各种 UI 组件工具库层出不穷,煞有八仙过海之 ...
- Web Components之Custom Elements
什么是Web Component? Web Components 包含了多种不同的技术.你可以把Web Components当做是用一系列的Web技术创建的.可重用的用户界面组件的统称.Web Com ...
- 【转】Facebook React 和 Web Components(Polymer)对比优势和劣势
原文转自:http://segmentfault.com/blog/nightire/1190000000753400 译者前言 这是一篇来自 StackOverflow 的问答,提问的人认为 Rea ...
- 可选的Web Components类库
首先需要说明的是这不是一篇 Web Components 的科普文章,如果对此了解不多推荐先读<A Guide to Web Components>. 有句古话-“授人以鱼,不如授人以渔” ...
- Polymer——Web Components的未来
什么是polymer? polymer由谷歌的Palm webOS团队打造,并在2013 Google I/O大会上推出,旨在实现Web Components,用最少的代码,解除框架间的限制的UI 框 ...
- The state of Web Components
Web Components have been on developers’ radars for quite some time now. They were first introduced b ...
- 前端应该知道的Web Components
前端组件化的痛点 在前端组件化横行的今天,确实极大的提升了开发效率.不过有一个问题不得不被重视,拟引入的这些html.css.js代码有可能对你的其他代码造成影响. 虽然我们可以通过命名空间.闭包等一 ...
- 一个使用 Web Components 的音乐播放器: MelodyPlayer
先上效果预览: Web Components 首先,什么是 Web Components ? MDN 给出的定义是: Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的 ...
随机推荐
- 用java字节码解释i++和++i(转)
这几天抽着一些时间,把Java的class文件结构研究了一下,再后来就想起了这个令人厌烦的问题,想从字节码指令的角度看看,java到底是怎么处理这个的 先看一段java代码 package bishi ...
- PHP图片等比缩放,并添加Logo水印特定代码和盯
<? php //PHP图片等比缩放,并添加Logo水印 --->百度 "美日汇" /** * 等比缩放函数(以保存的方式实现) * @param string $pi ...
- Net下无敌的ORM
Dapper ORM 用法—Net下无敌的ORM http://www.renfb.com/blog/2011/Article/335 假如你喜欢原生的Sql语句,又喜欢ORM的简单,那你一定会喜欢上 ...
- SharePoint 创建一个简单的Web Part 部分
SharePoint 创建一个简单的Web Part 部分 标准Web零件有时会很强大,运行多个功能的能力. 本文介绍了如何使用Visual Studio 创建一个简单的Web部分. 1. 打开VS, ...
- tiny210——uboot移植Makefile文章分析
这东西已经写,我们没有时间发布,如今,终于有时间稍微长送记录汇总uboot学习过程.具体了.以后忘了也能够再温习回来嘛有些特殊字符显示得乱掉了 Makefile追踪技巧: 技巧1:能够先从编译目标開始 ...
- ASP.NET vNext (一)- 基本概念和环境配置
ASP.NET vNext (一)- 基本概念和环境配置 转发:微软MVP 卢建晖 的文章,希望对大家有帮助. 编者语:时代在变,在csdn开博一年就发了那么的两篇文章,无论是什么原因都觉得有愧了.但 ...
- hdu 4932 Miaomiao's Geometry(暴力)
题目链接:hdu 4932 Miaomiao's Geometry 题目大意:在x坐标上又若干个点,如今要用若干条相等长度的线段覆盖这些点,若一个点被一条线段覆盖,则必须在这条线的左端点或者是右端点, ...
- Cassandra C++/NodeJs开发环境
工作的需要,开始更多地倾向于去中心化的结构,目前看来Cassandra算是去中心化DB中性能/管理最热门的选择,崇尚其P2P的理念. 自身原因对JAVA不擅长(周围写C的好少),还是更热衷于C++/J ...
- MVC生成CheckBoxList并对其验证
原文:MVC生成CheckBoxList并对其验证 通过扩展方法,可以让CheckBox水平排列,生成CheckBoxList,正如"MVC扩展生成CheckBoxList并水平排列&quo ...
- 转载JQuery绑定鼠标粘贴事件工具类
// 粘贴事件监控 $.fn.pasteEvents = function( delay ) { if (delay == undefined) delay = 10; return $(this). ...