猪小花1号

个人签名

282篇博客

Webcomponent-overview

猪小花1号2018-12-04 10:51

此文已由作者郑海波授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验


Web Component 是什么?

Web Component 是一系列规范的集合来帮助开发者以更符合标准的方式开发可重用的组件,它们包括:

 

1. [Templates](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/explainer/index.html#template-section)

2. [__*Shadow DOM__](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html)

3. [__*Custom Elements__](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html)

4. [Imports](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html)

5. <del>[Decorators](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/explainer/index.html#decorator-section): 

本次不做介绍</del>

<!-- Web component 跟所谓MVVM、MV*不是对应的关系, Web Component的核心是什么?   -->

> 每一块在组成Web Component时,都有其职责所在, 而在功能上无可取代的是__Custom Element__ 以及 __Shadow Dom__;

--------------


# 举一个例子

> __"Progress bar"__

* 准备工作`chrome://flags`

1. experimental javascript

2. Experimental Web Platform Features

3. HTML Imports


----------------------

 

# nwd-progress——version 1

<p> on <a href='http://codepen.io'>CodePen</a></p>

## 问题:页面上需要书写的初始化结构太多,理想的初始结构是

```xml

<div value=10 max=100></div>

```

> __template!__


----------------


# template

> __惰性的__的模板,用于结构定义

* 使用

```javascript

var template = document.querySelector('template');

document.body.appendChild(template.content.cloneNode(true));

```


* 优势

  1. 与`textarea、script`: 基于dom, 安全、无需解析()

  2. 与`display:none` : 惰性,只在被嵌入文档时才会被"激活"

    > 激活的含义是:当嵌入页面时,才会下载对应资源,运行脚本,样式激活

----------------

 

# nwd-progress——version 2 

<p> on <a href='http://codepen.io'>CodePen</a></p>

1. 利用template来重用结构

2. 利用scoped css 隔离样式(单向、优先级?)

3. 加入了wrap接口,使得外部可以控制

## 问题仍然存在: 我们其实在文档中并不需要展现内部细节,就与`progress`一样

 

----------------

# Shadow Dom

页面上可见(__rendered__)但文档中不可见(__invisible in dom__)的元素

![shadow1](./shadow1.png)

>所谓 __"封装"__ 的核心所在,这块是概念性最强的部分

----------------


## root & host & boundary

```javascript

var h2 = document.createElement('h2'); //host

h2.innerHTML = "哈哈";

var root = h2.createShadowRoot(); // root

root.innerHTML = '<style>p{color: red;}</style>'+

  '<p>我是shadow Root的内容</p>';

```

 

1. host——`h2`: 即shadow dom 的容器节点

2. root: shadow dom 的根节点,一个host只能有一个生效的shadow root;

3. boundary

  1. 节点隔离: 外部无法获取到内部节点

  2. 样式隔离: 1) 外部样式无法影响到内部(可控), 2)内部样式无法影响到外部(不可控)

----------------------------------------


## 单独阐述下distributed Nodes

<p> on <a href='http://codepen.io'>CodePen</a></p>

1. `<content>` : 钩子节点,填入host中的内容

2. select属性 : 钩子节点的选择符,会选择selector对应的节点(默认为`*`)

3. `<shadow>` : 同时可以创建多的shadowroot ,但是只有一个生效,可以通过`<shadow>`占位符来获得`oldShadowRoot`中的内容

> 由于shadow dom 会代替host的原内容完成render。 如果配合distributed Nodes, root与host分别看做展现与内容的关系

------------------------

 

#图解

![多个root](./shadow2.png)

--------------------------

## 关于shadow dom 的其它几个概念简述

1. 选择器——`:host`(原at-rule `@host`)

2. 选择器——`:content`(原`:ditributed()`)

3. 自定义伪元素 —— `pseudo`属性(需要`x-`)

4. event重定向

------------------------------------------

 

## 真实世界的例子

1. video <video src="http://www.w3school.com.cn/i/movie.ogg" controls="controls"></video>

2. audio

<audio controls="controls">

<source src="http://www.w3school.com.cn/i/song.ogg" type="audio/ogg">

<source src="http://www.w3school.com.cn/i/song.mp3" type="audio/mpeg">

</audio>

3. progress <progress></progress>

4. input[type="time"] <input type='time'/>

5. ......

> 许多内建UI封装的机制是与shadow dom一致的


 

---------------

# nwd-progress——version 3

<p> on <a href='http://codepen.io'>CodePen</a></p>

## 仍然存在的问题

1. 接口是以包装器的形式, 直接的属性改动无法反馈到表现

2. 每次都必须进行初始化,并没有集成到文档中

> Custom Elemenets!!!

------------------


# Customer Element

1. 扩展已有标签元素

2. 创建新标签元素

> 这里指的是js、html两个层级的定义

----------------

##注册

* 使用document.create

```javascript

var proto = Object.create(HTMLElement.prototype); //这个取决于你的使用

proto.createdCallback = function() {};

var NwdSlide = document.register('nwd-slide', {

prototype: proto

});

```

* <del>使用element标签注册</del> 已经被废弃

```xml

<element name="nwd-button" extends='button'>

<script>

({tick: function () {… }});

</script>

</element>

```


