likes
comments
collection
share

超爱Web Components: 用它封装通用组件(一)(Designed once, Built once, Used everywhere)

作者站长头像
站长
· 阅读数 53

前言

随着vuereact等框架的兴起,虽然极大地便利了前端开发者们,但同时也带来了一些弊端,比如基于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语法。

超爱Web Components: 用它封装通用组件(一)(Designed once, Built once, Used everywhere)

组件的初始代码如下所示:

import { LitElement, html } from 'lit-element';

export class ZwfInput extends LitElement {
    render() {
        return html``;
    }
}

customElements.define('zwf-input', ZwfInput);

使用customElements.define,我们可以自定义新的HTML标签

ZwfInput添加属性

我们给该输入框添加labelvalue两个属性值,代码如下所示:

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;
        }
    `
    ...
}

在样式里,我们做了两件事:

  1. 默认隐藏所有slot内容
  2. 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>

但是,运行的时候,却报如下错误

超爱Web Components: 用它封装通用组件(一)(Designed once, Built once, Used everywhere)

翻墙查找错误的原因,大概是因为需要为babel指定现代版本的浏览器,否则无法编译通过。

试过如下解决方法:

  1. .babelrc内,添加如下代码:
{
  "presets": ["es2017"]
}
  1. package.json里添加如下代码:
"browserslist": [
    "last 1 Chrome versions"
 ]

但是错误依旧,最后在vue.config.js里添加如下代码:

options: {
    presets: [
        [
            'env',
             {
                targets: {
                  browsers: [
                    "last 1 Chrome versions"
                  ],
                },
              },
         ],
    ]
}

问题得到解决,代码运行的效果如下图所示:

超爱Web Components: 用它封装通用组件(一)(Designed once, Built once, Used everywhere)

小结

在本节课里,我们学习了如何封装通用的Web Components组件,以及如何在Vue2项目中使用它,下节课,我们将会讨论如何调用Web Components里的form表单,属性值传递等等知识点,感谢大家的关注,下节课再见~

转载自:https://juejin.cn/post/7146452759087153160
评论
请登录