Web Components 入门实例教程

标签: JavaScript | 发表时间:2019-08-06 17:39 | 作者:
出处:http://www.ruanyifeng.com/blog/

组件是前端的发展方向,现在流行的 React 和 Vue 都是组件框架。

谷歌公司由于掌握了 Chrome 浏览器,一直在推动浏览器的原生组件,即 Web Components API。相比第三方框架,原生组件简单直接,符合直觉,不用加载任何外部模块,代码量小。目前,它还在不断发展,但已经可用于生产环境。

Web Components API 内容很多,本文不是全面的教程,只是一个简单演示,让大家看一下怎么用它开发组件。

一、自定义元素

下图是一个用户卡片。

本文演示如何把这个卡片,写成 Web Components 组件,这里是最后的 完整代码

网页只要插入下面的代码,就会显示用户卡片。

   
<user-card></user-card>

这种自定义的 HTML 标签,称为自定义元素(custom element)。根据规范,自定义元素的名称必须包含连词线,用与区别原生的 HTML 元素。所以, <user-card>不能写成 <usercard>

二、 customElements.define()

自定义元素需要使用 JavaScript 定义一个类,所有 <user-card>都会是这个类的实例。

   
class UserCard extends HTMLElement {
  constructor() {
    super();
  }
}

上面代码中, UserCard就是自定义元素的类。注意,这个类的父类是 HTMLElement,因此继承了 HTML 元素的特性。

接着,使用浏览器原生的 customElements.define()方法,告诉浏览器 <user-card>元素与这个类关联。

   
window.customElements.define('user-card', UserCard);

三、自定义元素的内容

自定义元素 <user-card>目前还是空的,下面在类里面给出这个元素的内容。

   
class UserCard extends HTMLElement {
  constructor() {
    super();

    var image = document.createElement('img');
    image.src = 'https://semantic-ui.com/images/avatar2/large/kristy.png';
    image.classList.add('image');

    var container = document.createElement('div');
    container.classList.add('container');

    var name = document.createElement('p');
    name.classList.add('name');
    name.innerText = 'User Name';

    var email = document.createElement('p');
    email.classList.add('email');
    email.innerText = '[email protected]';

    var button = document.createElement('button');
    button.classList.add('button');
    button.innerText = 'Follow';

    container.append(name, email, button);
    this.append(image, container);
  }
}

上面代码最后一行, this.append()this表示自定义元素实例。

完成这一步以后,自定义元素内部的 DOM 结构就已经生成了。

四、 <template>标签

使用 JavaScript 写上一节的 DOM 结构很麻烦,Web Components API 提供了 <template>标签,可以在它里面使用 HTML 定义 DOM。

   
<template id="userCardTemplate">
  <img src="https://semantic-ui.com/images/avatar2/large/kristy.png" class="image">
  <div class="container">
    <p class="name">User Name</p>
    <p class="email">[email protected]</p>
    <button class="button">Follow</button>
  </div>
</template>

然后,改写一下自定义元素的类,为自定义元素加载 <template>

   
class UserCard extends HTMLElement {
  constructor() {
    super();

    var templateElem = document.getElementById('userCardTemplate');
    var content = templateElem.content.cloneNode(true);
    this.appendChild(content);
  }
}  

上面代码中,获取 <template>节点以后,克隆了它的所有子元素,这是因为可能有多个自定义元素的实例,这个模板还要留给其他实例使用,所以不能直接移动它的子元素。

到这一步为止,完整的代码如下。

   
<body>
  <user-card></user-card>
  <template>...</template>

  <script>
    class UserCard extends HTMLElement {
      constructor() {
        super();

        var templateElem = document.getElementById('userCardTemplate');
        var content = templateElem.content.cloneNode(true);
        this.appendChild(content);
      }
    }
    window.customElements.define('user-card', UserCard);    
  </script>
</body>

五、添加样式

自定义元素还没有样式,可以给它指定全局样式,比如下面这样。

   
user-card {
  /* ... */
}