---------------

 

## custom elements的一些细节阐述

1. 标签的合法性: 必须是`-`分割的连缀形式

  1. `invalid`: `tabs`、`xTags`

  2. `valid`: `x-tag`、`X-Tag` (测试是大小写无关)

2. 使用可以在注册之前(Unresolved elements -> Custom Elements)

3. 可以不传入第二个参数,此时默认为继承HTMLElement

--------------------------------

## 使用

* 直接在页面写入

```xml

<nwd-button></nwd-button>

<!-- 或 -->

<button is='nwd-button'></button>

```

* 你当然也可以通过js动态创建

```javascript

// 1

var instance = document.createElement('nwd-button');

// 2

var instance = new NwdButton();

// 3

document.body.innerHTML = '<nwd-button id='mybutton'></nwd-button>'

var instance = document.querySelector('#mybutton');

```

>__它们是完全等价的!__


------------------

 

## 生命周期

1. `createdCallback()`: 当元素实例被创建时

2. `enteredViewCallback()`: 当元素添加到文档时

3. `leftViewCallback()`: 当元素从文档移除时

4. `attributeChangedCallback(attrName, oldVal, newVal)`:

  当元素的属性该表时(注意,innerHTML、textContent的改变无法触发)

-------------------

 

# nwd-progress: __step 4__

<p> on <a href='http://codepen.io'>CodePen</a></p>

<p >一点点小瑕疵,样式、脚本、html混杂在页面中,让html imports做最后的收尾工作吧</p>

------------------

 

# Html Imports

> fetch this document so I can use it later

```xml

<head>

<link rel="import" href="./nwd-progress.html">

</head>

<body>

<nwd-progress max=2 value=.5></nwd-progress>

</body>

```


----------------------

 

##import带来了什么?

<p >js和css、html等资源的加载依赖模式都是分离的,现在可以以一定的规则打包成一类资源,提供统一的url,比如bootstrap, 我们可以这么做</p>

```xml

<link rel="stylesheet" href="bootstrap.css">

<link rel="stylesheet" href="fonts.css">

<script src="jquery.js"></script>

<script src="bootstrap.js"></script>

<script src="bootstrap-tooltip.js"></script>

<script src="bootstrap-dropdown.js"></script>

<!-- scaffolding markup -->

<template>

...

</template>

<script>

// lib initialize

</script>

```


----------------------

 

## html imports的简要阐述

1. 有跨域限制(CORS-enabled needed)

2. 会为import单独开辟html parser,而js是__按序执行__(并且defer)——无法通过ajax来完全模拟

3. 可以为一整套资源提供一个统一的资源入口(url): 想象下bootstrap

4. import的文档有自己的文档环境——`document.currentScript.ownerDocument;`

5. JS的执行共用一套执行环境,即import的文档的`document`即全局`document`

6. 你可以通过`link.imports`来获得加载的文档对象,但是有几种情况为null:

  1. 不支持 html imports

  2. rel 不等于 'import'

  3. link 从文档被移除(即必须保留)

  4. 资源未加载成功

7. imports中仍然可以包含另一个imports

 

-----------------------------

 

# nwd-progress: __step 5__

<nwd-progress id='progress' max=2 value=.5></nwd-progress>

直接修改属性也可以实时生效!

```

<nwd-progress id='progress' max=2 value=.5></nwd-progress>

```

-----------------------------

 

#Web Component的优势

> 按我理解的先后顺序

1. __生命周期__(想想angular的那些 compile、link的概念)

2. 标准,与目前一些标准节点的类似实现方式

3. 封装&&模块化

4. 简单、便于使用

---------------------------

 

#Web Component的劣势

1. 兼容性: Polymer解决不了所有问题,还是寄托国内的xp用户升级系统吧。

2. 草案变化快,实时资料很难找,目前紧跟Polymer社区观望是最好的选择。

3. 目前还没有与数据绑定相关的较稳定草案(Polymer: MDV -> Node.bind -> )

4. 带来了一些问题:比如import的对页面的首屏的影响,shadow dom的封装的严格性与可控性的平衡(这块其实一直在变化)

-------------------------------

 

# 最后看看这个slide的源码

```xml

<!DOCTYPE html>

<html >

<head>

<meta charset="UTF-8">

<title>Web Components——getting start</title>

<link rel="import" href="./nwd-slideshow/nwd-slideshow.html">

<link rel="import" href="./nwd-progress.html">

</head>

<body>

<nwd-slideshow src='ppt.txt'></nwd-slideshow>

</body>

</html>

```


-------------------------------

 

#资源列表

1. [gist: Web Components Resources](https://gist.github.com/ebidel/6314025)

2. [shadow dom: the basics](http://robdodson.me/blog/2013/08/27/shadow-dom-the-basics/)

3. [shadow dom-301](http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-301/)

4. [shadow dom-101](http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/)

5. [shadow dom-201](http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/)

6. [web components: why youre alreay on expect](http://markdalgleish.com/2013/11/web-components-why-youre-already-an-expert/)

7. [html imports](http://robdodson.me/blog/2013/08/20/exploring-html-imports/)

8. 资源永远都要看最新的...

----------------------------

 

# Q && A

----------------------

# Next Topic: Polymer——Overview



免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击