当前位置: 首页 > news >正文

4 html5 web components原生组件详细教程

web components 前面我们已经介绍过,这一期我们就来讲一讲具体用法和这其中的关键只是点:

1 基本使用

如果我们想实现一个封装的原生组件,那就离不开使用js去封装,这里主要就是基于HTMLElement这个类,去创建创建一个子类,然后使用customElements.define去页面中注册这个组件。

export class dialog extends HTMLElement {constructor() {super(); // 必须首先调用 super()// 正确的做法if (!this.shadowRoot) {this.attachShadow({ mode: "open" }); // 或者 'closed' 根据需求}// 创建并添加样式到影子DOM中const style = document.createElement("style");style.textContent = `dialog {padding: 0;width: 300px;height: 200px;border: 1px solid #888;}dialog::backdrop {background: rgba(0,0,0,0.5);}dialog .title {box-sizing: border-box;padding: 0 5px;width: 100%;height: 30px;line-height: 30px;color: #fff;background: var(--primary-color);}dialog .close {float: right;cursor: pointer;}dialog .footer{margin-top: 40px;}`;this.shadowRoot.appendChild(style);// 创建并添加元素到影子DOM中const div = document.createElement("div");const mode = this.getAttribute("mode") || "show";const content = this.getAttribute("content") || "我是默认内容";div.classList.add("custom-dialog");const form = `<form method="dialog">${content}<div class="footer"><button type="submit" value="submit">提交</button><button type="submit" value="cancel">取消</button></div></form>`;const dialogContent = `<dialog class="dialog"><div class="title">dialog弹窗 <span class="close">X</span></div><div class="content">${["show", "modal"].includes(mode) ? content : form}</div></dialog><button class="show-dialog">${mode}弹窗</button>`;div.innerHTML = dialogContent;this.shadowRoot.appendChild(div);this.dialog = this.shadowRoot.querySelector(".dialog");const closeEvent = (detail) =>new CustomEvent("onclose", {detail,bubbles: true, // 允许事件冒泡composed: true, // 允许事件穿透shadow DOM});this.shadowRoot.querySelector(".show-dialog").addEventListener("click", () => {if (mode === "show") {this.dialog.show();} else {this.dialog.showModal();}});this.shadowRoot.querySelector(".close").addEventListener("click", () => {this.dialog.close("top");});this.dialog.addEventListener("close", (e) => {// 触发自定义事件this.dispatchEvent(closeEvent(this.dialog.returnValue));});}
}

以上这个代码,我就封装了一个基于html5新标签dialog封装的弹窗组件,一下演示使用方法。

                <custom-dialog></custom-dialog><custom-dialogmode="modal"content="modal模式演示有close监听"></custom-dialog><custom-dialogmode="modal-form"content="modal模式演示带有表单"></custom-dialog>

以上代码我来简单分解下:

1 定义style

首先就是要给shadow Dom中要插入style标签和你自定义内容的html代码,这样来实现样式和html代码封装,主要的还是要实现代码的隔离。当然你接下来的交互,自然也要约束在你组件内部。

2 组件内查找节点

当然你js中节点事件处理,还是要基于this.shadowRoot.querySelector去做节点查找,然后使用原生的方法去做事件监听。

3 自定义事件派发:

 new CustomEvent("onclose", {detail,bubbles: true, // 允许事件冒泡composed: true, // 允许事件穿透shadow DOM});

这里就是定义一个自定义事件,然后使用this.dispatchEvent(closeEvent(this.dialog.returnValue))派发出去。

4 注册组件

接下来我们聊聊这个注册组件,以上代码仅仅是你定义组件,页面上还并不能直接使用。customElements.define(`custom-${i}`, dialog); 使用以上方法,才能在页面上以自己定义的名称使用。

2 插槽使用

插槽的使用给我们页面开发带来很大的方便,这里我们演示一下插槽的使用方法。在web components中插槽沿革执行了,组件的shadow-dom的特性,即插槽部分的样式定义和js是不受shadow-dom的this.shadowRoot的影响的,因为插槽为自定义内容,其内容被渲染也不属于组件shadow-dom内部,所以插槽被渲染出来dom结构并不在组件内部。

1 通过slot来定义插槽

<div slot="desc">纯html+css实现,无法实现更为复杂的功能和交互,而且无法实现shadow-root实现样式隔离</div>

2 使用插槽

在组件内部通过 <slot name="">来使用,如下。

const editorHtml = `<div class="editor-desc"><slot name="desc"></slot></div><div class="editor-code"><div class="code-type">${codeType}</div><div class="code-container"><div class="code-lines"></div><div class="code-content"><slot name="code"></slot></div></div></div>`;

3 插槽的渲染

其渲染出来的效果如图:

f998e35753624121b1ae9f800be7b0dd.png

这里需要特别注意插槽为自定义内容,其内容被渲染也不属于组件shadow-dom内部,所以插槽被渲染出来dom结构并不在组件内部,特别实在做样式控制时要特别注意,需要在使用组件的页面上定义样式。如果是在嵌套环境更要注意。

3 样式部分

因为shadow-dom的特性,我们样式只可以写在组件内部。但是也有一些技巧。这里我分享一个就是做样式分离的方法。

const style = document.createElement("link");style.setAttribute("rel", "stylesheet");style.setAttribute("href", "./src/assets/editor.css");this.shadowRoot.appendChild(style);

 1 分离css方法

创建一个link标签,将样式从外部链接进来,这样就可以不用做style这么麻烦了。

当然后面我再讲一讲如何做html代码分离。

样式分离了,但是这里面又有一个问题就是,样式穿透问题。这里就不得不说几个基于web component的味蕾和和选择器。

:host 这个使用在组件样式内部,表示跟组件。使用这个我们可以约束根组件的样式。通常我们封装的组件都要通过一个div元素放在根组件内部,而这个div通常我们并没有给其设置类名。这时使用这个伪类就比较好。

:host() 只选择自身包含特定选择器的自定义元素;

:host-context() 选择拥有特定选择器父元素的自定义元素。

这些选择器后面详细来讲,这里我主要谈一下,样式的从父级项向下穿透的样式规则和需求。比如我们通常定义样式有全局级别的,页面级别的,还有小组件级别的。但是现在这种shadow-dom样式的完全隔离,向实现其实比较困难。

2 样式控制

我先说第一种:页面级别,其页面级别样式可以直接影响到自己页面本身和插槽的样式,这个也很好理解。因为插槽本身就是就是小组件外的内容,样式的作用域能控制到当前页面,也就能控制当前页面内的插槽。特别是在组件嵌套的情况下,当家控制要是要特别注意。类似于这个:host/:host()都可以往出派生,都比较容易控制。

第二种情况就比较特殊了,就是全局样式。全局样式如果想穿透组件,控制组件内部,其实是比较麻烦的。这里我来详细讲一下。主要使用了::part伪元素。

.page::part(case-container) {margin: 10px 0;border: 1px solid #f6f6f6;
}
.page::part(case-title) {padding: 0 10px;line-height: 25px;font-size: 16px;
}.page::part(case-content) {background-color: #f6f6f6;padding: 10px;
}

3 样式穿透

这里大家看到,这个::part()括号中的是对应的组件.page中part属性的属性值的元素。

c68d15f1e78541c8a0d7001279f2bed6.png

如上图,这些html标签都被协商了part的key-value键值对。

前面的.page大家注意,这个地方本来是要写组件名称的,但如果写组件名称,作用域就太小了,所以我们给由相同需求的组件写一个类名,这样作用范围就更广,来实现更多需求全局样式穿透。

这个地方是给跟组件设置class的方法,建议大家在这个地方处理,这样的化,组件本身就具备了类名。但是::part不能派生,这也是个限制,希望官方后期能更新这个限制。

 this.setAttribute("class", "page");

1121b8f8cce64cb8aeaf8e9984367888.png

今天,就先分享到这,我的第一部分的原生html-web components项目部分已经马上完成,即将和大家见面。大家请关注。 

 

http://www.lryc.cn/news/445890.html

相关文章:

  • nginx+keepalived健康检查案例详解(解决nginx出现故障却不能快速切换到备份服务器的问题)
  • 什么是AI大模型?
  • 建造者模式__c#
  • 学习MRI处理过程中搜到的宝藏网站
  • 【C语言】const char*强制类型转换 (type cast)的告警问题
  • python-比较月亮大小/数组下标/人见人爱a+b
  • 什么是组态、组态的应用场景介绍
  • Java项目: 基于SpringBoot+mybatis+maven实现的智能推荐卫生健康系统分前后台(含源码+数据库+开题报告+任务书+毕业论文)
  • 本地生活商城开发搭建 同城O2O线上线下推广
  • 41. 如何在MyBatis-Plus中实现批量操作?批量插入和更新的最佳实践是什么?
  • LlamaIndex 的Node节点后处理器模块介绍
  • Dubbo 如何使用 Zookeeper 作为注册中心:原理、优势与实现详解
  • Linux:进程间通信之命名管道
  • UE4_后期处理七—仿红外线成像效果
  • 静态路由和默认路由(实验)
  • TCP: Textual-based Class-aware Prompt tuning for Visual-Language Model
  • 2024年软考网络工程师中级题库
  • CSS的盒子模型(Box Model)
  • 用OpenSSL搭建PKI证书体系
  • scp 命令:在两台主机间远程传输文件
  • 家用迷你洗衣机哪款质量高?五大热销高分单品强势来袭
  • rpm 命令:RedHat底层包管理器
  • Xilinx 使用DDS实现本振混频上下变频
  • ClickHouse-Kafka Engine 正确的使用方式
  • PTA L1-071 前世档案
  • 解决mac下 Android Studio gradle 下载很慢,如何手动配置
  • 第三篇 第17章 工程计量与支付
  • [半导体检测-1]:半导体检测概述
  • 公共字段自动填充
  • 超详细 Git 教程:二十篇博客,三万字干货