Web Component 简单示例

前言

Web Component: 创建可重用的定制元素

学习内容来源:

调试设置:

谷歌浏览器:设置------preferences------Elements------Show user agent shadow DOM打钩

基本概念

  • Custom element(自定义元素):class或者function,定义组件api。
  • Shadow DOM(影子 DOM):附加到元素中,可隔离样式。
  • HTML template(HTML 模板): <template><slot> 可以编写不在呈现页面中显示的标记模板。

自定义组件

  1. 注册组件
js 复制代码
// TodoItem.js
class TodoItem extends HTMLElement{}
customElements.define("todo-item", TodoItem)  // 传入组件名称与组件
html 复制代码
<!--index.html-->
<!--使用-->
<todo-item></todo-item>
  1. 创建模板
js 复制代码
// TodoItem.js
const template=document.createElement("template")
template.innerHTML=`
    <style>
        label{
            display: block;
        }
        .description{
            color: #a9a9a9;
            font-size: .8em;
        }
    </style>

    <label>
        <input type="checkbox" />
        <slot></slot>
        <span class="description">
            <slot name="description"></slot>
        </span>
    </label>
`

也可以直接写在html中

html 复制代码
<!--index.html-->
<template id="myTemp">
    <style>
        label{
            display: block;
        }
        .description{
            color: #a9a9a9;
            font-size: .8em;
        }
    </style>

    <label>
        <input type="checkbox" />
        <slot></slot>
        <span class="description">
            <slot name="description"></slot>
        </span>
    </label>
<template>
  1. 开启shadow dom
js 复制代码
// TodoItem.js
class TodoItem extends HTMLElement{
    constructor(){
        super()
        // ++ 开启shadow dom
        const shadowDom = this.attachShadow({ mode : "open"})
        // ++ 如果模板是写在js中,直接克隆模板,并把模板加入到shadow dom中:
        shadowDom.appendChild(template.content.cloneNode(true))
        
        // 否则需要获取一下模板: 
        // let template = ducoment.querySelector('#myTemp')
        // shadowDom.appendChild(template.content.cloneNode(true))
    }
}

现在可以在插槽中加入一些内容。在模板中设置的<style>将不会影响到外边的元素:

html 复制代码
<todo-item>
    <!--内容会加到默认slot中 -->
    todo1    
    
    <!--内容会加到description slot中 -->
    <span slot="description">其他描述</span> 
</todo-item>
  1. 生命周期
js 复制代码
// TodoItem.js

class TodoItem extends HTMLElement{
    constructor(){
        super()
        const shadowDom = this.attachShadow({ mode : "open"})
        shadowDom.appendChild(template.content.cloneNode(true))
    }
    
	// ++ 自定义元素挂载时被调用
	connectedCallback() {}
	// ++ 自定义元素卸载时被调用
	disconnectedCallback() {}
}

内置元素扩展与组件属性

  1. 注册组件:继承ul标签
js 复制代码
// ExpandableList.js
class ExpandableList extends HTMLUListElement {}

customElements.define('expandable-list', ExpandableList, {
   extends: 'ul',
});
  1. 定义组件
js 复制代码
class ExpandableList extends HTMLUListElement {
   constructor() {
      super();
      this.style.position = 'relative';

	  // 创建一个子元素
      this.toggleBtn = document.createElement('button');
      this.toggleBtn.style.position = 'absolute';
      this.toggleBtn.style.border = 'none';
      this.toggleBtn.style.background = 'none';
      this.toggleBtn.style.padding = 0;
      this.toggleBtn.style.top = 0;
      this.toggleBtn.style.left = '5px';
      this.toggleBtn.style.cursor = 'pointer';
      this.toggleBtn.innerText = '>';
      this.appendChild(this.toggleBtn);
      
	 // 定义点击事件
      this.toggleBtn.addEventListener('click', () => {
         this.dataset.expanded = !this.isExpanded;
      });

      // 获取某个属性
      console.log(this.getAttr('name'))
   }

   // 获取属性
    private getAttr (key) {
		return this.getAttribute(key)
	}

   // 计算属性
   get isExpanded() {
      return (
         this.dataset.expanded !== 'false' && this.dataset.expanded !== null
      );
   }

   // 监听属性
   static get observedAttributes() {
      return ['data-expanded'];
   }

  // 监听到上方属性变化后调用
   attributeChangedCallback(name, oldValue, newValue) {
      this.updateStyles();
   }


   updateStyles() {
      const transform = this.isExpanded ? 'rotate(90deg)' : '';
      this.toggleBtn.style.transform = transform;
      [...this.children].forEach((child) => {
         if (child !== this.toggleBtn) {
            child.style.display = this.isExpanded ? '' : 'none';
         }
      });
   }
   
   // 挂载后调用
   connectedCallback() {
      this.updateStyles();
   }
}

customElements.define('expandable-list', ExpandableList, {
   extends: 'ul',
});
  1. 使用:通过is传入扩展
html 复制代码
<ul is="expandable-list" data-expanded name="myul">
    <li>apple</li>
    <li>banana</li>
</ul>
相关推荐
江号软件分享几秒前
轻松解决Office版本冲突问题:卸载是关键
前端
致博软件F2BPM7 分钟前
Element Plus和Ant Design Vue深度对比分析与选型指南
前端·javascript·vue.js
慧一居士1 小时前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead1 小时前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码6 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子7 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年7 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子7 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina7 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路8 小时前
React--Fiber 架构
前端·react.js·架构