Web Components 入门实例教程

来源 | http://www.ruanyifeng.com/blog/2019/08/web_components.html

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

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

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

1、自定义元素

下图是一个用户卡片。

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

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

<user-card></user-card>

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

2、customElements.define()

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

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

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

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

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

3、自定义元素的内容

自定义元素<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 = 'yourmail@some-email.com';
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结构就已经生成了。

4、<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">yourmail@some-email.com</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>

5、添加样式

自定义元素还没有样式,可以给它指定细分样式,类似下面这样。

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">yourmail@some-email.com</p><button class="button">Follow</button></div></template>

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

6、自定义元素的参数

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

<user-cardimage="https://semantic-ui.com/images/avatar2/large/kristy.png"name="User Name"email="yourmail@some-email.com"></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);   

7、影子DOM

我们不希望用户能够看到<user-card>的内部代码,Web组件允许内部代码隐藏起来,这叫做Shadow DOM,即这部分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组件组件就完成了,完整代码可以访问这里。可以看到,整个过程还是很简单的,不像第三方框架那样有复杂的API。

8、组件的扩展

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

(1)与用户互动

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

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

(2)组件的封装

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

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

  • Web组件初学者教程

  • 自定义元素v1:可重用的Web组件

九,参考链接

  • Web组件剖析,Uday Hiwarale

(完)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值