前言
在前端快速发展的今天,组件贯彻着我们日常开发的方方面面,不管是针对业务封装的业务组件,还是项目中依赖的第三方基础 UI 组件(Ant Design、Element),亦或是依赖的前端框架(Angular、Vue、React),它们都贯彻着「组件化」的概念,而这一切「组件化」都是「高内聚、低耦合」思想下的产物。
「组件化」开发已然成为前端主流开发方式,因为「组件化」开发在代码复用、提升团队效率方面有着无可比拟的优势,现在流行的 Vue、React、Angular 等等框架都是组件框架。所以毫不夸张的说 「组件化将会是前端的发展方向」。
目前存在问题
作者所在的部门一半的项目用的 Vue 技术栈,一半的项目用的 React 技术栈,遵循「高内聚、低耦合」的原则,我们为这两套技术栈分别搭建了Vue UI 组件库和React UI 组件库。这两套 UI 组件库的功能几乎毫无差异,但是要由两个团队来维护。适配这两个框架,将要耗费双倍的人力成本。
即使针对同一个技术栈,在面临版本升级时,旧的组件库也会存在不可用的情况。比如:Vue 从 2.x 升级到 3.x 版本,React从 17版本升级到 18 版本, 之前封装的基础组件和业务组件都在新版本中就不能正常使用。一套组件库在同一个框架不同版本之间存在差异,甚至只能固定在某一个版本中使用,这本身于前端的「组件化」趋势是相违背的。
随着前端复杂度的增加,项目中使用的框架越来越多,会在不经意间出现样式的冲突。很多框架在组件设计时没有样式进行样式隔,例如:在使用 React 时,React 在书写组件样式时经常出现样式覆盖、错乱的问题,不得不借助其他方案来解决此类问题。
Webpack 盛行的今天,大多数项目使用 Webpack 作为编译工具,这让组件经过 Webpack 编译之后想要在运行时即引即用变得不太现实。
前端框架的「组件化」并不是真正的「组件化」。虽然书写代码时的确是「组件化」的方法在写,但是编译完之后就不再「组件化」了。
「组件化」给前端的开发带来了极大的效率提升,随着发展前端「组件化」的框架也因此层出不穷,从最早的 jQuery,再到 React、Vue、Angular、Ratchet、Ionic 等等,几乎每年都有很多新的框架如雨后春笋悄然而生,它们或借鉴或颠覆其他已存在的框架,究其本质这些框架的很大一部分模块在功能上是重合的,但也仅仅在功能层面重合,代码层面确完全不兼容。
为了解决这些问题,各种解决方案层出不穷,其中 Web ***ponents
就是其中关键的一环。
什么是web ***ponents?
Web ***ponents 也是一个浏览器原生支持的组件化方案,允许你创建新的自定义、可封装的HTML 标记,使用时不用加载任何额外的模块。自定义组件和小部件基于 Web ***ponents 标准构建,可跨现代浏览器工作,并可与任何支持 HTML 的 JavaScript 库或框架一起使用。
web ***ponents的历史:
实际上:Vue 作者在创建 Vue 的时候大量参考了 Web ***ponents 的语法。
web ***ponents的组成:
Custom element(自定义元素):一组 JavaScript API,允许你定义 custom elements 及其行为,然后可以在你的用户界面中按照需要使用它们。
Shadow DOM(影子 DOM):一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,你可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
HTML template(HTML 模板): [` 和 `` 元素使你可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。
web ***ponents的使用
js"><!-- 自定义组件的使用 -->
<my-button as-atom></my-button>
<template id="my-button-template">
<button class="my-button">click</button>
<style>
.my-button {
padding: 10px 20px;
font-size: 16px;
border-radius: 12px;
background-color: rgb(35, 64, 121);
color: white;
border: none;
}
</style>
</template>
<script>
class MyButton extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'closed' });
const templateElem = document.getElementById('my-button-template');
const content = templateElem.content.cloneNode(true);
shadow.appendChild(content);
/* 添加点击事件 */
this.$button = shadow.querySelector('button');
this.$button.addEventListener('click', () => {
this.dispatchEvent(
new CustomEvent('myClick', {
detail: 'hello'
})
)
});
}
// 类似mounted
connectedCallback() {
if (this.hasAttribute('as-atom')) {
this.$button.style.padding = '0';
}
}
get label() {
return this.getAttribute('label');
}
set label(value) {
this.setAttribute('label', value);
}
static get observedAttributes() {
return ['label'];
}
attributeChangedCallback(name, oldVal, newVal) {
this.render();
}
render() {
this.$button.innerHTML = this.label;
}
}
window.customElements.define('my-button', MyButton);
</script>
</body>
成熟的组件库
- hybrids: 是一个 JavaScript UI 框架,用于创建功能齐全的 Web 应用程序、组件库或具有独特的混合声明性和功能性架构的单个 Web ***ponents。
- Polymer :是 Google 推出的 Web ***ponents 库,支持数据的单向和双向绑定,兼容性较好,跨浏览器性能也较好;提供了一组用于创建 custom elements 的功能。这些功能旨在使 custom elements 像标准 DOM 元素一样工作更容易和更快。