Web componentd组件内部事件回调及痛点剖析

目录
  • 写在前面
  • WC 到底是什么?
  • 目前存在的缺陷
    • 1、组件内部事件的回调
    • 2、组件样式覆盖
    • 3、组件内部资源相对路径问题
    • 4、form表单类组件 value 获取问题
    • 5、其它
  • 写在后面

写在前面

最近致力于研究 Web components(以下简称WC),并且也初有成效的拿到了一定的结果,但今天想回过头来重新审视一下 WC。

WC 到底是什么?

简单的讲,Web Component 就是把组件封装成 html 标签的形式,并且在使用时不需要写额外的 js 代码。

组件是前端的发展方向,抛开周边技术生态,单纯看 React 和 Vue 都是组件框架。因此,WC 可以视为原生标签的拓展/延伸,说到底,它依旧是一个标签!

类似 <video></video> 标签,相比于原生标签,它多了更为丰富的样式和可操作属性。

谷歌公司由于掌握了 Chrome 浏览器,一直在推动浏览器的原生组件,即 Web Components API。

相比第三方框架,原生组件简单直接,符合直觉,不用加载任何外部模块,代码量小。貌似一切完美,似乎大有可以用来替换React、Vue之类的趋势。

目前存在的缺陷

与其它 web 框架一起使用存在一些小问题,会给开发体验上造成一些困扰。

1、组件内部事件的回调

比如,一个弹窗组件(<my-dialog></my-dialog>)中的确定按钮,那么它的事件是如何触发的呢?

class myDialog extends HTMLElement {
  // ...
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = `
      <div class="dialog">
        <div class="dialog-content">
          <div class="dialog-body">
            弹窗内容
          </div>

          <button id="okBtn">确定</button>
        </div>
      </div>
    `;
     shadowRoot.querySelector('#okBtn').addEventListener('click', () => {
      // 组件内部定义事件
      this.dispatchEvent(new CustomEvent('okBtnFn'));
    });
  }
}
customElements.define('my-dialog', myDialog);

现在方案是 custom element 内部自定义事件 new CustomEvent(),外部用 addEventListener监听。这样的写法是很丑陋的,仿佛又回到了原生 JS 写应用的时代。

<my-dialog></my-dialog>
<script>
  export default {
    created() {
      document.addEventListener('okBtnFn', function(){
        // 点击弹窗按钮,触发回调事件
      });
    }
  }
</script>

2、组件样式覆盖

对于开发者来说,难免会遇到需要调整组件内部样式的时候。无论你是使用antdvant还是使用其它组件库,但 WC 的 CSS 防污染机制导致你很难修改内部样式。这需要你付出一些代价来变相的修改内部样式

3、组件内部资源相对路径问题

就目前来说,任何直接基于 Custom Element v1, Template 和 HTML Import 的组件都无法做到完全资源独立 —— 在不知道使用方环境且不给使用方增加额外限制的情况下使用内部封装的任何资源文件。比如如果你有一个自定义 icon 组件:

class MyIcon extends HTMLElement {
    static get observedAttributes() { return ['name','size','color'] }
    constructor() {
        super();
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.innerHTML = `
            <svg class="icon" id="icon" aria-hidden="true" viewBox="0 0 1024 1024">
                <use id="use"></use>
            </svg>
    }
    attributeChangedCallback (name, oldValue, newValue) {
        if( name == 'name' && this.shadowRoot){
            // 如果使用的项目中,根目录没有 icon.svg 文件,那就 gg
            this.use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', `./icon.svg#icon-${newValue}`);
        }
    }
}
customElements.define('my-icon', MyIcon);

如果使用的项目中,根目录没有 icon.svg 文件,那就 gg。如果你在这里使用 cdn 路径,就会出现跨域问题。

4、form表单类组件 value 获取问题

Shadow DOM 中包含有 <input>、<textarea> 或 <select> 等标签的 value 不会在 form 表单中自动关联。

示例代码:

// web component
class InputAge extends HTMLElement {
  constructor() {
    super();
  }
// connect component
  connectedCallback() {
    const shadow = this.attachShadow({ mode: 'closed' });
    shadow.innerHTML = `<input type="number" placeholder="age" min="18" max="120" />`;
  }
}
// register component
customElements.define( 'input-age', InputAge );

