22
33## 概述
44
5- 各种网站往往需要一些相同的模块,比如日历、调色板等等,这种模块就被称为“组件”(component)。Web Components 就是网页组件式开发的技术规范 。
5+ 各种网站往往需要一些相同的模块,比如日历、调色板等等,这种模块就被称为“组件”(component)。Web Components 就是浏览器原生的组件规范 。
66
7- 采用组件进行网站开发 ,有很多优点。
7+ 采用组件开发 ,有很多优点。
88
9- (1)管理和使用非常容易。加载或卸载组件,只要添加或删除一行代码就可以了。
9+ (1)有利于代码复用。组件是模块化编程思想的体现,可以跨平台、跨框架使用,构建、部署和与其他 UI 元素互动都有统一做法。
10+
11+ (2)使用非常容易。加载或卸载组件,只要添加或删除一行代码就可以了。
1012
1113``` html
1214<link rel =" import" href =" my-dialog.htm" >
1315<my-dialog heading =" A Dialog" >Lorem ipsum</my-dialog >
1416```
1517
16- 上面代码加载了一个对话框组件。
17-
18- (2)定制非常容易。组件往往留出接口,供使用者设置常见属性,比如上面代码的heading属性,就是用来设置对话框的标题。
19-
20- (3)组件是模块化编程思想的体现,非常有利于代码的重用。标准格式的模块,可以跨平台、跨框架使用,构建、部署和与其他UI元素互动都有统一做法。
21-
22- (4)组件提供了HTML、CSS、JavaScript封装的方法,实现了与同一页面上其他代码的隔离。
23-
24- 未来的网站开发,可以像搭积木一样,把组件合在一起,就组成了一个网站。这是非常诱人的。
25-
26- Web Components 不是单一的规范,而是一系列的技术组成,包括Template、Custom Element、Shadow DOM、HTML Import四种技术规范。使用时,并不一定这四者都要用到。其中,Custom Element和Shadow DOM最重要,Template和HTML Import只起到辅助作用。
27-
28- ## ` <template> ` 标签
29-
30- ### 基本用法
31-
32- ` <template> ` 标签表示组件的 HTML 代码模板。
33-
34- ``` html
35- <template >
36- <h1 >This won't display!</h1 >
37- <script >alert (" this won't alert!" ); </script >
38- </template >
39- ```
40-
41- ` <template> ` 内部就是正常的 HTML 代码,浏览器不会将这些代码加入 DOM。
42-
43- 下面的代码会将模板内部的代码插入 DOM。
44-
45- ``` javascript
46- let template = document .querySelector (' template' );
47- document .body .appendChild (template .content );
48- ```
49-
50- 注意,模板内部的代码只能插入一次,如果第二次执行上面的代码就会报错。
51-
52- 如果需要多次插入模板,可以复制` <template> ` 内部代码,然后再插入。
53-
54- ``` javascript
55- document .body .appendChild (template .content .cloneNode (true ));
56- ```
57-
58- 上面代码中,` cloneNode() ` 方法的参数` true ` 表示复制包含所有子节点。
59-
60- 接受` <template> ` 插入的元素,叫做宿主元素(host)。在` <template> ` 之中,可以对宿主元素设置样式。
61-
62- ``` html
63- <template >
64- <style >
65- :host {
66- background : #f8f8f8 ;
67- }
68- :host (:hover ) {
69- background : #ccc ;
70- }
71- </style >
72- </template >
73- ```
18+ 上面代码中,第一行加载了一个对话框组件,第二行在网页上放置这个组件的实例。
7419
75- ### document.importNode()
20+ (3)开发和定制很方便。组件开发不需要使用框架,只要用原生的语法就可以了。开发好的组件往往留出接口,供使用者设置常见属性,比如上面代码的 ` heading ` 属性,就是用来设置对话框的标题。
7621
77- document.importNode方法用于克隆外部文档的DOM节点 。
22+ (4)组件提供了 HTML、CSS、JavaScript 封装的方法,实现了与同一页面上其他代码的隔离 。
7823
79- ``` javascript
80- var iframe = document .getElementsByTagName (" iframe" )[0 ];
81- var oldNode = iframe .contentWindow .document .getElementById (" myNode" );
82- var newNode = document .importNode (oldNode, true );
83- document .getElementById (" container" ).appendChild (newNode);
84- ```
24+ 未来的网站开发,可以像搭积木一样,把组件合在一起,就组成了一个网站。这种前景是非常诱人的。
8525
86- 上面例子是将iframe窗口之中的节点oldNode,克隆进入当前文档 。
26+ Web Components 不是单一的规范,而是一系列的技术组成,以下是它的四个构成 。
8727
88- 注意,克隆节点之后,还必须用appendChild方法将其加入当前文档,否则不会显示。换个角度说,这意味着插入外部文档节点之前,必须用document.importNode方法先将这个节点准备好。
28+ - Custom Elements
29+ - Template
30+ - Shadow DOM
31+ - HTML Import
8932
90- document.importNode方法接受两个参数,第一个参数是外部文档的DOM节点,第二个参数是一个布尔值,表示是否连同子节点一起克隆,默认为false。大多数情况下,必须显式地将第二个参数设为true 。
33+ 使用时,并不一定上面四种 API 都要用到。其中,Custom Element 和 Shadow DOM 比较重要,Template 和 HTML Import 只起到辅助作用 。
9134
9235## Custom Element
9336
94- HTML 预定义的网页元素,有时并不符合我们的需要,这时可以自定义网页元素,这就叫做 Custom Element。它是 Web component 技术的核心。举例来说,你可以自定义一个叫做` <super-button> ` 的网页元素。
37+ HTML 预定义的网页元素,有时并不符合我们的需要,这时可以自定义网页元素,这就叫做 Custom Element。简单说,它就是用户自定义的网页元素,是 Web components 技术的核心。
38+
39+ 举例来说,你可以自定义一个叫做` <super-button> ` 的网页元素。
9540
9641``` html
97- <super-button ></super-button >
42+ <my-element ></my-element >
9843```
9944
100- 注意,自定义网页元素的标签名必须含有连字符` - ` ,一个或多个都可 。这是因为浏览器内置的的 HTML 元素标签名,都不含有连字符,这样可以做到有效区分。
45+ 注意,自定义网页元素的标签名必须含有连字符` - ` ,一个或多个连字符都可以 。这是因为浏览器内置的的 HTML 元素标签名,都不含有连字符,这样可以做到有效区分。
10146
10247下面的代码先定义一个自定义元素的类。
10348
10449``` javascript
10550class MyElement extends HTMLElement {}
10651```
10752
108- 然后,需要登记一下自定义元素与这个类之间的映射 。
53+ 然后,` window.customElements.define() ` 方法,用来登记自定义元素与这个类之间的映射 。
10954
11055``` javascript
111- customElements .define (' my-element' , MyElement);
56+ window . customElements .define (' my-element' , MyElement);
11257```
11358
114- 登记以后,页面上的每一个` <my-element> ` 元素都是一个` MyElement ` 的实例 。只要浏览器解析到` <my-element> ` 元素,就会运行` MyElement ` 的构造函数。
59+ 登记以后,页面上的每一个` <my-element> ` 元素都是一个` MyElement ` 类的实例 。只要浏览器解析到` <my-element> ` 元素,就会运行` MyElement ` 的构造函数。
11560
116- 自定义元素的类有一些自定义的生命周期方法 。
61+ 自定义元素的类( ` MyElement ` )有一些自定义的生命周期方法 。
11762
118- - ` connectedCallback() ` :自定义元素添加到页面时调用。这可能不止一次发生,比如元素被移除并重新添加。
119- - ` disconnectedCallback() ` :自定义元素移除出页面时调用。
120- - ` attributeChangeCallback() ` :修改白名单里面的自定义元素属性时触发。
63+ - ` connectedCallback() ` :自定义元素添加到页面(进入 DOM 树)时调用。这可能不止一次发生,比如元素被移除后又重新添加。类的设置应该尽量放到这个方法里面执行,因为这时各种属性和子元素都可用。
64+ - ` disconnectedCallback() ` :自定义元素移出 DOM 时执行。
65+ - ` adoptedCallback() ` :` document.adoptNode(element) ` 时执行。
66+ - ` attributeChangeCallback() ` :加入` observedAttributes ` 白名单的属性发生属性值变化时触发。
12167
12268下面是一个例子。
12369
@@ -153,6 +99,25 @@ customElements.define('hey-there', GreetingElement);
15399<hey-there name =" Potch" >Personalized Greeting</hey-there >
154100```
155101
102+ 生命周期方法调用的顺序如下:` constructor ` -> ` attributeChangedCallback ` -> ` connectedCallback ` 。
103+
104+ ``` javascript
105+ class MyElement extends HTMLElement {
106+ constructor () {
107+ this .container = this .shadowRoot .querySelector (' #container' );
108+ }
109+ attributeChangedCallback (attr , oldVal , newVal ) {
110+ if (attr === ' disabled' ) {
111+ if (this .hasAttribute (' disabled' ) {
112+ this .container .style .background = ' #808080' ;
113+ } else {
114+ this .container .style .background = ' #ffffff' ;
115+ }
116+ }
117+ }
118+ }
119+ ` ` `
120+
156121如果你想扩展现有的 HTML 元素(比如` < button> ` )也是可以的。
157122
158123` ` ` javascript
@@ -171,9 +136,21 @@ customElements.define('hey-there', GreetingElement, { extends: 'button' });
171136< button is= " hey-there" name= " World" > Howdy< / button>
172137` ` `
173138
139+ 除了直接插入网页,使用脚本插入网页也是可以的。
140+
141+ ` ` ` javascript
142+ window .customElements .define (
143+ ' my-element' ,
144+ class extends HTMLElement {... }
145+ );
146+ const el = window .customElements .get (' my-element' );
147+ const myElement = new el (); // same as document.createElement('my-element');
148+ document .body .appendChild (myElement);
149+ ` ` `
150+
174151### document.registerElement()
175152
176- 使用自定义元素前,必须用document对象的registerElement方法登记该元素 。该方法返回一个自定义元素的构造函数。
153+ 使用自定义元素前,必须用 ` document . registerElement () ` 方法登记该元素 。该方法返回一个自定义元素的构造函数。
177154
178155` ` ` javascript
179156var SuperButton = document .registerElement (' super-button' );
@@ -341,6 +318,70 @@ var XFoo = document.registerElement('x-foo-with-markup',
341318
342319```
343320
321+ ## `<template>`标签
322+
323+ ### 基本用法
324+
325+ `<template>`标签表示组件的 HTML 代码模板。
326+
327+ ```html
328+ <template>
329+ <h1>This won' t display! < / h1>
330+ < script> alert (" this won't alert!" );< / script>
331+ < / template>
332+ ` ` `
333+
334+ ` < template> ` 内部就是正常的 HTML 代码,浏览器不会将这些代码加入 DOM。
335+
336+ 下面的代码会将模板内部的代码插入 DOM。
337+
338+ ` ` ` javascript
339+ let template = document .querySelector (' template' );
340+ document .body .appendChild (template .content );
341+ ` ` `
342+
343+ 注意,模板内部的代码只能插入一次,如果第二次执行上面的代码就会报错。
344+
345+ 如果需要多次插入模板,可以复制` < template> ` 内部代码,然后再插入。
346+
347+ ` ` ` javascript
348+ document .body .appendChild (template .content .cloneNode (true ));
349+ ` ` `
350+
351+ 上面代码中,` cloneNode ()` 方法的参数` true ` 表示复制包含所有子节点。
352+
353+ 接受` < template> ` 插入的元素,叫做宿主元素(host)。在` < template> ` 之中,可以对宿主元素设置样式。
354+
355+ ` ` ` html
356+ < template>
357+ < style>
358+ : host {
359+ background: #f8f8f8;
360+ }
361+ : host (: hover ) {
362+ background: #ccc;
363+ }
364+ < / style>
365+ < / template>
366+ ` ` `
367+
368+ ### document.importNode()
369+
370+ document.importNode方法用于克隆外部文档的DOM节点。
371+
372+ ` ` ` javascript
373+ var iframe = document .getElementsByTagName (" iframe" )[0 ];
374+ var oldNode = iframe .contentWindow .document .getElementById (" myNode" );
375+ var newNode = document .importNode (oldNode, true );
376+ document .getElementById (" container" ).appendChild (newNode);
377+ ` ` `
378+
379+ 上面例子是将iframe窗口之中的节点oldNode,克隆进入当前文档。
380+
381+ 注意,克隆节点之后,还必须用appendChild方法将其加入当前文档,否则不会显示。换个角度说,这意味着插入外部文档节点之前,必须用document.importNode方法先将这个节点准备好。
382+
383+ document.importNode方法接受两个参数,第一个参数是外部文档的DOM节点,第二个参数是一个布尔值,表示是否连同子节点一起克隆,默认为false。大多数情况下,必须显式地将第二个参数设为true。
384+
344385## Shadow DOM
345386
346387所谓 Shadow DOM 指的是,浏览器将模板、样式表、属性、JavaScript 码等,封装成一个独立的 DOM 元素。外部的设置无法影响到其内部,而内部的设置也不会影响到外部,与浏览器处理原生网页元素(比如` < video> ` 元素)的方式很像。Shadow DOM 最大的好处有两个,一是可以向用户隐藏细节,直接提供组件,二是可以封装内部样式表,不会影响到外部。
@@ -799,4 +840,5 @@ template标签定义了网页元素的模板。
799840- Eric Bidelman, [HTML Imports](http://www.html5rocks.com/en/tutorials/webcomponents/imports/)
800841- TJ VanToll, [Why Web Components Are Ready For Production](http://developer.telerik.com/featured/web-components-ready-production/)
801842- Chris Bateman, [A No-Nonsense Guide to Web Components, Part 1: The Specs](http://cbateman.com/blog/a-no-nonsense-guide-to-web-components-part-1-the-specs/)
843+ - [Web Components will replace your frontend framework](https://blog.usejournal.com/web-components-will-replace-your-frontend-framework-3b17a580831c), Danny Moerkerke
802844
0 commit comments