拥抱 Web Components

——入门、实战与持续更新

@2018-09-16

@西安 Web 前端交流大会

freeCodeCamp 2018 Top Contributor,开源爱好者,参与、组织过多次开源技术社区活动,目前在滴滴进行前端岗位的实习。

Try to create “webcomponents-china” organization

奇异目录

  • 零、进击的前端框架,加速前端组件化进程
  • 一、Web Components 标准呼之欲出
  • A - Custom Elements
  • B - HTML Imports
  • C - HTML Templates
  • D - Shadow DOM
  • THX & Q & A
* 本场分享定义的难度为初级——概念型。

* 本场分享希望达到的效果:Web Components 背景与概念过一遍,持续关注线上的更新。

* 本场分享参考了大量的资料。

零、进击的前端框架,加速前端组件化进程

0-1 前后端分离的进程

① 前后端分离,REST 架构发展

② 传统开发方式(单体架构):

build 项目构建(webpack)相关代码
config 配置目录,包括端口号等。我们初学可以使用默认的。
node_modules npm 加载的项目依赖模块
src
这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:
assets: 放置一些图片,如logo等。
components: 目录里面放了一个组件文件,可以不用。
App.vue: 项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。
main.js: 项目的核心文件。
static 静态资源目录,如图片、字体等。
test 初始测试目录,可删除
.xxxx文件 这些是一些配置文件,包括语法配置,git配置等。
index.html 首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
package.json 项目配置文件。
README.md 项目的说明文档,markdown 格式

软件系统是整块应用,效率低、维护成本高、复杂度随业务增长呈指数级增长

③ 热插拔式化繁为简:

前端    ->    组件化

后端    ->    微服务

行业性实践

...微服务

0-2 前端组件化发展的几个阶段

1. 交互少的静态页面时期:公共 JS 模块公共 CSS

2. 早期动态页面时期:动态引入
  * 由于静态页面不能在页面上存储数据,需要一种技术能把交互的动态数据展现起来。
  * 于是出现了很多服务端技术,比如 ASP,JSP。这些技术的出现使得前端页面活起来了,用户可以根据自己的需求进行数据的交互。
  * 此时页面逻辑逐步复杂,也被公用出来:比如公共的页面头(header)和尾(footer)以及数据库的连接(DatabaseConn.jsp)等。

0-2 前端组件化发展的几个阶段

3. 后端为主的 MVC 时期:Tag 标签
  * 早期动态页面时期的业务逻辑都写在页面上,随着逻辑的增多,页面越来越复杂
  * 以 servlet 为代表的 MVC 时代逐渐登上历史舞台,这时页面上的逻辑都被转入到 servlet 中,使得 View 层的表现更加简洁,也更加的易于阅读,从而达到了开发的分层。
  * 随着 Struts 以及 Spring 的出现,MVC 的开发方式达到鼎盛时期,前端 View 层的展现也变得越来越简单,没有了复杂的业务逻辑,前端的组件化方式主要是 taglib 标签,比如 jsp 标签,Struts 标签等,把 HTML 代码和业务逻辑打包成一个标签,然后使用者直接放置在想要的地方,就可以了。
  * 但这个时期,整个 WEB 应用的开发轻前端重后端,那些 taglib 标签也都是 JAVA 代码编写的。

4. 前端 Ajax 时期:JS 大行其道
  * 服务端动态渲染消耗服务器性能,AJAX 的出现备受欢迎,服务端只用专注提供数据。
  * 这时出现了 JQuery-UI, easy-UI,miniUI 以及大名鼎鼎的 ExtJS。
  * 组件共用起来很复杂。

0-2 前端组件化发展的几个阶段

5. 前端 MV* 时期:自定义组件
  * 想要修改这些(ExtJS,miniUI)JS 框架中的组件是非常困难的,因此开发者希望能够很容易的自定义一些组件,而且可以让开发者将已经存在的组件进行封装。
  * 这时 npm 以及 bower 这些包管理库也出现,开发者可以很容易的将自己开发的组件 publish 到这些库中,在使用时只要把他们下载下来(比如 npm install)就可以直接使用。
  * Backbone、Angular、React、Vue 等框架逐步成为潮流。
