微前端之Web组件自定义元素示例详解

目录
  • 我们知道的
  • Web组件使用
    • 名称规范
    • 组件传参数并可以写模板包括js和css
    • Shadow Dom 影子节点
    • 类中的构造函数和钩子函数
    • getter/setter属性和属性反射
    • 扩展原生 HTML

我们知道的

第一:我们熟知的HTML标签有 a, p, div, section, ul, li, h2, article, head, body, strong, video, audio 等等

第二:我们知道,a标签是链接,p标签是段落,div是块级,h2是字体,strong 是粗体,video 可以播放视频,标签可以添加click等事件等等

所以,那么显然我们知道这些标签的名称,而且知道他们的默认的css样式,而且知道他们默认的js事件

由此,我们是否可以自定义标签呢,由我们自己规定名称,规定默认css样式,规定标签默认显示dom结构,规定默认事件呢?

答案当然是肯定的!!下面就介绍主角Web组件

Web组件使用

名称规范

  • 在定义它们时必须使用至少两个单词和一个连字符
  • 必须小写
  • 自定义元素不能自闭合
class UserCard extends HTMLElement {
  constructor() {
    super();
  }
}
window.customElements.define('user-card', UserCard);

组件传参数并可以写模板包括js和css

组件的样式应该与代码封装在一起,只对自定义元素生效,不影响外部的全局样式,:host伪类,指代自定义元素本身

<html>
    <head></head>
    <body>
        <user-card name="Marty Mcfly"></user-card>
        <template id="userCardTemplate">
          <div class="profile-picture">
            <img src="marty.png" alt="Marty Mcfly" />
          </div>
          <div class="name"></div>
          <style>
           :host {
             display: flex;
             align-items: center;
             width: 450px;
             height: 180px;
             background-color: #d4d4d4;
             border: 1px solid #d5d5d5;
             box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
             border-radius: 3px;
             overflow: hidden;
             padding: 10px;
             box-sizing: border-box;
             font-family: 'Poppins', sans-serif;
           }
           .image {
             flex: 0 0 auto;
             width: 160px;
             height: 160px;
             vertical-align: middle;
             border-radius: 5px;
           }
        </style>
        </template>
        <script>
            class UserCard extends HTMLElement {
              constructor() {
                super();
                var templateElem = document.getElementById('userCardTemplate');
                var content = templateElem.content.cloneNode(true);
                // 使用name参数
                content.querySelector('.container>.name').innerText = this.getAttribute('name');
                this.appendChild(content);
              }
            }
            window.customElements.define('user-card', UserCard);
        </script>
    </body>
</html>

Shadow Dom 影子节点

  • 没有开启Shadow Dom 前template的内容会直接append到user-card节点上
  • 开启Shadow Dom后,template和user-card中间多了一个节点叫shadowRoot
  • attachShadow 的mode参数有 open 和 closed 两个不同参数,区别是open时,外部能访问内部节点,closed时完全隔离
  • attachShadow 是大多数标签都支持的,比如 div,p,selection,但是a,ul,li等不支持
<script>
    class UserCard extends HTMLElement {
      constructor() {
        super();
        var shadow = this.attachShadow( { mode: 'closed' } );   /******这行开启shadow*****/
        var templateElem = document.getElementById('userCardTemplate');
        var content = templateElem.content.cloneNode(true);
        // 使用name参数
        content.querySelector('.container>.name').innerText = this.getAttribute('name');
        shadow.appendChild(content);         /******这行template添加到shadow*****/
      }
    }
    window.customElements.define('user-card', UserCard);
</script>

类中的构造函数和钩子函数

class UserCard extends HTMLElement {
  // attributeChangedCallback 能监听的属性
  static get observedAttributes() {return ['name', 'url']; }
  constructor() {
    super();
    // 可以创Shadom,通过this.shadowRoot获取
    // this.attachShadow({ mode: 'open' })
  }
  connectedCallback (){
      console.log('钩子,元素append到ducument触发')
  }
  disconnectedCallback (){
      console.log('钩子,元素从document删除触发')
  }
  // 只有observedAttributes 中监听的属性name,url变化会触发下面回调
  attributeChangedCallback (attr, oldVal, newVal){
      console.log('钩子,元素属性改变时触发')
  }
}
window.customElements.define('user-card', UserCard);
姓名 何时调用
constructor 元素的一个实例被创建。对于初始化状态、设置事件监听器或创建shadow Dom。
connectedCallback 每次将元素插入 DOM 时调用。一般主要工作代码写在这里。
disconnectedCallback 每次从 DOM 中删除元素时调用。一般写清理的一些代码。
attributeChangedCallback(attrName, oldVal, newVal) 观察属性在添加、删除、更新、替换时被调用。只有属性中列出的observedAttributes属性才会收到此回调。
adoptedCallback 自定义元素已被移动到一个新的document(例如有人称为document.adoptNode(el))。

