前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >web components 从0到1

web components 从0到1

作者头像
copy_left
发布2021-12-08 10:10:42
2940
发布2021-12-08 10:10:42
举报
文章被收录于专栏:方球方球方球

简单例子

// html
<c-button>
  click me
</c-button>


// js
// 定义元素类
class CButton extends HTMLElement{
  constructor(){
    super()
    this._root = this.attachShadow({ mode: 'closed' })
    this.tmp = document.createElement('template')
    this.tmp.innerHTML = `
       <style> 
         .c-button{ border: 0; background: #fff; color: orange }
       </style>
       <button class='c-button'>
         <slot />
       </button>
    `
    this._root.appendChild(this.tmmp.content.cloneNode(true))
  }
}
// 注册元素
window.customElements.define('c-button', CButton)

定义

Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。

web components 就是一套提供自定义元素的的技术

组成

  • Custom elements(自定义元素) 提供自定义元素内容及行为的能力
  • Shadow DOM(影子DOM)提供元素封装性
  • HTML templates(HTML模板)提供使用html标签编写自定元素的内容结构

定义元素类

自定义元素通过API window.customElements.define(name, constructor, options) 注册到当前文档中,

该接口主要接收参数:

  • name 元素标签名, 例如: 'c-title' , 使用时: <c-title> </c-title>
  • constructor 元素构造函数,需要继承 HTMLElement 类
  • options 配置属性

所以我们需要通过类的方式定义自定义元素

class CustomElement extends HTMLElement {
   constructor(){
      ....
   }
}
// 注册
window.customElements.define('', CustomElement)

html 模板

HTML templates 提供了两个新的元素标签

  • <template> 模板容器
  • <slot> 内容插槽

自定元素更接近于我们平常定义的UI组件,将具有一定功能的 html, css, js 封装在元素标签内。所以编写的流程也大致相同。

首先我们先定义内容的基础结构

const root = this.attachShadow({ mode: 'closed' })
const tmp = document.querySelector('template')
tmp.innerHTML = `
  // 内容样式 
 <style>
   .c-card{
      width: 400px;
      height: 300px;
      padding: 8px 10px;
      background: #fff;
      border: 1px solid #eee;
   }
  </style>


 // 内容结构
  <div class='c-card'>
     <slot />
  </div>
`
// 挂载内容
root.appendChild(tmp.content)

除了使用模板字符串,还可以使用 html 模板或 document.createElement 构建内容结构

  • html 模板
// html
<template id='tmp'>
  <div class='c-card'>
    <slot />
  </div>
</template>


// js
class Card extends HTMLElement{
  construction(){
    ...
    const tmp = document.querySelector('#tmp')
    root.appendChild(tmp.content)
  }
}
// <template> 内容不会直接显示在html文档中
  • createElement
class Card extends HTMLElement{
  construction(){
    ...
    const tmp = document.createElement('template')
    const slot = document.createElement('slot‘)
    
    card.classList.add('c-card')
    card.appendChild(slot)
    tmp.addpendChild(card)
    root.appendChild(tmp.content)
  }
}

获取/自定义属性

  • 字段属性
// html
<c-card follow='10'> // 在模板中设置属性值
</c-card>


// js
{
  constructor(){
    ...
     const card = document.createElement('div')
    // 通过 getAttribute 查询元素上是否设置自定义属性值
     const follow =  this.getAttribute('title') || 0
     console.log('follow: ', follow, typeof follow) // 值类型未字符串
    card.innerHTML = follow
  }
}
// 使用js设置属性值
const card = document.querySelector('c-card')
card.setAttribute('follow', 20)

这里有几个问题: 1. 通过 getAttribute 获取的值,类型都是字符串。2. 后续修改属性时,没有响应式的修改属性内容, 如果需要响应属性修改,需要配置标签的周期钩子。