6. 以上的组件化基本以 HTML 和 JS 为主,CSS 的组件化——less 和 sass
  * 最早的组件化尝试是把公共的 CSS 样式写成一个个公共 class,但阅读性就变得困难了,也不容易修改。
  * less 和 sass 出现之后,使得 CSS 的编程可以定义变量,可以实现继承,CSS 内容的结构也变得更加清晰。

0-3 前端组件化的四原则

* 标准性:任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件。
​
* 组合性:组件之间应该是可以组合的。

* 重用性:任何一个组件应该都是一个可以独立的个体,可以使其应用在不同的场景中。

* 可维护性:任何一个组件应该都具有一套自己的完整的稳定的功能,仅包含自身的,与其它组件无关的逻辑。

0-4 传统开发与组件化开发

传统开发与组件化开发:

* 对比(标准性、阅读性、复用性、代码冗余、开发效率、维护成本):
  * 传统前端开发:(难统一、难、难、多、低、高)

  * 组件化开发:(统一、易、易、基本没冗余、高、低)
* 对前端开发者的要求逐步加深:
  * 传统前端开发:只要求开发者懂 HTML,JS,CSS
  * 组件化开发:要求开发者掌握 less,sass,或者 ES6 等的语法,以及 Webpack,glup 等的前端打包以及构建工具。

* 前端生态逐渐完善
  * jade、less、scss、typeScript 和 webpack 等工具的完善,前端的组件化开发效率已经有了很大的提升。
  * 如果你需要 SEO,React 和 Vue 的 SSR 框架 Next.js 和 Nuxt.js 更是提供了开箱即用的集成方案,也使开发“同构页面系统“(Google It)变得更加简单。

0-5 从共识到通用的标准

组件化的开发一直是有共识,但没有一个通用的标准。

Web Components 标准呼之欲出

一、Web Components 标准呼之欲出

Web Component 就是网页组件式开发的技术规范。
* 管理和使用非常容易。加载或卸载组件,只要添加或删除一行代码就可以了。

* 定制非常容易。组件往往留出接口,供使用者设置常见属性,比如上面代码的 heading 属性,就是用来设置对话框的标题。

* 组件是模块化编程思想的体现,非常有利于代码的重用。标准格式的模块,可以跨平台、跨框架使用,构建、部署和与其他UI元素互动都有统一做法。

* 组件提供了 HTML、CSS、JavaScript 封装的方法,实现了与同一页面上其他代码的隔离。
Web Components 不是单一的规范,而是一系列的技术组成,包括

Template

Custom Element

Shadow DOM

HTML Import

四种技术规范。使用时,并不一定这四者都要用到。其中,Custom Element 和 Shadow DOM 最重要,Template 和 HTML Import 只起到辅助作用。

A - Custom Elements

浏览器将自定义元素保留在 DOM 之中,但不会任何语义。除此之外,自定义元素与标准元素都一致。

 

事实上,浏览器提供了一个HTMLUnknownElement,HTMLElement对象,所有自定义元素都是该对象的实例。

var tabs=document.createElement("tabs");
console.log(tabs instanceof HTMLUnknownElement);//true
console.log(tabs instanceof HTMLElement);//true

Custom Elements 标准:“自定义元素的名字必须包含一个破折号(-)
一旦名字之中使用了破折号,自定义元素就不是HTMLUnknownElement的实例了。

var tabs=document.createElement("my-tabs");
console.log(tabs instanceof HTMLUnknownElement);//false
console.log(tabs instanceof HTMLElement);//true

document.createElement()

Custom Elements 标准规定了,自定义元素的定义可以使用 ES6 的class语法