但是,组件的样式应该与代码封装在一起,只对自定义元素生效,不影响外部的全局样式。所以,可以把样式写在 <template>里面。

   
<template id="userCardTemplate">
  <style>
   :host {
     display: flex;
     align-items: center;
     width: 450px;
     height: 180px;
     background-color: #d4d4d4;
     border: 1px solid #d5d5d5;
     box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
     border-radius: 3px;
     overflow: hidden;
     padding: 10px;
     box-sizing: border-box;
     font-family: 'Poppins', sans-serif;
   }
   .image {
     flex: 0 0 auto;
     width: 160px;
     height: 160px;
     vertical-align: middle;
     border-radius: 5px;
   }
   .container {
     box-sizing: border-box;
     padding: 20px;
     height: 160px;
   }
   .container > .name {
     font-size: 20px;
     font-weight: 600;
     line-height: 1;
     margin: 0;
     margin-bottom: 5px;
   }
   .container > .email {
     font-size: 12px;
     opacity: 0.75;
     line-height: 1;
     margin: 0;
     margin-bottom: 15px;
   }
   .container > .button {
     padding: 10px 25px;
     font-size: 12px;
     border-radius: 5px;
     text-transform: uppercase;
   }
  </style>

  <img src="https://semantic-ui.com/images/avatar2/large/kristy.png" class="image">
  <div class="container">
    <p class="name">User Name</p>
    <p class="email">[email protected]</p>
    <button class="button">Follow</button>
  </div>
</template>

上面代码中, <template>样式里面的 :host伪类,指代自定义元素本身。

六、自定义元素的参数

<user-card>内容现在是在 <template>里面设定的,为了方便使用,把它改成参数。

   
<user-card
  image="https://semantic-ui.com/images/avatar2/large/kristy.png"
  name="User Name"
  email="[email protected]"
></user-card>

<template>代码也相应改造。

   
<template id="userCardTemplate">
  <style>...</style>

  <img class="image">
  <div class="container">
    <p class="name"></p>
    <p class="email"></p>
    <button class="button">Follow John</button>
  </div>
</template>

最后,改一下类的代码,把参数加到自定义元素里面。

   
class UserCard extends HTMLElement {
  constructor() {
    super();

    var templateElem = document.getElementById('userCardTemplate');
    var content = templateElem.content.cloneNode(true);
    content.querySelector('img').setAttribute('src', this.getAttribute('image'));
    content.querySelector('.container>.name').innerText = this.getAttribute('name');
    content.querySelector('.container>.email').innerText = this.getAttribute('email');
    this.appendChild(content);
  }
}
window.customElements.define('user-card', UserCard);    

七、Shadow DOM

我们不希望用户能够看到 <user-card>的内部代码,Web Component 允许内部代码隐藏起来,这叫做 Shadow DOM,即这部分 DOM 默认是隐藏的,开发者工具里面看不到。

自定义元素的 this.attachShadow()方法开启 Shadow DOM,详见下面的代码。

   
class UserCard extends HTMLElement {
  constructor() {
    super();
    var shadow = this.attachShadow( { mode: 'closed' } );

    var templateElem = document.getElementById('userCardTemplate');
    var content = templateElem.content.cloneNode(true);
    content.querySelector('img').setAttribute('src', this.getAttribute('image'));
    content.querySelector('.container>.name').innerText = this.getAttribute('name');
    content.querySelector('.container>.email').innerText = this.getAttribute('email');

    shadow.appendChild(content);
  }
}
window.customElements.define('user-card', UserCard);

上面代码中, this.attachShadow()方法的参数 { mode: 'closed' },表示 Shadow DOM 是封闭的,不允许外部访问。

至此,这个 Web Component 组件就完成了,完整代码可以访问 这里。可以看到,整个过程还是很简单的,不像第三方框架那样有复杂的 API。

八、组件的扩展

在前面的基础上,可以对组件进行扩展。

(1)与用户互动

用户卡片是一个静态组件,如果要与用户互动,也很简单,就是在类里面监听各种事件。

   
this.$button = shadow.querySelector('button');
this.$button.addEventListener('click', () => {
  // do something
});

(2)组件的封装

上面的例子中, <template>与网页代码放在一起,其实可以用脚本把 <template>注入网页。这样的话,JavaScript 脚本跟 <template>就能封装成一个 JS 文件,成为独立的组件文件。网页只要加载这个脚本,就能使用 <user-card>组件。

这里就不展开了,更多 Web Components 的高级用法,可以接着学习下面两篇文章。

