超爱Web Components: 用它封装通用组件(一)(Designed once, Built once, Used everywhere)
前言
随着vue
、react
等框架的兴起,虽然极大地便利了前端开发者们,但同时也带来了一些弊端,比如基于react
写的组件,无法在vue
中使用,反之亦然。这意味着我们需要为不同的框架,编写同样的组件功能,吼吼吼~~,我不愿,敢问大家愿意吗?Web Components
完美地解决了该困境,它不依赖于第三方框架,它的主要优点如下所示:
- 所有主流浏览器均支持
- Shadow Dom(内部样式,不被外界污染)
- 【CSS自定义属性】使用于自定义主题
Web Components
的主要技术点为:
- Custom Elements
- Shadow DOM
- HTML templates
接下来,我们将使用Shadow DOM
编写自定义输入框组件
Shadow DOM
Shadow DOM
使用shadow root
作为根节点,我们可以使用它将Shadow DOM
封装在其独立的沙盒DOM
树中。大家可以将文档里的所有DOM节点想象成一片森林,而Shadow DOM
便是在森林内部独立的小岛,shadow root
是这座小岛的保护罩,让其不受外界干扰。同理可得,Shadow DOM
里面的元素及其样式不受外部DOM的影响。
它的特点如下所示:
- 我们的样式被保护
- client样式被保护(比如clientWidth等等)
- 我们可以依照原始DOM元素的特性来约定它
- 功能像原生的DOM元素,或者几乎表现一样
别忘记,Shadow DOM
也有slot
,其功能和vue
里的slot
一样
Shadow DOM
编写slot
的样式时,使用::slot
创建ZwfInput
组件
我们选择lit-element
编写 ZwfInput
输入框组件,lit-element
基于它的核心库lit
,提供了响应式、scoped样式、小巧但丰富的模板系统,并且支持Typescript
语法。
组件的初始代码如下所示:
import { LitElement, html } from 'lit-element';
export class ZwfInput extends LitElement {
render() {
return html``;
}
}
customElements.define('zwf-input', ZwfInput);
使用customElements.define
,我们可以自定义新的HTML标签
给ZwfInput
添加属性
我们给该输入框添加label
和value
两个属性值,代码如下所示:
export class ZwfInput extends LitElement {
static get properties() {
return { label: {type: String}, value: {type: String} };
}
render() {
return html`
<label id="label">${this.label}</label>
<input aria-labelledby="label" .value=${this.value}>
<slot name="icon"></slot>
`;
}
}
aria-labelledby
属性可以让辅助软件知道该input
的作用,即机器自动识别
根据设计,该input框的右侧有一个图标,且图标需为用户定义,故我们使用slot来实现
给ZwfInput
添加事件
export class ZwfInput extends LitElement {
static get properties() {
return { label: {type: String}, value: {type: String} };
}
handleChange(e) {
this.value = e.target.value;
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
}
render() {
return html`
<label id="label">${this.label}</label>
<input aria-labelledby="label" .value=${this.value} @change=${this.handleChange}>
<slot name="icon"></slot>
`;
}
}
bubbles: true, composed: true
:使Shadow Dom
外部的DOM元素可以监听到该事件
给ZwfInput
添加样式
export class ZwfInput extends LitElement {
static styles = css`
slot[name="icon"]::slotted(*) {
display: none;
}
slot[name="icon"]::slotted(svg) {
display: inline-flex;
width: 36px;
height: 36px;
}
`
...
}
在样式里,我们做了两件事:
- 默认隐藏所有
slot
内容 - 给
slot
内部的svg
添加指定的宽高大小(按设计要求)
如何在Vue 2.*
项目使用ZwfInput
组件
我们使用@vue/cli
脚手架,初始化项目,脚本命令如下所示:
# 全局安装@vue/cli
npm i -g @vue/cli
# 创建项目,选择默认选项
vue create vue-webcomponent
# 进入到项目文件夹内
cd vue-webcomponent
# 安装lit-element依赖
npm i lit-element
在src
文件夹内新建webcomponents
文件夹,里面存放ZwfInput.js
文件
再在App.vue
内引入该组件,引入代码如下所示:
<template>
<div id="app">
<zwf-input class="test"></zwf-input>
</div>
</template>
<script>
import './src/webcomponents/ZwfInput.js'
</script>
但是,运行的时候,却报如下错误
翻墙查找错误的原因,大概是因为需要为babel
指定现代版本的浏览器,否则无法编译通过。
试过如下解决方法:
- 在
.babelrc
内,添加如下代码:
{
"presets": ["es2017"]
}
- 在
package.json
里添加如下代码:
"browserslist": [
"last 1 Chrome versions"
]
但是错误依旧,最后在vue.config.js
里添加如下代码:
options: {
presets: [
[
'env',
{
targets: {
browsers: [
"last 1 Chrome versions"
],
},
},
],
]
}
问题得到解决,代码运行的效果如下图所示:
小结
在本节课里,我们学习了如何封装通用的Web Components
组件,以及如何在Vue2
项目中使用它,下节课,我们将会讨论如何调用Web Components
里的form
表单,属性值传递等等知识点,感谢大家的关注,下节课再见~
转载自:https://juejin.cn/post/7146452759087153160