<my-element content="Custom Element"></my-element>
class MyElement extends HTMLElement {//自定义元素的定义可以使用ES6的class语法
  get  content() {
    return this.getAttribute('content');
  }
 
  set  content(val) {
    this.setAttribute('content', val);
  }
}

// 原生的window.customElements对象的define方法用来定义 Custom Element。
// 该方法接受两个参数,第一个参数是自定义元素的名字,第二个参数是一个 ES6 的class。
window.customElements.define('my-element', MyElement);

window.onload=function(){//在页面元素加载完之后,才执行
function customTag(tagName, fn){//Array.from([arguments]);可以将字符串,数组,类数组集合转化为数组
  Array
    .from(document.getElementsByTagName(tagName))
    .forEach(fn);
}
function myElementHandler(element) {
  element.textContent = element.content;
}
customTag('my-element', myElementHandler);
};

另一个简单的例子

<my-element content="Custom Element"></my-element>
  window.onload = function() {
            function customTag(tagName, fn){
            console.log(document.getElementsByTagName("div"));
            Array .from(document.getElementsByTagName(tagName)).forEach(fn);
             }
 
            function greetingHandler(element) {
              element.innerHTML = '你好,世界';
            }  
            customTag('greeting', greetingHandler);
    }
<greeting>Hello World</greeting>
<greeting>Hello World</greeting>
<greeting>Hello World</greeting>

<style>
    greeting{
        display:block;
         font-size:36px;
        color:red;
    }
  </style>

B - HTML Imports

C - HTML Templates

C-1 概念

<template>元素,基本上可以确定是2013年才出现的,见名知意,模板元素,

C-2 template 标签实战

function supportsTemplate() {
  return 'content' in document.createElement('template');
}

if (supportsTemplate()) {
  // 支持
} else {
  // 不支持
}

检测浏览器是否支持 template

创建一个 template

<template id="profileTemplate">
  <div class="profile">
    <img src="" class="profile__img">
    <div class="profile__name"></div>
    <div class="profile__social"></div>
  </div>
</template>
var template = document.querySelector('#profileTemplate');
template.content.querySelector('.profile__img').src = 'profile.jpg';
template.content.querySelector('.profile__name').textContent = 'Barack Obama';
template.content.querySelector('.profile__social').textContent = 'Follow me on Twitter';
document.body.appendChild(template.content);

直接修改 template 的模板

创建一个 template

var clone = document.importNode(template.content, true);
document.body.appendChild(clone);

C-2 标签实战

C-3 宿主样式

宿主样式:host:

  • 接受 template 插入的元素,叫做宿主元素(host)。在 template 之中,可以对宿主元素设置样式。
  • 在shadow DOM中利用:host定义宿主的样式,当然用户可以在主文档中覆盖这个样式。
  • :host 是伪类选择器(Pseudo Selector),:host或者 :host(*)是默认给所有的宿主添加样式,或者单独给一个宿主添加样式,即通过:host(x),x可以是宿主的标签或者类选择器等。
  • 另外:host还可以配合:hover、:active等状态来设置样式
  :host {
    background: #f8f8f8;
  }
  :host(:hover) {
    background: #ccc;
  }

::shadow:

  • 原则上来说,影子边界保证主 DOM写的 CSS 选择器和 JavaScript 代码都不会影响到Shadow DOM。
  • 但你可能会想打破影子边界的所谓保证,主文档能够给Shadow DOM添加一些样式,这时可以使用::shadow。

/deep/:

::shadow 选择器的一个缺陷是他只能穿透一层影子边界,如果你在一个影子树中嵌套了多个影子树,那么使用 /deep/ 。

::content

通过 标签把来自主文档并添加到 shadow DOM 的内容被称为分布节点。分布节点的样式渲染需要用到 ::content。即使分布节点为em标签,直接写 em {} 不生效,应该写成::content > em {}。

D - Shadow DOM

THX & Q & A

轻点拍砖。。

Made with Slides.com