WC 组件被使用后

<form id="myform">
  <input type="text" name="your-name" placeholder="name" />
  <input-age name="your-age"></input-age>

  <button>submit</button>
</form>

<script>
 const form = document.getElementById('myform');

  form.addEventListener('submit', e => {

    e.preventDefault();
    console.log('Submitted data:');

    const data = new FormData(form);
    for (let nv of data.entries()) {
      console.log(`  ${ nv[0] }: ${ nv[1] }`);
    }

  });
</script>

提交的时候无法获取 input-age 的 value。当然会有解决方案,但会很复杂。

5、其它

此外,缺少数据绑定和状态管理也是 WC 存在的缺陷,此处不再赘述。

写在后面

WC 指在丰富 HTML 的 DOM 特性,让 HTML 拥有更强大的复用能力

WC 可以直接当做原生标签,在任何前端框架和无框架中运行

结合当下的主流技术栈来说,WC 当前主要问题在于复杂的组件中,数据通信和事件传递存在一定使用成本

兼容问题,比如可以覆盖内部样式的 :part 方法

以上就是Web componentd组件内部事件回调及痛点剖析的详细内容,更多关于Web componentd事件回调及痛点的资料请关注我们其它相关文章!

时间: 2021-11-23

手把手带你封装一个vue component第三方库

为什么选择自己封装第三方库 最近几个月我司把之前两三年的所有业务都用了 vue 重构了一遍,前台使用 vue+ssr,后台使用了 vue+element,在此过程中封装和自己写了很多 vue component.其实vue 写 component 相当简单和方便,github上有很多的 vue component 都只是简单的包装了一些 jquery 或者原生 js 的插件,但我个人是不太喜欢使用这些第三方封装的.理由如下: 很多第三方封装的组件参数配置项其实是有缺损的.如一些富文本或者图表组件

剖析Angular Component的源码示例

Web Component 在介绍Angular Component之前,我们先简单了解下W3C Web Components 定义 W3C为统一组件化标准方式,提出Web Component的标准. 每个组件包含自己的html.css.js代码. Web Component标准包括以下四个重要的概念: 1.Custom Elements(自定义标签):可以创建自定义 HTML 标记和元素: 2.HTML Templates(HTML模版):使用 <template> 标签去预定义一些内容,但

浅谈Vue内置component组件的应用场景

官方的说明 渲染一个"元组件"为动态组件.依 is 的值,来决定哪个组件被渲染. <!-- 动态组件由 vm 实例的属性值 `componentId` 控制 --> <component :is="componentId"></component> 具体可以官网文档中的 动态组件 内置的组件component 场景 这里通过一个业务场景来阐述vue内置component组件的应用. 如图所示,这里展示经典注册页面,注册分为邮箱注册

一个@Component注解引发的大坑

目录 一个@Component注解引发的大坑 问题是这样的 思考 对spring @component注解的理解 @Component注解的使用 注解的类 测试的类 一个@Component注解引发的大坑 首先,我们这个是用springboot架构来实现的业务 这是项目包结构和配置文件结构 这是定时需要执行的任务 这是我执行PromoCodeCMCJob这个定时器的报错信息 问题是这样的 加了两个MQ之后才报错的这个信息,当我执行启动PromoCodeCMCJob定时任务的时候就报错,报错信息如

spring @Component注解原理解析

这篇文章主要介绍了spring @Component注解原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.@controller 控制器(注入服务) 2.@service 业务(注入dao) 3.@repository dao(实现dao访问) 4.@component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>) 5.@Comp

设计引导--一个鸭子游戏引发的设计理念(多态,继承,抽象,接口,策略者模式)

这篇博文是从实际生活中,提炼出来的设计理念,它现在是骨架,现在我加以代码实例,完成程序的血肉,以求让大家活生生的体会设计中的精髓. 自从我们学习面向对象编程以来,它方便了我们的思维思考模式,一个事物具备什么,就以对应的属性及方法加之. (▽) 没有什么难的,但是你学到的是最基础的语法和连自己都不是很了解的语言,用一段C语言程序,你可以很轻松的把它改成C#,JAVA等,这有什么难的?大多数程序员们扭曲了C#语言,把C的语法都移植到C#上(在我不了解C#的时候,我自己都这么做过),错了不可怕,可怕的