九、参考链接

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名( 创意共享3.0许可证
  • 发表日期: 2019年8月 6日

相关 [web components 实例] 推荐:

Web Components 入门实例教程

- - 阮一峰的网络日志
组件是前端的发展方向,现在流行的 React 和 Vue 都是组件框架. 谷歌公司由于掌握了 Chrome 浏览器,一直在推动浏览器的原生组件,即 Web Components API. 相比第三方框架,原生组件简单直接,符合直觉,不用加载任何外部模块,代码量小. 目前,它还在不断发展,但已经可用于生产环境.

Web未死

- Sinan - GeekPark 捕风捉影
App的极限已经浮现,而Web则是突破此极限,推动下一个数字时代革命的起点. 距离美国《连线》杂志发表《Web已死,互联网永生》这篇文章还不到一年的时间,业界为Web平反的声音渐起. 2010年1月,苹果发布iPad,紧随其后在6月又发布了iPhone4. 没有人质疑过苹果的iTunes+App的商业模式,App可谓如日中天.

web的演变

- 酿泉 - 前端观察
这是一个基于GAE的项目,有mgmt design、GOOD、Hyperakt和Vizzuality开发,也有Google chrome团队的参与,记录了浏览器与互联网技术的演变. 不多介绍,直接去看看吧:Evolution Of Web. 值得一提的是,这个项目的代码很不错,值得学习一下.

Web Service入门

- - 博客 - 伯乐在线
本文来自文章作者 @Jeremy黄国华 的投稿. 伯乐在线也欢迎其他朋友投稿,投稿时记得留下您的新浪微博账号哦~. 目前对Web Service没有统一的定义,定义一:Web Service是自包含的、模块化的应用程序,它可以在Web中被描述、发布、查找以及调用. 定义二:Web Service是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得Web Service能与其他兼任的组件进行操作.

Web Apps来袭

- - HTML5研究小组
如同历史上任何一次互联网基础标准的变化都会在随后几年中带来应用创新的大爆发一样,当HTML5在2011年逐渐被主流厂商所接受之后,围绕Web Apps领域的创新风暴正山雨欲来. 2012年1月12日,老牌传媒集团《金融时报》(Financial Times,以下简称FT)宣布收购为其开发移动Web App的研发公司Assanka ,这样,FT将不再以外包的形式雇佣Assanka为其打造移动Web App,而可以直接让它在内部进行开发.

Google 的 Web Desinger

- - 极客公园-GeekPark
[核心提示]Google 的免费 Web 设计工具虽然现在主要目的是为广告设计,今后会不会成为 Chrome 应用的开发工具. 听到 Google 推出了一个名为 Google Web Designer 的网页设计还有点惊讶. 虽然 Google 是 Web 技术的大力倡导者,毕竟自己严重依赖这个平台,但市面上相关的产品太多了,从专业的开发工具到小白的所见即所得软件数不胜数,还有 Adobe 这个专业玩家.

Chrome 的 Web Intents 会改变 Web 吗?

- hailin - 爱范儿 · Beats of Bits
2011年8月4日,Chrome 团队宣布将支持一个新的技术—— Web Intents. 这个技术未来可能会极大的影响网络应用和浏览器. 什么是 Web Intents. 如果您用过 Android 手机可能就会对这个技术有所了解. Android Intents 可以让两个独立的程序之前通信互相,神奇的是这两个程序中的任何一个程序可能不知道它在和谁通信.

[Web] 連結分享

- yasy - 網站製作學習誌
关于做PHP扩展开发的一些资源. 我对PHP5.4的一个改进. schema-database – 查詢結果與 PDO::FETCH_CLASS. 讓AJAX動態內容支援瀏覽器回上頁功能. LESS介紹及其與Sass的差異. 網頁設計該用哪種字級單位:px、em或rem. IE10将增强对HTML5和CSS3的支持.

艺术与web:线

- 南风 - 译言-每日精品译文推荐
来源Art and the Web: Line | Think Vitamin. 初识各类艺术要素与原则时,你或许会以为它们太过简单甚而不屑一顾. 然而,多花些时间,对每一类要素与原则都仔仔细细地沉思默想一番,却是十分重要的. 在学习艺术要素与原则时,这些便是你要问自己的基本问题. 思考一下如何你才能将它们融入到你的创意思维里,如何才能卓有成效地将它们应用于你自己的web项目中.