  • getter / setter
{
  constructor(){
    this._value = 0
  }
  // 除了通过 getAttribute 获取属性外,
  // 也可以通过 getter, setter 定义属性
  get value(){
     return this._value
  }
  set value(v){
     this._value = v
      console.log('value type: ',typeof v)
  }
}
// 修改属性值
const card = document.querySelector('c-card')
card.value = 20
// value type: number

使用 getter , setter 后,可以通过直接赋值的方式设置属性值。 并且获取的值为原对象值类型,而非字符串

属性响应

上面修改非属性值时,我们无法响应属性的修改。要实现对属性的响应,需要依赖 attributeChangedCallback observedAttributes 两个属性

{
  // 属性响应函数, 属性修改后将触发该函数, 类似 vue 的watch函数
  attributeChangedCallback(name, oldValue, newValue){
    console.log(`属性名称: ${name}`)
    console.log(`旧属性值: ${oldValue}`)
    console.log(`新属性值: ${newValue}`)
  }
  // 并不是所有的属性都触发 attributeChangedCallback,只有在 observedAttributes 内注册的属性,才能触发回调
  // 注册触发响应的属性名称
  static observedAttributes = ['value']
  // observedAttributes 也可以为 getter, setter
}

生命周期钩子

自定义元素内可以配置相应的周期钩子,以执行不同任务.

  • connectedCallback 插入时
  • disconnectedCallback 删除时
  • adoptedCallback 移动时
  • attributeChangedCallback 属性修改时

自定义事件

{
  constructor(){
    const card = document.createElement('div')
    card.addEventListener('click', () => {
      // 创建自定义事件
      const event = new CustomEvent('cardClick', {detail: 'from card'})
      // 抛出事件
      this.dispatchEvent(event)
    })
  }
}
// 挂载事件监听
cont card = document.querySelector('c-card')
card.addEventListener('cardClick', ({detail}) => console.log(detail))

通信

在了解了自定义属性和自定事件后, 基本就满足了元素内与元素外通信的条件了。

模式类似vue的父子通信, 通过自定义属性获取外部值, 通过自定义事件向外抛出数据

{
  constructor(){
    this._value = 0
    this._root = this.attachShadow({ mode: 'closed' })
    this._rootElement = document.createElement('template')
    this._rootElement.innerHTML = `
<div>
 <button> add </button>
</div>
`
this._rootElement.content.querySelector('button')
.addEventListener('click', () => {
const event = new CustomEvent('add', {detail: this._value + 1})
this.dispatchEvent(event)
})
  }
  render(){
    const ele = document.createElement('div')
    ele.innerHTML = this._value
    this._root.appendChild(ele)
 }
 get value(){ return this._value }
 set value(n){ this._value = n; this.render() }
}
const ele = document.querySelect('element')
// 设置初始值
ele.value = 10
// 添加响应回调
ele.addEventListener('add', ({detail}) => {
  ele.value = detail
})

插槽

使用<slot /> 可以为标签添加嵌套功能, 与 vue slot 类似。

{
  constructor(){
    const tmp = `
      <div>
        <div>
          默认插槽: <slot />
        </div>
        <div>
          具名插槽: <slot name='sub' />
        </div>
      </div>
    `
    ...
  }
}
// html
<element>
  <div name='sub'> 附属信息 </div>
  <div> 主内容 </div>  
</element>

隐藏 / 开放

shadow DOM 提供了隐藏元素实现的能力。外部将不能影响或获取到内部元素, 通过设置mode 类型开启 or 关闭。

{
  constructor(){
    // mode 的值可以为 'open' | 'closed'
    this._root = this.attachShadow({ mode: 'closed' }) // 禁止外部访问内部节点
  } 
}

总结

web compoents 可以看作一种官方的组件化方案, 在不依赖其他MVVM框架或编译器的情况下,实现通用性组件。

在开发中,现有的API都比较简略,实际应用依然需要更上层的封装或工程化依赖做辅助。

参考

MDN Web Components

阮一峰 Web Components 入门实例教程

Web Components Tutorial or Beginners

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021/11/4 上,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简单例子
  • 定义
  • 组成
    • 定义元素类
      • html 模板
        • 获取/自定义属性
          • 属性响应
            • 生命周期钩子
              • 自定义事件
                • 通信
                  • 插槽
                    • 隐藏 / 开放
                      • 总结
                        • 参考
                        相关产品与服务
                        容器服务
                        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档