一个单引号引发的MYSQL性能问题分析

对于大型的系统而言,Oracle,SQLServer无疑是最好的选择,可看看现在越来越多的小网站,他们没有自己的服务器,只是买别人的空间和数据库,但这种小型的数据库在性能上当然和大型数据库没有对比性,但小型的数据库也要对自己的优化方式,今天和大家分享Mysql中加没加单引号的巨大区别,对于MYSQL性能优化很有意义. 刚刚我们说过了,生活中难免会有一些不如意,比如,我们用一个字符串类型的字段来作为主键,表面上,这太不如意了,然而,事实也证明这是有用的.问题也就出来了,当在查询语句中对该字段值加上

详解Java的Spring框架中的注解的用法

1. 使用Spring注解来注入属性 1.1. 使用注解以前我们是怎样注入属性的 类的实现: class UserManagerImpl implements UserManager { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } ... } 配置文件: <bean id="userManagerImpl" class="com.

浅谈spring ioc的注入方式及注入不同的数据类型

关于Spring-IoC的简单使用参考: spring ioc的简单实例及bean的作用域属性解析 1.通过set方法注入不同数据类型 测试类代码(set方式注入的属性一定要加set方法) /**通过set方法注入示例*/ public class IoC_By_Set { /**注入Integer类型参数*/ private Integer id; /**注入String类型参数*/ private String name; /**注入实体Bean*/ private User user; /

SpringBoot 如何添加容器启动的初始化逻辑的操作方法

背景 传统 SpringMVC 项目中,我们可以定义容器初始化 Servlet ,然后在 web.xml 配置该 Servlet ,指定 load-on-startup 就可以在容器启动后,执行一些系统的初始化逻辑.比如设置全局资源文件路径.加载系统定时任务.数据初始化等. 技术转换到 SpringBoot 的时候,该在哪里添加应用初始化的代码呢? 如果初始化逻辑中,有一些普通类,需要通过 Spring 的 ApplicationContext 的 getBean 获取依赖属性,如何保证 Spr

简单注解实现集群同步锁(spring+redis+注解)

互联网面试的时候,是不是面试官常问一个问题如何保证集群环境下数据操作并发问题,常用的synchronized肯定是无法满足了,或许你可以借助for update对数据加锁.本文的最终解决方式你只要在方法上加一个@P4jSyn注解就能保证集群环境下同synchronized的效果,且锁的key可以任意指定.本注解还支持了锁的超时机制. 本文需要对Redis.spring和spring-data-redis有一定的了解.当然你可以借助本文的思路对通过注解对方法返回数据进行缓存,类似com.googl

Java自定义注解实现Redis自动缓存的方法

在实际开发中,可能经常会有这样的需要:从MySQL中查询一条数据(比如用户信息),此时需要将用户信息保存至Redis. 刚开始我们可能会在查询的业务逻辑之后再写一段Redis相关操作的代码,时间长了后发现这部分代码实际上仅仅做了Redis的写入动作,跟业务逻辑没有实质的联系,那么有没有什么方法能让我们省略这些重复劳动呢? 首先想到用AOP,在查询到某些数据这一切入点(Pointcut)完成我们的切面相关处理(也就是写入Redis).那么,如何知道什么地方需要进行缓存呢,也就是什么地方需要用到AO

java基于spring注解AOP的异常处理的方法

一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...finally对异常进行处理,但是我们真的能在写程序的时候处理掉所有可能发生的异常吗? 以及发生异常的时候执行什么逻辑,返回什么提示信息,跳转到什么页面,这些都是要考虑到的. 二.基于@ControllerAdvice(加强的控制器)的异常处理 @ControllerAdvice注解内部使用@Except

Spring 中 @Service 和 @Resource 注解的区别

Spring 中 @Service 和 @Resource 注解的区别 1 前言 在咱们使用 spring 框架的时候,注解是"不可或缺"的一部分,她帮我们脱离了配置繁琐的 XML 文件的工作,但有一点却需要我们自己去把握,那就是"3何",即"何时何地用何注解?"在本篇博文中,作者就简单介绍一下如何恰当的使用 @Service 和 @Resource 这两个注解. 2 注解 2.1 @Service 当咱们需要定义某个类为一个 bean 的时候,