getter/setter属性和属性反射

html中attribute 和 类中property 是各自独立,想要建立映射需要手动设置

getter和setter生效

<html>
  <head></head>
  <body>
    <user-card id="usercard" name="Marty"></user-card>
    <script>
      class UserCard extends HTMLElement {
        _name = null;
        constructor() {
          super();
        }
        set name(value) {
          this._name = name;
        }
        get name() {
          return this._name;
        }
      }
      window.customElements.define("user-card", UserCard);
    </script>
  </body>
</html>

测试

document.getElementById('usercard').name = 'jack'  // 会进入setter
document.getElementById('usercard').name  // 会进入getter,返回jack
document.getElementById('usercard').getAttribute('name')  // 不会进入getter,返回Marty
document.getElementById('usercard').setAttribute('name','bob')  // 不会进入setter
document.getElementById('usercard').getAttribute('name')  // 不会进入getter,返回bob

此时,我们看到 setAttribute 和 gettAttribute 都不会触发类中的 getter 和 setter 方法,但是可以看到如果是 .name 这样的方式可以触发 ,那么改造方式如下:

方式一 重写 setAttribute :

<html>
  <head></head>
  <body>
    <user-card id="usercard" name="Marty"></user-card>
    <user-card id="uc" name="Marty Mcfly"></user-card>
    <script>
      const rawSetAttribute = Element.prototype.setAttribute;
      class UserCard extends HTMLElement {
        _name = null;
        constructor() {
          super();
          Element.prototype.setAttribute = function setAttribute(key, value) {
            // 特定的指定name
            if (key == "name") {
              this.name = value; // 这样就能触发 setter
            }
            rawSetAttribute.call(this, key, value);
          };
        }
        set name(value) {
          debugger;
          this._name = name;
        }
        get name() {
          debugger;
          return this._name;
        }
      }
      window.customElements.define("user-card", UserCard);
    </script>
  </body>
</html>

方式二 重写 observedAttributes和attributeChangedCallback 监听 name :

<html>
  <head></head>
  <body>
    <user-card id="uc" name="Marty Mcfly"></user-card>
    <script>
      class UserCard extends HTMLElement {
        static get observedAttributes() {
          return ["name"];
        }
        _name = null;
        constructor() {
          super();
        }
        attributeChangedCallback(attr, _oldVal, newVal) {
          if (attr == "name") {
            this.name = newVal;
          }
        }
        set name(value) {
          debugger;
          this._name = name;
        }
        get name() {
          debugger;
          return this._name;
        }
      }
      window.customElements.define("user-card", UserCard);
    </script>
  </body>
</html>

由此,可以看到组件在页面渲染时会进入setter方法,而且 setAttribute,getAttribute 均进入到setter方法

document.getElementById('usercard').setAttribute('name','bob')  // 会进入setter
document.getElementById('usercard').getAttribute('name')  // 会进入getter,返回bob

扩展原生 HTML

假设您想创建一个更高级的<button>. 与其复制<button>的行为和功能,更好的选择是使用自定义元素逐步增强现有元素。

  • 扩展现有元素的主要好处是获得其所有特性(DOM 属性、方法、可访问性)。
  • Safari浏览器兼容性待提高
  • 要扩展一个元素,您需要创建一个继承自正确 DOM 接口的类定义。前面例子都是继承HTMLElement,现在更多继承例如,按钮HTMLButtonElement,图片HTMLImageElement
class FancyButton extends HTMLButtonElement {
    constructor() {
        super();
        this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
    }
    drawRipple(x, y) {
        let div = document.createElement('div');
        div.classList.add('ripple');
        this.appendChild(div);
        div.style.top = `${y - div.clientHeight/2}px`;
        div.style.left = `${x - div.clientWidth/2}px`;
        div.style.backgroundColor = 'currentColor';
        div.classList.add('run');
        div.addEventListener('transitionend', e => div.remove());
    }
}
customElements.define('fancy-button', FancyButton, {extends: 'button'});

注意:扩展基础元素时,对的调用 define() 略有变化。必需的第三个参数告诉浏览器您正在扩展哪个标签

  • 调用方式一:
