内容简介:Polymer——面向未来的Web组件开发
这本书将jQuery组件、自定义元素组件(Polymer)前后串联了起来,涵盖了当今很多重要的原始技术细节,特笔记如下框架,方便后续逐步回顾整理。
第一章 概述
Web组件技术将用户界面和逻辑封装在一起,以一种可重用的方式开发用户界面。
Web组件正在逐渐被主流接收, Polymer 小组走在了最前列, Mozilla 也有自己的框架( Mozilla or Microsoft释疑 ),社区的开发者们也在开发自己的实现方式。
什么是Web组件
Web组件是用来描述“一组允许开发者高效地表达已存在HTML元素实现”的技术集合的术语。
Web组件并不能解决所有问题,但给了开发者一种标准的方式,以将标签的实现打包封装。
Web组件关键概念列表
-
HTML模板
HTML模板是随时可以拿出来重复使用、序列化的文档对象模型(DOM)。可在
<template>
标签中编写模板片段(template之前的方式是:DOM节点对象创建、模板引擎、模板函数等)。 -
HTML引用
HTML引用用来在应用中接入被加载进来的独立代码片段,开发者只需要通过一个简单的
@include
就可以引用一个小型应用及其所有资源,而不用手动将其下载后再引用。 -
自定义元素
自定义元素,是在各框架、各平台和领域之间的一种通用、标准的元素生成方式。
-
Shadow DOM
Shadow DOM将Web组件中的DOM、样式和数据保护并封装起来,外界不能通过常规手段去改变。
为什么使用Web组件
Web已经逐渐进化为了应用平台,HTML5以及一些新API的出现是Web正在进化为一个应用平台的证据。
严格地说,HTML5是一个简单的具有一些新元素的文档模型。然而,当人们谈及HTML5时,更多的是表示包含了新元素、CSS3和一些新的JavaScript API的集合。这些技术重新定义了Web开发。
Web组件通过定义一组新特性,以支持原生的扩展、抽象、引用和封装,允许开发者创建具有标准生命周期的可重用元素(构建网页的部件),并能被任意其他应用所引用。这项标准将为元素和应用之间建立清晰的关系,使创建和维护界面的过程更加平滑。
第一部分 UI核心概念
软件开发的“基础”是指对设计概念和实现模式的深刻理解。
没有“基础”,开发出的软件不可避免的会漏洞百出。比如,由于对UI组件的布局、拖动、缩放等核心概念的误解而写出失败的代码,因为低效的设计或对标准 设计模式 理解有误而写出糟糕的API和实现等等。
第2章 基础知识
编写Voltron组件基类的目的是,提供一套用于构建DOM库的标准,这种标准无关具体实现——比如JavaScript控件、纯Web组件、Polymer组件,等等。
DOM抽象层的重要性
直接使用DOM的API很繁琐,尤其当需要兼容多浏览器的时候。jQuery是最著名和最被广泛使用的DOM库,实现了一个完善的DOM库的通用功能,DOM选择器、DOM元素遍历和DOM元素操作等。
控件的API和生命周期
控件应该保持一致的API和通用的生命周期,遵循统一的模式,保持同样的行为,以便于开发、维护和使用。控件的生命周期包括但不限于以下几个阶段:初始化、渲染、事件绑定、销毁等。
完善的API函数需要预设恰当的默认参数,并通过事件允许开发者切入控件的某些阶段实现需求。
继承模式
JavaScript使用原型继承,即直接继承自另一个对象;在类继承体系中,对象是通过类创建的,继承自类。
依赖
当创建一个Web组件时,应该尽最大的努力来限制控件对其他模块的依赖。在添加一项依赖之前,应当充分评估,其所带来的价值是否超过了它的体积和维护成本,这些负担将由控件的使用者来承担。
优化
过早优化是万恶之源 —— Donald Knuth
当应用的规模足够大时,理解整个应用的生命周期是不可能的。由此更突出控件一致的API和通用的生命周期的重要性。
UI控件基类Voltron主要包括渲染、事件绑定/解绑定、销毁等阶段,并实现一套通用的接口便于扩展和适配这些概念。大部分软件的UI层都包括构建、继承、初始化等概念。控件提供了对应阶段的事件,以便于在实现API细节的过程中,定期的运行性能测试。
在编写代码时要遵循某些最佳实践,比如批量更新DOM,在实现API的过程中进行性能测试,当性能问题显现后再进一步优化。如果你的设计足够合理,那么在软件开发后期性能瓶颈显现之时,你可以单独的优化每一个模块。
Web组件不仅仅是JavaScript
Web组件希望解决的是更大的问题——将HTML、CSS、JavaScript封装成一个可扩展的不影响周围DOM节点的集合。通过这种封装、声明性的实现,将Web从一个准开发平台升级为一个真正的开发平台。
示例控件
Voltron控件基类
对话框类
对话框的CSS和HTML
总结
定义一个具有通用生命周期的控件基类,使应用开发更易于开发、维护、调试,规定插件预期的输出和行为。
第3章 文档流和定位
每一个Web组件都需要被加入到DOM中,并最终渲染在屏幕上的某个位置与用户交互。DOM元素是Web组件的基石,理解DOM元素如何定位是Web组件开发的关键。
理解浏览器如何布局一个页面,首先需要理解两个关键概念:文档流和定位。
文档流
浏览器依据 文档流 来定位和渲染页面上的元素。
参考W3C CSS2.1标准:
文档流中的盒子,存在于格式化的上下文中,可能是块状的或行内的。块状盒子属于块状格式化上下文,行内盒子属于行内格式化上下文。
“盒子”指盒模型,每一个盒子代表一个元素。
一个元素可以看做一个盒子,盒子具有外边距、边框、内边距、内容,这四者共同决定盒子在页面上占据多大的空间。
块状元素垂直布局,如果不指定宽度,默认撑满整个父元素的宽度;
行内元素水平布局,宽度取决于元素里的内容、左右内边距、左右边框和左右内边距。在行内元素上设置宽度和高度不会影响元素的大小。
行内元素仅仅占据其中内容需要的空间,而块状元素则撑满整个父元素,除非指定宽度。
当元素不指定position属性时,文档流是元素布局的默认规则。
定位元素
文档流中元素position属性的默认值是static,即静态元素,其定位由文档流确定。
“定位元素”指position属性的值为relative、absolute或者fixed的元素,这些元素脱离了文档流。
position属性不会被子元素继承,每个需要定位的元素都需要设置position属性值,否则该元素的属性值就是static。
offsetParent
定位元素可以被看做容器元素,其被定位后代元素的位置(left、top属性)参考该容器元素。容器元素是所有没有更近定位祖先元素的后代元素(包含静态元素和定位元素)的offsetParent。
浏览器如何确定元素的offsetParent
一个元素的offsetParent有以下4种情况:
-
offsetParent是null
- 元素为<body>元素;
- 元素尚未被加入DOM;
- 元素不具有布局(元素的display为none);
- 元素的position属性未fixed(位置相对于视口区域)。
虽然这些情况下元素的offsetParent为null,但jQuery的offsetParent方法返回值是<html>元素,这样能保证每个元素都有一个offsetParent。
-
offsetParent是<body>
如果元素不是任何一个定位元素的后代,也不满足任何返回null的条件,那么其offsetParent为<body>。
-
offsetParent是<td>、<th>、<tbody>
对于非定位元素,如果<td>、<th>、<tbody>是其相对祖先定位元素更近的祖先元素,则offsetParent为<td>、<th>、<tbody>。
- offsetParent是最近的祖先定位元素 上述3种情况之外,对于定位元素或非定位元素,其offsetParent是最近的祖先定位元素。
定位
理解定位和布局的原理,是更高效的构建页面的基础之一。
相对定位
在文档流中仍然会保留相对定位元素的位置。如果不设置其他属性,该元素仍然会在那个位置。
当对该元素设置top、right、bottom或者left属性后,该元素会相对于元素在正常文档流中的位置平移。
相对定位的作用是:使元素成为一个容器元素,或者为元素应用z-index属性(z-index属性对于静态元素没有作用)。
绝对定位
绝对定位元素脱离了文档流,文档流中不存在其位置。通过top、right、bottom和left属性,相对于offsetParent定位(<body>的子元素是个例外,如果<body>具备定位属性,则相对于<html>定位)。
固定定位
固定定位元素也会脱离文档流,通过top、right、bottom和left属性,相对于视口定位,好像是视口的一部分。
计算元素的位置
相对于视窗
使用 element.getBoundingClientRect
方法可以获取元素相对于视窗的width、height、left、top、right和bottom值。也可以使用jQuery的 outerWidth
和 outerHeight
方法来获取元素的尺寸,但原生方法更快。
相对于文档
获取元素相对于文档(<body>)中的位置,常有以下两种方法。
-
方法一
沿着元素的offsetParent回溯DOM树直到<body>元素,将每一级的offsetLeft和offsetTop累加起来。
-
方法二(来自jQuery)
将元素相对于视窗的位置 + 视口和页面文档的位移(pageXOffset和pageYOffset)- 页面文档的位移(clientLeft和clientTop)。
定位对话框控件
总结
掌握文档流和定位的概念,能显著地提高Web组件的设计和性能。
第4章 理解和操作z-index
影响元素层叠次序的因素:层叠上下文(stacking context)、定位、透明度等等。
z-index究竟是什么?
根据W3C,z-index属性用来决定:
- 盒子在堆叠上下文中的堆叠级别;
- 盒子是否建立了一个堆叠上下文。
要理解z-index,需要首先了解渲染层(rendering layer)和堆叠次序。
渲染层、堆叠次序与z-index
网页是三维结构,具有水平维度的X轴和垂直维度的Y轴,以及第三维的Z轴——一个元素的z-index属性代表了该元素所在的 渲染层 ,即在Z轴上的位置。
默认的渲染层位于Z轴的位置0上。如果一个渲染层的z-index为负数,那么这一层将会被放置在默认渲染层的后面,用户就看不见它了(父层的背景颜色会将其遮盖)。这项技术通常被用以在开发者不希望用户看到这个元素的情况下对其进行计算。
当一个定位元素脱离了文档流,它仍然处在与之前相同的那个 渲染层 中。定位仅仅影响了元素在这个渲染层中的 堆叠次序 。
默认堆叠次序
没有z-index属性的元素,按照以下顺序从底到顶依次排序:
- 根节点的背景和边框;
- 文档流中的后代元素,按照在HTML中出现的顺序;
- 浮动元素( 关于float是否脱离标准文档流详细解析 );
- 脱离文档流的定位后代元素,按照在HTML中出现的顺序。
See the Pen stacking by testudy ( @testudy ) on CodePen .
重写默认堆叠次序
z-index属性被用来在 定位元素 上指定不同于默认情形的堆叠次序。
为元素赋一个大于0的z-index属性值,就使得元素离顶部(离用户)更近了。如果在 同一个堆叠上下文 中,其他元素也具有z-index属性值,那么z-index属性值最高的元素将出现在最顶部。而为元素的z-index赋一个负值,就把这个元素放在了默认渲染层的后面。具有相等z-index属性值的元素,堆叠顺序遵循默认的堆叠规则。
堆叠上下文
默认堆叠次序和z-index值生效的前提条件是,这些元素是某个已经创建堆叠上下文祖先元素的后代元素。
堆叠上下文是怎么创建的?
- 页面的根元素<html>创建了默认堆叠上下文;
- 定位元素并且具有非auto的z-index属性值;
- 在Webkit和Chrome 22+中,对于固定定位的元素,即使z-index为auto,也总是会创建上下文;
- 元素具有小于1的opacity属性;
- 元素中对于CSS 3中的一些属性,比如transform属性不为none,也会创建堆叠上下文。
事情越来越复杂了
堆叠上下文最重要的作用是,它创建了一组独立的渲染层,其中各元素的堆叠次序不受外部元素的z-index值影响。
See the Pen z-index by testudy ( @testudy ) on CodePen .
管理z-index
在商户网站之类的单一、静态Web网站中,将不同种类的控件划分为不同的层级,是一种简单有效的管理z-index的方法。比如下表中 IAB显示广告z-index使用指导 :
z-index区间 | 内容类型 | 详情 |
---|---|---|
< 0 | 背景元素 | |
0 - 4,999 | 主要内容与标准广告 | 标准广告标签与常规内容,包括OBA本身的常规消息 |
5,000 - 1,999,999 | 扩展广告 | 整个可扩展的广告单元应该位于该区间 |
2,000,000 - 2,999,999 | 浮动广告 | 页面上方的广告(OTP) |
3,000,000 - 3,999,999 | 弹出元素 | 对话框,消息提示框 |
4,000,000 - 4,999,999 | 非固定元素 | 调查补充区域 |
5,000,000 - 5,999,999 | 扩展站点导航元素 | 下拉导航、站点警告等。只有导航元素的扩展部分才位于这一层 |
6,000,000+ | 全页面浮层 | 覆盖整个页面(OTP)的广告,以及页面之间的覆盖整个页面的广告 |
但对于动态的Web应用等不确定的环境中,DOM结构和z-index会被频繁更改,更可靠的方案是:利用关于z-index的知识,在应用和组件的代码中,编程管理z-index。
z-index管理者对象
作者编写了 jenga 来实现z-index的编程管理,主要包含一下功能:
- 把一个元素放到堆叠上下文的顶部或底部;
- 创建一个元素的堆叠上下文;
- 获取一个元素是否创建了堆叠上下文。
转化为jQuery插件
一个良好的插件应当仅仅提供一个针对单个元素或元素集合的有限且专业的功能。
一个工程的开发者在决定依赖一个库之前,应当充分衡量其带来的好处和负担。对z-index管理对象而言,依赖jQuery带来的好处并没有超过其带来的负担。
向对话框控件类添加z-index管理功能
借住jenga可以方便的使对话框展示在页面的最上方。
总结
仔细研究元素在Z轴上的渲染情况。
第二部分 构建UI
学习如何定位元素、如何控制元素的位置和尺寸。
第5章 克隆节点
克隆节点会增加性能开销,常见于下面3种场景中:
- 拖拽操作;
- 复制节点;
- 基于“幕后”的节点计算和操作。
拖拽操作的步骤:
- 克隆要拖拽的节点,即制造节点的复制品;
- 如果节点被拖拽到指定区域外,直接删除节点的复制品;
- 如果节点被成功的拖拽到了指定位置,只需将节点的复制品接到对应的节点上,然后删除原有的节点即可;
- 在复制品节点没有被成功的接到目标位置上时,原有的节点无需删除。
在拖拽操作中克隆节点,比把节点从DOM树中脱离下来、移动,然后再接到DOM树上,要更有效率。
使用cloneNode方法
所有浏览器都原生支持节点的 cloneNode 方法,以克隆节点。函数接受一个可选的参数,以指定是否是深度复制所有的后代节点,默认值为false。
因为标准的变化,cloneNode方法deep参数的默认值,不同的浏览器版本不一样。DOM3中的默认值是false,DOM4中的默认值是true,为了保证向后兼容,应该总是明确的传入一个deep参数。
当一个节点被克隆时,所有的属性和值(包括内联的事件处理函数)都被复制到了节点的复制品上。但是通过addEventListener或元素的事件属性(如onclick)绑定的事件不会被复制。
cloneNode方法会复制节点所有的属性,应当格外注意可能会产生重复的ID。严格意义上,页面中是不允许出现重复ID的,因为ID本身就意味着唯一的标识符。但是,实际上浏览器却通常允许重复ID。重复ID可能会导致一些问题,比如通过ID查询元素并期望仅仅返回一个元素时却返回了多个(querySelectorAll方法)。
使用jQuery.clone
jQuery.clone 方法总是会进行深度克隆,当需要时,可以将所有通过jQuery绑定的方法和通过$.data绑定在元素上的数据,能够复制到新的节点上。
数组和对象是通过引用复制的,如果有必要,可以在节点复制完成之后,进一步对数据进行深度复制。
对话框控件
设置options.clone选项,以允许开发者决定如何使用传入的容器元素。避免容器元素被应用其他部分不经意的改变。
总结
适当选择cloneNode或jQuery.clone方法高效的克隆节点。
第6章 创建浮层
浮层是一种基础的控件,其主要功能是相对文档定位一个元素,或相对其他元素定位一个元素。浮层是一个DOM元素,一个空的容器,可以容纳任何内容。在浮层的基础上可以通过扩展、混入等方式构建更加具体的与用户直接交互的控件,比如对话框、提示框、下拉菜单等。
浮层具有两个最主要的功能。
首先浮层应该能够相对于视窗进行定位,这样浮层就可以用来向用户提醒一些重要的信息,如错误等。
第二,浮层应该能相对于文档中的另一个元素进行定位,以能够跟某个元素对齐。
定义API
首先确定API,进而确定支持性的、供公开方法调用的私有方法。
工具
为了简便,可以直接将计算尺寸和位置的 工具 方法和函数在闭包中定义。
检测滚动栏的宽度
定位一个元素的时候,需要考虑容器元素的滚动条宽度。元素的offsetWidth属性值和offsetHeight属性值包含滚动条占据的宽度,而元素的clientWidth属性值和clientHeight属性值不包含滚动条占据的宽度,前者减去后者即是滚动条的宽度。
macOS系统上的浏览器中滚动条不占元素空间,上述方法算出来值为0
See the Pen offsetWidth vs clientWidth by testudy ( @testudy ) on CodePen .
计算容器宽度时考虑滚动条
如果元素的内容溢出,其scrollWidth和scrollHeight会比元素实际的宽度和高度大一些,此时元素尺寸的计算需要考虑滚动条的宽度。
获取元素尺寸和坐标
浮层的定位信息,包括浮层自身的尺寸,和浮层相对于容器元素或并列元素的位置,包含{width, height, top, right, bottom, left}6个属性。
所谓并列元素,是指浮层依赖其定位的元素。比如,用户鼠标悬停在某个元素上时显示一个提示框,悬停的元素就是提示框的并列元素。
监听尺寸改变事件和滚动事件
当浮层相对于其容器元素位置固定时,当容器内容滚动,尺寸变化等情况发生时,应当重新对浮层进行定位,浮层在容器中的视觉位置不发生变化。通过监听 scroll
和 resize
事件,实时调整浮层的位置。
更新选项
通过setOptions方法重新设置el和alignTo等属性,在不销毁浮层的情况下,改变浮层的配置。比如在众多的单元格上显示单元格相关信息的时候,可以通过这个方法来代替频繁的销毁和创建组件。
销毁
对于销毁控件,控件需要做的就是把元素恢复到控件初始化前的状态,即destroy函数中把el和alignTo等属性设置为null,并接触任何绑定到该元素的事件处理函数,以防止潜在的内存泄露。
而销毁这些控件实例的责任在使用该控件的业务代码中,浮层不应该假设这个元素在浮层控制之外还有一些特定状态,只解除由控件本身产生的副作用即可。
定位
如果元素是相对于父元素定位,实现相应的position方法; 如果元素是相对于另一个元素(兄弟元素)定位,实现相应的align方法。
相对视窗或相对另一个元素定位
在固定在中央这样的简单情况下,直接使用CSS就能完成;
当需要拖拽等复杂操作的时候,就需要JavaScript基于元素相对视窗的位置进行计算。
相对另一个元素定位元素
向对话框控件添加浮层功能
总结
第7章 拖动元素
Web应用经常会出现可拖动的元素:滑块控制条、对话框、可调整宽度的表格列等。
鼠标事件
拖动一个元素,会用到mousemove,mousedown和mouseup3种鼠标事件。
-
$.mousemove
追踪鼠标的移动,是拖拽元素的基础,因为鼠标的位置将决定被拖拽元素的坐标。可以使用clientX、clientY和pageX、pageY等值获取鼠标位置的信息。
-
$.mousedown
mousedown事件帮我们确定用户何时何处按下了鼠标按钮。在拖拽元素时,按下鼠标按钮的那一刻获取鼠标位置,之后元素才会随着鼠标按钮的移动被拖拽。
-
$.mouseup
松开鼠标按钮并结束移动,是拖拽过程的最后一步。当mouseup事件被触发后,鼠标最后的位置就决定了元素被拖拽到的位置。
鼠标事件的最佳实践
-
在$.mousedown事件中绑定$.mousemove事件到html上
mousemove事件会在鼠标移动时频繁触发,即使鼠标只移动了一个像素。为了保证mousemove事件仅在必要时执行,最好的方法是在$.mousedown事件中绑定$.mousemove事件,以避免不必要的触发mousemove导致的性能开销。
-
在$.mouseup事件中解除$.mousemove的绑定
在结束拖拽后,避免不必要的mousemove事件。
-
将$.mouseup事件响应函数绑定到<body>上
有些情况下,触发mouseup事件的元素与触发mousemove事件的元素可能并不相同,所以最好的方法就是,总是将$.mouseup事件响应函数绑定到<body>标签上。
-
命名所有事件
在使用jQuery绑定事件时,为事件处理函数指定一个命名空间,保证绑定在控件上的函数与其他事件处理函数不相冲突,也方便在销毁控件时进行清理工作。同时命名空间也可以用来滤去其他冒泡到<body>的鼠标事件。
定义API
创建一个Shamen类,用来控制元素的拖拽。
创建拖拽柄
有时候,控件的拖拽柄是某个特定的子元素,用户通过拖拽该子元素来拖拽控件。比如,对话框的拖拽点往往是标题栏。
拖拽柄元素是响应mousedown事件的元素。用户在拖拽柄上按下鼠标按钮后,才可以开始拖拽整个控件元素,或者调整控件的尺寸。
拖动起来
$.mousedown响应函数
$.mousemove响应函数
$.mouseup事件响应函数
销毁可拖拽实例
就像浮层控件或其他控件一样,应该在销毁控件时进行清理工作。这样做有助于防止内存泄露,减少已被销毁的控件对应用的遗留影响。
使对话框控件可拖拽
总结
第8章 调整元素尺寸
一个功能齐备的Web应用可能具有多个可调整尺寸的元素。调整控件尺寸功能可以增强控件的可用性,或者支持某些功能。比如,允许调节控件尺寸可以调整控件中文本的排列,使其更易于阅读。
鼠标事件和最佳实践
使用鼠标操作调整元素大小和拖拽元素,都需要使用相同的鼠标事件,也遵循相同的最佳实践。
事件
最佳实践
调整元素尺寸
调整元素尺寸时需要考虑鼠标移动的方向,一般设置如下8种常见的方向:上、右上、右、右下、下、左下、左、左上。
在CSS的 cursor属性 中使用了方位,对应上面8种方向的值分别是:n-resize、ne-resize、e-resize、se-resize、s-resize、sw-reszie、w-resize、nw-resize。也包含ns-resize、ew-resize、nesw-resize、nwse-reisze 4种双向指针。
编写调整尺寸的API
拖拽/尺寸调整柄区
创建新的DOM元素作为尺寸调整柄区元素,应用预设好的CSS样式,然后将元素插入到DOM中。
绑定鼠标事件
$.mousedown事件响应函数
获取鼠标的最初位置,分辨元素尺寸的调整方向。
$.mousemove事件响应函数
在mousemove事件中,使用当前鼠标位置减去上次触发mousemove事件时鼠标的位置,然后根据当前调整的方向调整width、height、top或left等属性。
$.mouseup事件响应函数
销毁工作
清除对元素的引用,解除事件绑定函数,从DOM中移除调整柄区。进行清理工作以避免内存泄露。
完成调整尺寸功能
使对话框控件可调整大小
总结
第9章 完成对话框控件
添加样式
添加CSS
建议直接使用CSS样式表管理样式,因为使用JavaScript管理样式存在下列缺点:
- 不能很好的适应各种环境,比如需要逐个元素繁琐的设置,不如使用选择器便利;
- 设置样式的效率不如样式表高;
- 使用JavaScript管理样式使用的是内联样式,优先级管理比较困难。
合并JavaScript文件
合并JavaScript文件,将依赖项全部引入到闭包中,以减少对环境的污染。
总结
将对话框控件拆成更加小的、可重用的功能,每个功能处理一些特殊的情形,比如已经拆分的克隆、拖动、尺寸调整等类。
第三部分 构建HTML5 Web组件
W3C的Web组件标准 ,包含模板、自定义元素、Shadow DOM、引入HTML等部分,以进一步实现组件的语义性、声明性、封装性、可用性和可维护性。
第10章 模板
相关资料(未阅读)
在过去,浏览器缺乏原生模板,Mustache、Handlebars、Hogan、Ejs等客户端模板解决方案是弥补此缺陷的变通方法。
HTML5标准中定义了 Template元素 用来实现原生模板, 大多数浏览器 已经支持该标准,对模板的解析由浏览器原生完成,效率会比传统的客户端模板引擎更高。
理解模板的重要性
Mustache等传统的字符串模板与服务器端模板类似:将模板字符串编译为函数,这些函数接受模板数据,并将这些数据和模板字符串按照规则拼接成HTML。
使用 <template>
模板元素,可以从其content属性中获得对应模板内容的文档片段(document fragment)对象。在此基础上,通过提取或复制节点,以实现文档片段模板。
延迟加载资源
原生模板延迟了加载和处理模板所引用的资源(图片和脚本)的时机,不会阻碍页面的渲染(除去由模板自身带来的文档体积增加和传输时间延长)。
延迟渲染内容
原生模板在页面中的任何位置,浏览器都不会把模板中的内容直接渲染出来,也避免了由于解析不需要的模板内容而带来的开销。
从DOM中隐藏内容
原生模板中的内容并不是DOM的一部分,当查询DOM节点时,不会返回模板内的节点,不会拖慢DOM节点查询的速度,模板中的内容可以被看做是隐藏的。
创建和使用模板
直接在 <template>
标签中编写模板,然后使用JavaScript克隆其中的内容,并输出到DOM中。
检测浏览器支持
'content' in document.createElement('template')
将模板放到标签中
<template>
模板标签可以被插入到 <head>
、 <body>
、 <frameset>
等标签,或其任意后代标签中。
将模板内容插入到DOM中
先获取模板节点的引用,然后克隆(cloneNode或document.importNode)其中的节点,将其插入到DOM中。
使用模板编写对话框组件
在 <template>
标签中组织JavaScript、CSS和HTML等对话框代码,所有代码都是惰性的,在插入到DOM之前,不会被浏览器执行。
当文档加载和解析完成之后添加到DOM中的script标签,会异步的加载资源。在标签中设置 async="false"
属性不能解决这个问题,可以通过设置script元素的 async=false
来保证其顺序加载。
创建和包装对话框模板API
可以创建一个构造函数将克隆模板和插入DOM的过程封装起来以方便使用。
实例化对话框组件
抽象对话框模板包装
可以尝试将构造函数改造的更加通用,这要求所有模板必须使用相同的API,而且构造函数不能包含对话框专有的代码逻辑。
总结
原生模板相比Mustache等现有的模板解决方案方案的优势:
- 原生模板延迟了图像等标签的解析,并且不会影响页面渲染的性能(当然,内容的增加会带来一定的网络传输消耗);
- 内容的渲染和脚本的执行被延迟了,直到其显式的插入到DOM中;
- 模板对DOM而言是隐藏的,不会拖慢查询的性能。
第11章 Shadow DOM
相关资料(未阅读)
Shadow DOM是一种对HTML、CSS和JavaScript的封装。
对于控件/组件的开发而言,DOM的缺陷是缺乏封装,通常会导致以下问题:
- 组件CSS的渗入和渗出:为了确保组件样式和页面样式不冲突,我们被迫编写了过于详细的后代选择器,甚至会不得不滥用!important。
- 组件外部的JS代码可能会对组件内的DOM树产生影响。
什么是Shadow DOM?
W3C定义 Shadow DOM是由一些DOM节点组成的一棵树(Shadow DOM子树)。这些子树可以被连接在一个元素上,但并不会成为该元素的常规子节点。相反,这些子树形成了自己的“闭合空间”。比如,Shadow DOM子树可以包含与父文档中重复的ID和样式,但是因为Shadow DOM子树和文档是分离的,这些ID和样式并不会与文档中的相冲突。
Shadow DOM允许开发者在某个普通的元素下创建一棵新的DOM子树。这个新的DOM子树是独立的,与外部DOM文档是相隔离的。比如,新DOM树与外部文档可以拥有相同ID的元素,在外部文档中使用选择器进行查询不会获得子树中的元素。
Shadow DOM基础概念
Shadow宿主
Shadow宿主(Shadow Host)是包含Shadow子树根容器(Shadow Root)的DOM节点。Shadow宿主是文档中的一个普通节点,其下包含了Shadow子树。除了Shadow子树的根容器,宿主的任何其他后代元素都是可见、可被选择器匹配到的。
Shadow根元素
Shadow根元素(Shadow Root),即Shadow子树的根元素,是挂载于宿主元素下的一个元素。根元素内的任何内容都独立于外部文档,不能被选择器选中。 在父文档下的某个节点下创建Shadow根元素,将使得该节点成为宿主元素。
向一个宿主元素添加多个Shadow根元素时,遵循“后进先出”模式,只有最后一个添加的根元素会被渲染出来。
创建Shadow根元素
<div id="host"></div> <script> var host = document.querySelector('#host'); var root = host.createShadowRoot(); </script>
在Shadow DOM中使用模板
使用模板来填充根元素,只需要将template.content添加到根节点中即可。
See the Pen Shadow DOM basic by testudy ( @testudy ) on CodePen .
Shadow DOM的样式
造成全局样式问题的主要原因是:
- 过于宽泛的选择器,如div;
- 滥用!important;
- 控件中CSS渗出,影响到了应用级的CSS;
- 多个开发者共同维护一份代码的时候,上述问题会更突出,遵循相同的规范能解决上面的问题;
- 但如果应用依赖了开源库,如jQuery UI、Bootstrap等,这些库有可能没有遵循相同的规范。
使用Shadow DOM带来了两大好处:
- 解决了上述问题;
- 提供了一种标准的方式,用来为元素添加不与外部冲突的样式。
样式封装
在Shadow DOM中定义的任何样式,仅仅在Shadow子树中有效,对子树外的部分,不会有任何作用。定义在外部的样式也不会影响到Shadow DOM中的元素。
为宿主元素添加样式
使用 :host
选择器,可以从子树内部设置宿主元素的样式,从而将宿主元素的样式封装在Shadow子树中。
父文档中的选择器和内联样式比 :host
选择器具有更高的优先级。
:host
选择器后面还能跟随一个选择器,如 :hostselector
,以为特定的宿主设置样式。适用于设置主题和管理状态。
Shadow Host中其他的元素会被Shadow Root元素覆盖,当存在Shadow DOM的时候,Shadow Host仅用来做Shadow DOM的容器使用。
在文档中设置Shadow子树根节点样式
使用 ::shadow
伪元素选择器,可以从父文档中选择Shadow子树的根节点,进而选择子树中的元素。
连续使用 ::shadow
伪元素选择器可以穿透多层shadow根节点,也可以使用 /deep/
选择器穿透至shadow子树的根节点中。
这不是和封装的理念冲突了吗?
封装并不意味着Shadow DOM与父文档之间的界限是完全无法逾越的,以至于诸如主题定制等功能都无法实现。Web开发的问题是, 从来没有正式的封装组件的方法,也没有正式的打破封装进入组件内部的方法 。遵从统一的封装和打破封装的规范,能够使代码准确的表达出开发者的意图。
See the Pen Shadow DOM advance style by testudy ( @testudy ) on CodePen .
内容映射
Web开发的最佳实践之一,是“内容与表现的分离”:
- 内容与样式分离,Shadow DOM之前只能禁用内联样式之类的实践,而Shadow DOM则更进一步将样式完全封装在组件内部;
- 组件的内容放在Shadow宿主元素中,以便于外部文档获取和维护,并映射到Shadow根节点中以渲染出来。
通过content标签映射
Shadow Host中的任何内容,会自动映射到 <template>
中的 <content>
标签内。
通过选择器映射内容
<content>
标签的select属性可以赋值一个选择器的表达式,以匹配指定的内容,并且只有匹配到的内容才会被映射进来。
注意:
- 当匹配到多个元素时,都能被映射进来。
-
当存在多个匹配到同一个元素的
<content>
标签时,只有第一个<content>
可以匹配成功。 - 只有Shadow Host的子节点才能被映射。
内容映射至少解决了两个问题:
- 数据的传递,也即数据和样式结构的分离;
- 优雅降级,Shadow DOM目前阶段从业务实现上看可以是渐进增强。
See the Pen Shadow content by testudy ( @testudy ) on CodePen .
节点分发和接入点
被分发节点是指从Shadow Host中被映射到Shadow DOM中的节点;
Shadow Host中的节点可以被映射到Shadow DOM中的任意 <content>
节点(接入点)中,但 <content>
节点下没有任何后代节点。
- Element.getDestinationInsertionPoints() 方法用来获取Shadow Host中被映射节点对应的接入点;
-
HTMLContentElement.getDistributedNodes()
方法用来获取映射到
<content>
中的分发节点。
See the Pen Shadow DistributedNode & DestinationInsertionPoints by testudy ( @testudy ) on CodePen .
Shadow接入点
如果宿主元素中存在多个shadow根节点,根据加入的先后顺序,只有最后一个会被渲染出来。较早加入宿主的成为 较旧子树(older tree) ,而较晚加入的称为 较新子树(younger tree) 。
Shadow根节点的接入点使用 <shadow>
标签定义,允许在较新子树中提供一个接入点,将较旧子树映射到较新子树中。如果一个子树中包含了多个 <shadow>
元素,只有第一个会起作用。
See the Pen Shadow destination insertion point by testudy ( @testudy ) on CodePen .
事件与Shadow DOM
映射节点的改变会同步到shadow子树内部,绑定到被映射节点上的事件,有一些事件会被重定向到被映射元素,看上去像是由宿主元素触发的(此时,通过事件对象的path属性可以获取到触发该事件的子树);有一些不会被重定向,包括 abort
, error
, select
, change
, load
, reset
, resize
, scroll
, selectstart
9个。
使用Shadow DOM更新对话框模板
对话框标签
对话框的API
更新对话框的show方法
初始化对话框实例
总结
第12章 自定义元素
自定义元素规范有V1和V0两个版本,其中V0版本已经被标准废弃,标准推荐的是V1版本,下面的Demo中会标明两个版本的代码差异。
参考列表
自定义元素简介
自定义元素的目的,是提供一种通用的方式,将一些功能封装为“自描述”的标签。这种机制为元素提供了统一的生命周期和一套API以注册和扩展元素。
// v0 确定浏览器是否支持自定义元素 var supportsCustomElements = (function () { return 'registerElement' in document; }()); // v1 确定浏览器是否支持自定义元素 var supportsCustomElements = (function () { return 'customElements' in window; }());
注册自定义元素
为了让浏览器能够识别并支持某个自定义元素,首先需要向浏览器注册一个自定义元素,把该元素添加到浏览器的已注册元素列表。浏览器使用已注册元素列表来区分和解析页面里的元素,并根据定义的方式渲染元素。
V0通过 doument.registerElement
方法来进行自定义元素的注册,该方法接收两个参数,第一个参数是被注册的自定义元素的元素名,第二个参数是配置对象,用来指定该元素的原型(默认的原型是HTMLElement.prototype)。
V1通过 customElements.define
方法来进行自定义元素的注册,该方法接收两个参数,第一个参数是被注册的自定义元素的元素名,第二个参数是对应的自定义元素类。
自定义元素的命名规则是:自定义元素名全部用小写,中间包含一个连字符。以防止与HTML标准中的原生元素相冲突,也便于浏览器区分自定义元素和原生元素。
See the Pen Autonomous custom element by testudy ( @testudy ) on CodePen .
扩展原生元素
当一个自定义元素扩展自原生元素时,该自定义元素被称为 类扩展自定义元素(type extension custom element)
,可以被原生元素以is属性的方式调用,定义自己成为加强版的自定义元素,即类扩展自定义元素。 V0可以在 document.registerElement
方法的第二个参数中可以指定原型和extends属性来扩展元素。 V1可以在 customElements.define
方法的第二个参数中可以指定原型,第三个参数中的指定extends属性来扩展元素(目前浏览器还不支持)。
See the Pen Customized built-in element by testudy ( @testudy ) on CodePen .
定义属性和方法
自定义元素可以添加自己的属性和方法,直接在类定义上添加即可。
解析自定义元素
在自定义元素未注册之前,对于文档中的自定义元素,浏览器的HTML解析器并不会报错。浏览器把这类元素全部解析成HTMLElement元素。而对于不符合自定义元素命名标准的未知元素则和废弃的元素类型一样解析为HTMLUnknownElement。
参与自定义元素的生命周期
传统的JavaScript控件通常缺乏清晰的生命周期,不同库之间的处理方式存在差异,自定义元素的最大优势是提供了统一的生命周期——一套标准的关于元素生命周期的描述和事件方法,允许介入生命周期处理的过程。
控件的生命周期应该是统一和简单的,如果生命周期过于复杂,就说明控件做出了过多的假设,结果就是过于死板,难以使用,这些假设也很有可能被证明是不对的。
事件/方法名称 | 调用时机 |
---|---|
V0事件 | |
createdCallback | 元素实例创建好之后调用 |
attachedCallback | 元素添加到DOM上之后调用 |
detachedCallback | 元素从DOM中移除时调用 |
attributeChangedCallback (attrName, prevVal, newVal) |
元素的属性改变时调用 |
V1方法 | |
constructor | 创建或升级元素的一个实例。用于初始化状态、设置事件侦听器或创建 Shadow DOM。参见规范,了解可在 constructor 中完成的操作的相关限制。 |
connectedCallback | 元素每次插入到 DOM 时都会调用。用于运行安装代码,例如获取资源或渲染。一般来说,您应将工作延迟至合适时机执行。 |
disconnectedCallback | 元素每次从 DOM 中移除时都会调用。用于运行清理代码(例如移除事件侦听器等)。 |
attributeChangedCallback (attrName, oldVal, newVal) |
属性添加、移除、更新或替换。解析器创建元素时,或者升级时,也会调用它来获取初始值。注:仅 observedAttributes 属性中列出的特性才会收到此回调。 |
adoptedCallback() | 自定义元素被移入新的 document(例如,有人调用了 document.adoptNode(el))。 |
为自定义元素添加样式
为自定义元素添加样式的方法和为其它元素添加样式的方法相同。
如果自定义元素注册发生在页面渲染之前,此时文档中的自定义元素标签和样式会正常渲染。
如果自定义元素注册发生在页面渲染之后,则有可能会进行重绘导致自定义元素的闪烁(FOU,未样式化内容的闪烁),此时可以用 :unresolved
(v0)或 :not(:defined)
(v1)伪类来进行识别。
在自定义元素中使用模板和Shadow DOM
对自定义元素而言,建议将元素中的内容放在模板中,这样直到元素被实例化之前,这些内容都是惰性的。如果需要隐藏自定义元素的内部细节,可以使用Shadow DOM,以阻止元素的内部样式和节点被无意篡改。
将对话框组件实现为一个自定义元素
使用Web组件技术将UI组件封装起来,使之成为一个可自我描述的接口,既自定义元素。
Web组件技术在一定程度上解放了自定义元素的实现者,使其不用考虑维护一套向后兼容的的扩展共用API,允许具体实现者简单的在惰性模板中修改实现细节。Shadow DOM提供的封装特性有效的消除了组件内外代码的运行时冲突。
创建对话框自定义元素
实现对话框自定义元素的回调函数
实现对话框自定义元素的API
显示对话框
总结
自定义元素带来的主要优势:可维护性、可读性和封装。
第13章 引入文档
几乎每一种应用开发平台都允许开发者打包和引入外部代码,但传统的Web平台开发领域,通过 <link>
标签引入CSS,通过 <script>
标签引入JavaScript文件,共享不同的技术方案中的代码比较困难。由于Web缺乏一种标准的引入代码的方式,开发者们使用和开发了很多其他的有创造力的解决方案。但是这些地方都没有能够完整地解决CSS、JavaScript、HTML和图片等多种资源作为一个整体引入的一个问题,难以使开发者以一种统一的方式定义自己的资源。
声明引入
标准的引入外部代码的方式非常简单,使用一个 <link>
标签,然后设置 rel
的值为 import
即可,或者采用对应的JavaScript编程方式。
<link id="dialog-import" rel="import" href="/imports/dialog/index.html" />
不管引入了多少次,具有同一个URL的引入只会加载和执行一次。检测是否支持引入功能的代码如下:
'import' in document.createElement('link');
获取引入的内容
获取文档
不管JavaScript代码的上下文如何, document
总是代表最顶层的文档; currentScript
值是当前脚本所在的 <scritp>
标签节点; ownerDocument
值是当前节点所属的文档; 在外部文档中,可以通过 <link>
元素的 import
属性来获取被引入的文档。
应用样式
引入代码生效后,其中的样式就会自动应用在外部主文档上,如果需要,其中的样式选择器必须使用了合适的命名空间。如果删除掉引用标签 <link>
或者删除引用代码中的样式,那么样式的效果也会立即被移除。
如果两个引入元素的link地址是一样的,那么第二个引入的样式对页面的CSS完全没有影响,如果第一个link元素被移除后,另一个具有相同地址的link被添加进来,那么会影响CSS。
单页应用中可以在应用一开始的时候就把所有必须的资源——HTML、CSS和JavaScript都添加进来,也可以在合适的时机按需加载这些资源。单页应用从来不刷新页面,或者调整到另一个页面,在运行过程中会时不时地与Web服务器进行交流。
获取模板
可以使用 <link>
标签引入模板和公用模板集合页面。
执行JavaScript
引用中的JavaScript在主文档的上下文,即 window.document
中执行,定义在引用中 <script>
标签中的任何变量和函数,只要不在闭包中,都会是全局上下文 window
的一部分。这对于将函数输出到主文档很有用,但同时也为全局上下文的冲突带来了潜在的可能,需要小心使用。
当从主文档中移除一个 <link>
,并不会移除JavaScript执行过的效果,使用引用的时候要注意这个细节。
理解引入和主文档的关系
引入文档中,同样需要遵循基本的最佳事件:减少网络请求数、减少文件数、将脚本放在页面底部,等等。
解析引入
引入文档并不会阻碍主文档的渲染,也就是说引入文档中的所有脚本都像具有 defer
属性会延后执行。这些脚本执行的时机是在主文档完成对页面中的所有不具有 defer
属性的脚本执行完成之后。
// 测试该顺序 // 测试主页面脚本将import link移除脚本是否执行。
跨域
引入文档的URL与Ajax一样遵循同源限制策略。如果需要跨越可以设置跨域共享资源(CORS)。
二级引入
引入的文档中可以继续再引入其他文档。二级引入的机制与其他代码模块化的机制类似:重用、抽象、可测试、可扩展,等等。二级引入解析和执行的规则与引入文档一致。
加载自定义元素
使用自定义元素定义注册系统,避免污染全局变量。
引入对话框
总结
第四部分 使用Polymer测试、构建、部署Web组件
Ploymer项目不仅仅为Web组件提供了优美的API,还提供了非常多的特性和针对浏览器的包装(Shim),把未来的Web开发技术变成了今天就可用的方案。
第14章 Polymer简介
目前直接使用Web组件确实有许多不便之处,Ploymer是一个帮助开发者尽快使用Web组件技术的项目,Polymer的使命是:“尽可能地拥抱HTML,鼓励在任何可能的情况下使用自定义标签”。
Ploymer的作者为Web组件的方方面面及相关的技术创建了一些包装(Shim)。这些包装可用在Ploymer之外,已经被诸如Mozilla和Microsoft等公司接受为标准,并被其结合自己的Web组件框架一起使用。
Ploymer包含了两层代码,上层代码是在标准的API上面的某种专属语法糖,为创建和管理Web组件提供了框架和工具。而Ploymer的底层代码又称为platform.js,是一系列应用广泛的包装,为各种“常青树”浏览器提供统一的API。
常青树浏览器
“常青树”浏览器指那些不需要手动操作就能自动更新的浏览器,包括Chrome,现代的Firefox,现在的Opera和IE10+。常青树浏览器的含义是,该浏览器在一定时间范围内,很可能会支持新兴技术的浏览器,除非这个浏览器大幅的偏离了标准。
组成Polymer的上层模块和组成底层基础的下层模块之间的关系如下图:
Polymer虽然提供了优秀的浏览器底层包装,但Polymer的长期目标恰恰是通过促进浏览器技术的升级,消除这层浏览器底层包装。与jQuery不同的是,Polymer只承诺支持常青树浏览器,这样就能保证,随着浏览器的自动升级换代,这层浏览器包装代码最终能得以移除。
Polymer的语法受到 <element>
元素的启发,用来声明式的定义HTML文档中的元素。
Polymer元素
自定义元素的元素名必须有一个连字符,所以通常我们会利用前缀,把自定义元素定义在某个命名空间下,比如polymer-tab和myapp-button。对于那些逻辑上不属于某个命名空间的元素,通常使用x-或ui-这种宽泛的前缀。
添加样式
为元素添加CSS规则,只需要在 <template>
元素中添加一个 <style>
标签就行。其中可以使用Polymer提供的数据绑定机制。
外部资源
通过 <link>
标签无法将外部的样式表导入到Shadow DOM中,Polymer找到 <link>
元素,通过XMLHttpRequest请求异步的将CSS样式添加进来,并删除该 <link>
元素。
过滤表达式
模板格式
- 数据绑定
- 循环快
- 上下文绑定
- 分支判断
- 复合模板指令
特性(attribute)和属性(property):元素的API
对于Web组件,需要创建统一、直观、确定的“类HTML”API,需要考虑如下问题:
- 哪些将成为特性(attribute)?
- 哪些将成为属性(property)?
- 哪些两者都是?
- 哪些是只读的?
- 元素实例化后,如果属性或特性发生了变化,应该如何响应?
- 是否需要触发事件?如果需要,触发哪些事件?
- 元素的API是否需要遵从已有元素的API形式和最佳实践?
原生特性
元素的实例上调用 getAttribute
方法以获取对应的特性,特性无法通过模板双向绑定。
公开属性
公开属性是自定义元素暴露给外部的属性,可以通过模板绑定。
实例方法
通过Polymer定义在元素原型上的所有方法,都作为元素的实例方法暴露给外部JavaScript。
Polymer的JavaScript API
Web组件的优美之处,以及Polymer的逻辑,就是尽可能的拥抱HTML,赋予其最现代的能力。HTML不再是一些你需要小心翼翼的使用。通过Web组件和Polymer,我们自定义HTML的功能,使用JavaScript实现这些功能,然后以HTML形式分享给用户。
生命周期方法
Polymer | V0标准中对应的方法 | 执行时机 |
---|---|---|
created | createCallback | 元素被创建时 |
ready | 无 | 完成了元素的初始化时 |
attached | attachedCallback | 元素被添加到DOM中时 |
domReady | 无 | 初始子节点存在时 |
detached | detachedCallback | 元素从页面中移除时 |
attributeChanged | attributeChangedCallback | 元素的特性(attribute)被增加、删除或修改时 |
事件
可以使用原生方法触发事件,也可以使用 fire
方法。
处理延迟工作
可以使用job方法,注册需要过一段时间执行的工作。
总结
可扩展Web宣言
- 专注于向Web平台添加新的、安全的和高效的底层功能;
- 暴露那些描述已存在特性——诸如HTML和CSS——的底层功能,允许用户理解和实践这些功能;
- 开发、描述和测试新的高级JavaScript功能,允许Web开发者在这些功能成为标准前反复测试,以建立标准和开发者的良性循环;
- 优先开发遵循这些建议的功能,降低或重新审视不遵循这些建议的功能。
第15章 将对话框迁移至Polymer
Web组件本质上提供了一层抽象,将API推到了HTML的层面。可以看做未来Web的基石,是改良下一代Web开发的契机。
到底为什么要迁移到Polymer
进步不止的Web
你见过用Ant管理,或者用Rhino构建的JavaScript项目吗?他们确实存在,但是今天已经很难发现有什么流行的项目是依赖Ant或Rhino了。
今天,Ant和Rhino依然可以正常工作,但是它们的作用已经完全被后起之秀,诸如grunt和Node.js取代。在今天的JavaScript开发者的工具箱中,后者几乎已经完全的替代了Ant和Rhino。
接下来呢?我们看到Gulp非常流行,它吸取了Grunt的精华,而改进了Grunt不那么好的地方。
Node.js之后又会出现什么呢?我也不知道。现在,有些项目在视图分一杯羹,但要说哪个项目能够成功,还为时过早。比如,JXCore就是这样的一个项目,它是Node.js的一个替代品,它能够处理多线程和线程恢复,它正是试图寻找JavaScript-to-llvm的解决方案。
Web的演化是迅速的,我们的站点和应用从来不会成为经典。只有与时俱进,才能从诸多的竞争者中脱颖而出。向前看,时刻关注哪些东西正在流行,哪些东西将要衰亡,总是有益的。
“一键迁移”
Web组件的好处在于,它并不强制你使用最新的技术,可以继续使用那些你熟悉的技术。
管理依赖
使用Bower安装依赖
让我们开始吧
太简单了吧
Polymer世界中的jQuery
带来了什么
移除jQuery
关于jQuery的结论
总结
第16章 测试Web组件
任何新技术面世后,遇到的最大问题就是如何接入当前已存在的开发链路,并对链路中的其余部分造成最小的影响。开发链路包括一下部分:
- 开发环境(IDE支持,文档)——去哪儿寻求帮助?如何解决问题?
- 可维护性——这项技术是否稳定?是否会发生巨大的变化?使用这项技术编写的代码,与一年前的代码比较,两者是否相互兼容?
- 测试——如何编写单元测试?新技术编写的代码可测试性如何?
- 构建——新技术是否影响构建流程?
- 部署——部署会变化吗?为测量新技术的影响,需要跟踪哪些度量的标准。
Web组件目前最大的问题是缺乏可测试性,常用的测试工具/框架有:
- PhantomJS 1
- PhantomJS 2
- Selenium WebDriver
- Karma
测试用例
运行测试
总结
第17章 打包和发布
Node.js命令行工具
Node.js编写命令行工具的最佳实践遵循“构建优先”模式,该模式规定:工具的功能首先应当是一个可重用的Node.js模块,而命令行接口只包括一个最小的包装,以将模块的接口暴露给命令行,两者的参数基本上一致。
Vulcanize
Gulp
Grunt
Gruntfiles
Grunt任务
注册任务
Grunt配置
使用Bower发布组件
注册组件
总结
第18章 结语
往何处去
Polymer
Mozilla X-Tag
document-register-element
WebComponents.org
CustomElements.io
祝你好运
以上所述就是小编给大家介绍的《Polymer——面向未来的Web组件开发》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 阿里面向分布式服务架构的流量控制组件开源了
- 面向Python,面向对象(基础)
- 面向Python,面向对象(基础3)
- Swift 中的面向协议编程:是否优于面向对象编程?
- <<深入PHP面向对象、模式与实践>>读书笔记:面向对象设计和过程式编程
- Python 面向对象
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从需求到产品:0岁产品经理进阶之道
权莉 / 人民邮电出版社 / 2018-7 / 49.80元
本书主要针对刚入职的初级产品经理,从贴近工作状态的场景切入,对各阶段的知识点进行分类总结,旨在提供一套经过实践检验的产品方法论,为读者从初级产品经理成长为产品经理奠定坚实的基础。 书中提炼的方法和案例涵盖初级产品经理工作的方方面面,从基本技能到思维方式,从需求管理到产品规划定义,从框架选型到流程梳理,从工作模块拆解到案例剖析,用具体且贴合实际工作场景的内容,还原真实的产品工作方法及实践案例,既有方......一起来看看 《从需求到产品:0岁产品经理进阶之道》 这本书的介绍吧!