如何从零开始学习开发 Web Components 业务组件库?
- 内容介绍
- 文章标签
- 相关推荐
本文共计2693个文字,预计阅读时间需要11分钟。
组件化是前端开发的一个重要趋势,它一方面提高了开发效率,另一方面降低了维护成本。主流的框架如Vue.js、React,以及衍生出的Ant Design、uniapp、Taro等,都是组件化框架的典型代表。Web Components则是一组Web标准,旨在提供一种创建自定义原生态组件的方法。
组件化是前端发展的一个重要方向,它一方面提高开发效率,另一方面降低维护成本。主流的 Vue.js、React 及其延伸的 Ant Design、uniapp、Taro 等都是组件框架。Web Components 是一组 Web 原生 API 的总称,允许我们创建可重用的自定义组件,并在我们 Web 应用中像使用原生 HTML 标签一样使用。目前已经很多前端框架/库支持 Web Components。
本文将带大家回顾 Web Components 核心 API,并从 0 到 1 实现一个基于 Web Components API 开发的业务组件库。
最终效果:blog.pingan8787.com/exe-components/demo.html仓库地址:github.com/pingan8787/Learn-Web-Components
一、回顾 Web Components
在前端发展历史中,从刚开始重复业务到处复制相同代码,到 Web Components 的出现,我们使用原生 HTML 标签的自定义组件,复用组件代码,提高开发效率。通过 Web Components 创建的组件,几乎可以使用在任何前端框架中。
1. 核心 API 回顾
Web Components 由 3 个核心 API 组成:
- 「Custom elements(自定义元素)」:用来让我们定义「自定义元素」及其「行为」,对外提供组件的标签;
- 「Shadow DOM(影子 DOM)」:用来封装组件内部的结构,避免与外部冲突;
- 「HTML templates(HTML 模版)」:包括 <template>和<slot> 元素,让我们可以定义各种组件的 HTML 模版,然后被复用到其他地方,使用过 Vue/React 等框架的同学应该会很熟悉。
另外,还有 HTML imports,但目前已废弃,所以不具体介绍,其作用是用来控制组件的依赖加载。
2. 入门示例
接下来通过下面简单示例快速了解一下「如何创建一个简单 Web Components 组件」。
- 使用组件
<html lang="en">
<head>
<script src="./index.js" defer></script>
</head>
<body>
<h1>custom-element-start</h1>
<custom-element-start></custom-element-start>
</body>
</html>
- 定义组件
* 使用 CustomElementRegistry.define() 方法用来注册一个 custom element
* 参数如下:
* - 元素名称,符合 DOMString 规范,名称不能是单个单词,且必须用短横线隔开
* - 元素行为,必须是一个类
* - 继承元素,可选配置,一个包含 extends 属性的配置对象,指定创建的元素继承自哪个内置元素,可以继承任何内置元素。
*/
class CustomElementStart extends HTMLElement {
constructor(){
super();
this.render();
}
render(){
const shadow = this.attachShadow({mode: 'open'});
const text = document.createElement("span");
text.textContent = 'Hi Custom Element!';
text.style = 'color: red';
shadow.append(text);
}
}
customElements.define('custom-element-start', CustomElementStart)
上面代码主要做 3 件事:
通过实现 CustomElementStart 类来定义组件。
将组件的标签和组件类作为参数,通过 customElements.define 方法定义组件。
导入组件后,跟使用普通 HTML 标签一样直接使用自定义组件 <custom-element-start></custom-element-start>。
随后浏览器访问 index.html 可以看到下面内容:
3. 兼容性介绍
在 MDN | Web Components 章节中介绍了其兼容性情况:
- Firefox(版本63)、Chrome和Opera都默认支持Web组件。
- Safari支持许多web组件特性,但比上述浏览器少。
- Edge正在开发一个实现。
关于兼容性,可以看下图:图片来源:www.webcomponents.org/
这个网站里面,有很多关于 Web Components 的优秀项目可以学习。
4. 小结
这节主要通过一个简单示例,简单回顾基础知识,详细可以阅读文档:
- 使用 custom elements
- 使用 shadow DOM
- 使用 templates and slots
image.png
二、EXE-Components 组件库分析设计
1. 背景介绍
假设我们需要实现一个 EXE-Components 组件库,该组件库的组件分 2 大类:
以「通用简单组件」为主,如exe-avatar头像组件、 exe-button按钮组件等;
以「复杂、组合组件」为主,如exe-user-avatar用户头像组件(含用户信息)、exe-attachement-list附件列表组件等等。
详细可以看下图:
接下来我们会基于上图进行 EXE-Components 组件库设计和开发。
2. 组件库设计
在设计组件库的时候,主要需要考虑以下几点:
当然,这几个是最基础需要考虑的点,随着实际业务的复杂,还需要考虑更多,比如:工程化相关、组件解耦、组件主题等等。
针对前面提到这 3 点,这边约定几个命名规范:
3. 组件库组件设计
这边我们主要设计 exe-avatar 、exe-button 和 exe-user-avatar三个组件,前两个为简单组件,后一个为复杂组件,其内部使用了前两个组件进行组合。这边先定义这三个组件支持的属性:
这边属性命名看着会比较复杂,大家可以按照自己和团队的习惯进行命名。
这样我们思路就清晰很多,实现对应组件即可。
三、EXE-Components 组件库准备工作
本文示例最终将对实现的组件进行「组合使用」,实现下面「「用户列表」」效果:体验地址:blog.pingan8787.com/exe-components/demo.html
1. 统一开发规范
首先我们先统一开发规范,包括:
image.png
image.png
组件开发模版分 index.js「组件入口文件」和 template.js 「组件 HTML 模版文件」:
// index.js 模版const defaultConfig = {
// 组件默认配置
}
const Selector = "exe-avatar"; // 组件标签名
export default class EXEAvatar extends HTMLElement {
shadowRoot = null;
config = defaultConfig;
constructor(){
super();
this.render(); // 统一处理组件初始化逻辑
}
render() {
this.shadowRoot = this.attachShadow({mode: 'closed'});
this.shadowRoot.innerHTML = renderTemplate(this.config);
}
}
// 定义组件
if (!customElements.get(Selector)) {
customElements.define(Selector, EXEAvatar)
}// template.js 模版
export default config => {
// 统一读取配置
const { avatarWidth, avatarRadius, avatarSrc } = config;
return `
<style>
/* CSS 内容 */
</style>
<div class="exe-avatar">
/* HTML 内容 */
</div>
`
}
2. 开发环境搭建和工程化处理
为了方便使用 EXE-Components 组件库,更接近实际组件库的使用,我们需要将组件库打包成一个 UMD 类型的 js 文件。这边我们使用 rollup 进行构建,最终打包成 exe-components.js 的文件,使用方式如下:
<script src="./exe-components.js"></script>接下来通过 npm init -y生成 package.json文件,然后全局安装 rollup 和 zoo.team","level":6,"avatar":"zcy.jpg","home":"7288974"}
]
我们就可以通过简单 for 循环拼接 HTML 片段,然后添加到页面某个元素中:
// 测试生成用户列表模版const usersTemp = () {
let temp = '', code = '';
users.forEach(item {
const {name, desc, level, avatar, home} = item;
temp +=
`
<exe-user-avatar
e-user-name="${name}"
e-sub-name="${desc}"
e-avatar-src="./testAssets/images/users/${avatar}"
e-avatar-width="36px"
e-button-type="primary"
e-button-text="关注"
on-avatar-click="toUserHome('${home}')"
on-button-click="toUserFollow('${name}')"
>
${
level >= 0 && `<span slot="name-slot">
<span class="medal-item">(Lv${level})</span>
</span>`}
</exe-user-avatar>
`
})
return temp;
}
document.querySelector('#app').innerHTML = usersTemp;
到这边我们就实现了一个用户列表的业务,当然实际业务可能会更加复杂,需要再优化。
五、总结
本文首先简单回顾 Web Components 核心 API,然后对组件库需求进行分析设计,再进行环境搭建和开发,内容比较多,可能没有每一点都讲到,还请大家看看我仓库的源码,有什么问题欢迎和我讨论。写本文的几个核心目的:
最后看完本文,大家是否觉得用 Web Components 开发组件库,实在有点复杂?要写的太多了。没关系,下一篇我将带大家一起使用 Stencil 框架开发 Web Components 标准的组件库,毕竟整个 ionic 已经是使用 Stencil 重构,Web Components 大势所趋~!
本文共计2693个文字,预计阅读时间需要11分钟。
组件化是前端开发的一个重要趋势,它一方面提高了开发效率,另一方面降低了维护成本。主流的框架如Vue.js、React,以及衍生出的Ant Design、uniapp、Taro等,都是组件化框架的典型代表。Web Components则是一组Web标准,旨在提供一种创建自定义原生态组件的方法。
组件化是前端发展的一个重要方向,它一方面提高开发效率,另一方面降低维护成本。主流的 Vue.js、React 及其延伸的 Ant Design、uniapp、Taro 等都是组件框架。Web Components 是一组 Web 原生 API 的总称,允许我们创建可重用的自定义组件,并在我们 Web 应用中像使用原生 HTML 标签一样使用。目前已经很多前端框架/库支持 Web Components。
本文将带大家回顾 Web Components 核心 API,并从 0 到 1 实现一个基于 Web Components API 开发的业务组件库。
最终效果:blog.pingan8787.com/exe-components/demo.html仓库地址:github.com/pingan8787/Learn-Web-Components
一、回顾 Web Components
在前端发展历史中,从刚开始重复业务到处复制相同代码,到 Web Components 的出现,我们使用原生 HTML 标签的自定义组件,复用组件代码,提高开发效率。通过 Web Components 创建的组件,几乎可以使用在任何前端框架中。
1. 核心 API 回顾
Web Components 由 3 个核心 API 组成:
- 「Custom elements(自定义元素)」:用来让我们定义「自定义元素」及其「行为」,对外提供组件的标签;
- 「Shadow DOM(影子 DOM)」:用来封装组件内部的结构,避免与外部冲突;
- 「HTML templates(HTML 模版)」:包括 <template>和<slot> 元素,让我们可以定义各种组件的 HTML 模版,然后被复用到其他地方,使用过 Vue/React 等框架的同学应该会很熟悉。
另外,还有 HTML imports,但目前已废弃,所以不具体介绍,其作用是用来控制组件的依赖加载。
2. 入门示例
接下来通过下面简单示例快速了解一下「如何创建一个简单 Web Components 组件」。
- 使用组件
<html lang="en">
<head>
<script src="./index.js" defer></script>
</head>
<body>
<h1>custom-element-start</h1>
<custom-element-start></custom-element-start>
</body>
</html>
- 定义组件
* 使用 CustomElementRegistry.define() 方法用来注册一个 custom element
* 参数如下:
* - 元素名称,符合 DOMString 规范,名称不能是单个单词,且必须用短横线隔开
* - 元素行为,必须是一个类
* - 继承元素,可选配置,一个包含 extends 属性的配置对象,指定创建的元素继承自哪个内置元素,可以继承任何内置元素。
*/
class CustomElementStart extends HTMLElement {
constructor(){
super();
this.render();
}
render(){
const shadow = this.attachShadow({mode: 'open'});
const text = document.createElement("span");
text.textContent = 'Hi Custom Element!';
text.style = 'color: red';
shadow.append(text);
}
}
customElements.define('custom-element-start', CustomElementStart)
上面代码主要做 3 件事:
通过实现 CustomElementStart 类来定义组件。
将组件的标签和组件类作为参数,通过 customElements.define 方法定义组件。
导入组件后,跟使用普通 HTML 标签一样直接使用自定义组件 <custom-element-start></custom-element-start>。
随后浏览器访问 index.html 可以看到下面内容:
3. 兼容性介绍
在 MDN | Web Components 章节中介绍了其兼容性情况:
- Firefox(版本63)、Chrome和Opera都默认支持Web组件。
- Safari支持许多web组件特性,但比上述浏览器少。
- Edge正在开发一个实现。
关于兼容性,可以看下图:图片来源:www.webcomponents.org/
这个网站里面,有很多关于 Web Components 的优秀项目可以学习。
4. 小结
这节主要通过一个简单示例,简单回顾基础知识,详细可以阅读文档:
- 使用 custom elements
- 使用 shadow DOM
- 使用 templates and slots
image.png
二、EXE-Components 组件库分析设计
1. 背景介绍
假设我们需要实现一个 EXE-Components 组件库,该组件库的组件分 2 大类:
以「通用简单组件」为主,如exe-avatar头像组件、 exe-button按钮组件等;
以「复杂、组合组件」为主,如exe-user-avatar用户头像组件(含用户信息)、exe-attachement-list附件列表组件等等。
详细可以看下图:
接下来我们会基于上图进行 EXE-Components 组件库设计和开发。
2. 组件库设计
在设计组件库的时候,主要需要考虑以下几点:
当然,这几个是最基础需要考虑的点,随着实际业务的复杂,还需要考虑更多,比如:工程化相关、组件解耦、组件主题等等。
针对前面提到这 3 点,这边约定几个命名规范:
3. 组件库组件设计
这边我们主要设计 exe-avatar 、exe-button 和 exe-user-avatar三个组件,前两个为简单组件,后一个为复杂组件,其内部使用了前两个组件进行组合。这边先定义这三个组件支持的属性:
这边属性命名看着会比较复杂,大家可以按照自己和团队的习惯进行命名。
这样我们思路就清晰很多,实现对应组件即可。
三、EXE-Components 组件库准备工作
本文示例最终将对实现的组件进行「组合使用」,实现下面「「用户列表」」效果:体验地址:blog.pingan8787.com/exe-components/demo.html
1. 统一开发规范
首先我们先统一开发规范,包括:
image.png
image.png
组件开发模版分 index.js「组件入口文件」和 template.js 「组件 HTML 模版文件」:
// index.js 模版const defaultConfig = {
// 组件默认配置
}
const Selector = "exe-avatar"; // 组件标签名
export default class EXEAvatar extends HTMLElement {
shadowRoot = null;
config = defaultConfig;
constructor(){
super();
this.render(); // 统一处理组件初始化逻辑
}
render() {
this.shadowRoot = this.attachShadow({mode: 'closed'});
this.shadowRoot.innerHTML = renderTemplate(this.config);
}
}
// 定义组件
if (!customElements.get(Selector)) {
customElements.define(Selector, EXEAvatar)
}// template.js 模版
export default config => {
// 统一读取配置
const { avatarWidth, avatarRadius, avatarSrc } = config;
return `
<style>
/* CSS 内容 */
</style>
<div class="exe-avatar">
/* HTML 内容 */
</div>
`
}
2. 开发环境搭建和工程化处理
为了方便使用 EXE-Components 组件库,更接近实际组件库的使用,我们需要将组件库打包成一个 UMD 类型的 js 文件。这边我们使用 rollup 进行构建,最终打包成 exe-components.js 的文件,使用方式如下:
<script src="./exe-components.js"></script>接下来通过 npm init -y生成 package.json文件,然后全局安装 rollup 和 zoo.team","level":6,"avatar":"zcy.jpg","home":"7288974"}
]
我们就可以通过简单 for 循环拼接 HTML 片段,然后添加到页面某个元素中:
// 测试生成用户列表模版const usersTemp = () {
let temp = '', code = '';
users.forEach(item {
const {name, desc, level, avatar, home} = item;
temp +=
`
<exe-user-avatar
e-user-name="${name}"
e-sub-name="${desc}"
e-avatar-src="./testAssets/images/users/${avatar}"
e-avatar-width="36px"
e-button-type="primary"
e-button-text="关注"
on-avatar-click="toUserHome('${home}')"
on-button-click="toUserFollow('${name}')"
>
${
level >= 0 && `<span slot="name-slot">
<span class="medal-item">(Lv${level})</span>
</span>`}
</exe-user-avatar>
`
})
return temp;
}
document.querySelector('#app').innerHTML = usersTemp;
到这边我们就实现了一个用户列表的业务,当然实际业务可能会更加复杂,需要再优化。
五、总结
本文首先简单回顾 Web Components 核心 API,然后对组件库需求进行分析设计,再进行环境搭建和开发,内容比较多,可能没有每一点都讲到,还请大家看看我仓库的源码,有什么问题欢迎和我讨论。写本文的几个核心目的:
最后看完本文,大家是否觉得用 Web Components 开发组件库,实在有点复杂?要写的太多了。没关系,下一篇我将带大家一起使用 Stencil 框架开发 Web Components 标准的组件库,毕竟整个 ionic 已经是使用 Stencil 重构,Web Components 大势所趋~!