<button is="fancy-button" disabled>Fancy button!</button>
  • 调用方式二:
let button = document.createElement('button', {is: 'fancy-button'});
button.textContent = 'Fancy button!';
button.disabled = true;
document.body.appendChild(button);
  • 调用范式三
let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;

以上就是微前端之Web组件自定义元素示例详解的详细内容,更多关于微前端Web组件自定义元素的资料请关注我们其它相关文章!

(0)

相关推荐

  • web2.0中流行的设计元素:颜色

    颜色的变化跟人类的智慧一样,是无穷的,每个阶段都会有流行的色彩,有属于一个时代的颜色!WEB2.0是一个概念,它宣扬,定位了一些东西,以用户为中心,群体智慧,分享等等,等等.同时顺应这些需求,一些顶尖的设计师和开发者赋予并领导了一些色彩流行趋势.我凭感觉做了部分归纳和收集,心理没大有底,把能想到的列出来希望跟大家一起探讨>>再做补充吧! ★淡淡的浅色系及延伸应用 此类颜色似乎最早被应用在一些文档处理软件比如word,和操作系统里(比如MacOSX)用于分类归档,标记,或者突出显示使用.此类颜色

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

    目录 写在前面 WC 到底是什么? 目前存在的缺陷 1.组件内部事件的回调 2.组件样式覆盖 3.组件内部资源相对路径问题 4.form表单类组件 value 获取问题 5.其它 写在后面 写在前面 最近致力于研究 Web components(以下简称WC),并且也初有成效的拿到了一定的结果,但今天想回过头来重新审视一下 WC. WC 到底是什么? 简单的讲,Web Component 就是把组件封装成 html 标签的形式,并且在使用时不需要写额外的 js 代码. 组件是前端的发展方向,抛开

  • Web纯前端“旭日图”实现元素周期表

    一.什么是旭日图 旭日图是在Excel 2016中新增的一种图表.有些类似饼图,饼图的优势是可以显示占比.但是饼图只能显示单级数据.旭日图用来表示多层级数据的占比.旭日图以一种分层方式显示,非常适合用来显示层级数据.层次结构中每个级别的比例通过1个圆环表示,离原点越近代表圆环级别越高,最内层的圆表示顶级结构,然后一层一层去看数据的占比情况. 我们通过一个简单的示例,初步感受一下旭日图的魅力. 季度 月份 周 销量 Q1 1月份 29 2月份 第一周 63 第二周 54 第三周 91 第四周 78

  • Web三大组件之Filter,Listener和Servlet详解

    目录 Filter:过滤器 Listener:监听器 servlet 总结 Filter:过滤器 1. 概念: * 生活中的过滤器:净水器,空气净化器,土匪. * web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能. * 过滤器的作用: * 一般用于完成通用的操作.如:登录验证.统一编码处理.敏感字符过滤... 2. 快速入门: 1. 步骤: 1. 定义一个类,实现接口Filter 2. 复写方法 3. 配置拦截路径 1. web.xml 2. 注解 2. 代码

  • JavaScript WebAPI、DOM、事件和操作元素实例详解

    目录 WebAPI DOM DOM树 DOM获取元素方式 document对象属性 事件 事件的使用步骤 事件的类型 操作元素 操作元素内容 操作元素属性 操作元素样式 排他思想 H5自定义属性 总结 WebAPI API:应用程序编程接口,是一些预先定义的函数,由某个软件开放给开发人员使用的,帮助开发者实现某种功能,开发人员无须访问源码.无须理解其内部工作机制细节,只需知道如何使用即可 简单理解: API 是给程序员提供的一种工具,以便能更轻松的实现想要完成的功能 WebAPI:主要针对浏览器

  • 微前端之Web组件自定义元素示例详解

    目录 我们知道的 Web组件使用 名称规范 组件传参数并可以写模板包括js和css Shadow Dom 影子节点 类中的构造函数和钩子函数 getter/setter属性和属性反射 扩展原生 HTML 我们知道的 第一:我们熟知的HTML标签有 a, p, div, section, ul, li, h2, article, head, body, strong, video, audio 等等 第二:我们知道,a标签是链接,p标签是段落,div是块级,h2是字体,strong 是粗体,vid

  • React元素与组件的区别示例详解

    目录 从问题出发 元素与组件 元素 组件 问题如何解决 自定义内容 第一种实现方式 第二种实现方式 第三种实现方式 从问题出发 我被问过这样一个问题: 想要实现一个 useTitle 方法,具体使用示例如下: function Header() { const [Title, changeTitle] = useTitle(); return ( <div onClick={() => changeTitle('new title')}> <Title /> </div

  • 比ant更丰富Modal组件功能实现示例详解

    目录 有哪些比ant更丰富的功能 render部分 渲染黑色蒙层 渲染弹框主体 设置body overflow:hiiden 有哪些比ant更丰富的功能 普通的modal组件如下: 我们写的modal额外支持,后面没有蒙版,并且Modal框能够拖拽 还支持渲染在文档流里,上面的都是fixed布局,我们这个正常渲染到文档下面: render部分 <RenderDialog {...restState} visible={visible} prefixCls={prefixCls} header={

  • Flutter CustomPaint自定义绘画示例详解

    目录 正文 CustomPaint 介绍 绘制点 PointMode3种模式 绘制线 和路径 绘制五子棋 总结 正文 CustomPaint是Flutter中用于自由绘制的一个widget,它与android原生的绘制规则基本一致,以当前Canves(画布)的左上角为原点进行绘制.在有些场景中,我们会需要绘制一些高度定制化的组件,比如 UI 设计师给我们出了个难题 —— 弄一个奇形怪状的边框.这个时候我们就不能直接使用 Flutter 自带的那些组件了,而是需要手动绘制组件,那就会需要用到 Cu

  • PHP与Web页面的交互示例详解二

    前言 在<PHP学习笔记-PHP与Web页面的交互1>笔记中讲解了form表单的一些属性,包括它的输入域标记.选择域标记和文字域标记的写法,接下来的内容就是讲如何获取表单数据以及PHP数据的传递,包括对各种控件值的获取. 插入表单 提交表单之前一定得有表单,当我们的表单创建完毕后可以将表单插入Web页中,代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://w

  • PHP与Web页面的交互示例详解一

    前言 这篇笔记记录的是Web表单的相关操作,Web表单主要用来在网页中发送数据到服务器.比如在日常开发中,提交注册时需要提交表单,表单从客户端传送到服务器,经过服务器处理后,再将用户所需要的信息传递回客户端,进而实现PHP与Web表单的交互. 表单 使用<form>元素,并在其中插入相关的表单元素,即可创建一个表单. 表单结构: <form name="form_name" method="method" action="url"

  • 基于gin的golang web开发:路由示例详解

    Gin是一个用Golang编写的HTTP网络框架.它的特点是类似于Martini的API,性能更好.在golang web开发领域是一个非常热门的web框架. 启动一个Gin web服务器 使用下面的命令安装Gin go get -u github.com/gin-gonic/gin 在代码里添加依赖 import "github.com/gin-gonic/gin" 快速启动一个Gin服务器的代码如下 package main import "github.com/gin-

  • svgicon组件使用方法示例详解

    目录 场景 编写SvgIcon组件 组件文件结构 icons文件结构 vue.config.js配置 最终效果 场景 最近在研发产品的过程中,ued切了很多svg的图片:咱们在使用过程中除了背景图再就是使用<img :src="url"/>进行使用. 在你进行公共组件编写的时候,使用图片路径这种方式编写完组件发布之后:在再项目中引入已发布的组件,在你运行代码的时候图片路径会附带上当前运行的域名,导致图片显示不出来. 那么怎么解决这种问题呢? 和UED进行沟通让他们把这种sv

  • js前端上传文件缩略图技巧示例详解

    目录 引言 文件对象简介 Blob File FileReader FormData 文件对象之间的关系 缩略图的实现 总结 引言 通常情况下,前端提交给服务器的数据格式为JSON格式,但很多时候用户想上传自己的头像.视频等,这些非文本数据的时候,就不能直接以JSON格式上传到后端了. 当我们要获取用户上传的文件,可以使用input表单项,将type属性值设置为“file”. <form action=""> <input type="file"

  • TypeScript前端上传文件到MinIO示例详解

    目录 什么是MinIO? 本地Docker部署测试服务器 上传的API TypeScript实现 1. XMLHttpRequest 2. Fetch API 3. Axios 从后端获取临时上传链接 上传文件 踩过的坑 1. presignedPutObject方式上传提交的方法必须得是PUT 2. 直接发送File即可 3. 使用Axios上传的时候,需要自己把Content-Type填写成为file.type 示例代码 什么是MinIO? MinIO 是一款高性能.分布式的对象存储系统.

随机推荐