[React]利用Webcomponent封装React组件

[React]利用Webcomponent封装React组件

为什么这么做

我个人认为,最重要的点是可以很方便地跨框架挂载和卸载wc元素(至少我在项目里是这么玩的),此外,基于wc的css沙箱以及它的shadowRoot机制,可以提供一套隔离机制,保证每个渲染组件的边界分明。

利用AI总结罗列了一下都有啥优点…

  1. 封装性:Web Components提供了一种封装UI组件的方法,使得组件可以在不同的框架或无框架环境中重用。
  2. 可重用性:封装为Web Components的React组件可以在任何支持Web Components的环境中使用,不限于React应用。
  3. 封装的样式和行为:Web Components允许你封装组件的HTML结构、样式和行为,确保样式和行为不会泄露到父组件或全局作用域。
  4. 独立性:Web Components封装的组件具有独立性,它们拥有自己的DOM树和作用域,不会影响外部环境。
  5. 易于集成:Web Components提供了一种标准化的集成方式,可以更容易地将React组件集成到其他Web应用中。
  6. 更好的性能:Web Components的自定义元素可以在不影响主线程的情况下进行升级和渲染,这有助于提高应用性能。
  7. 标准化:Web Components基于W3C标准,这意味着它们在不同的浏览器和环境中具有更好的一致性和兼容性。
  8. 易于维护:由于Web Components封装的组件具有清晰的接口和封装性,维护和更新组件变得更加容易。
  9. 样式隔离:Web Components的Shadow DOM技术可以确保组件的样式不会受到外部样式的影响,同时也防止组件内部样式泄露到外部。
  10. 生命周期管理:Web Components允许你定义组件的生命周期钩子,如connectedCallbackdisconnectedCallback等,这与React组件的生命周期方法类似。
  11. 跨框架使用:封装为Web Components的React组件可以被其他前端框架或库使用,例如Vue、Angular或原生JavaScript。
  12. 自定义元素:Web Components允许开发者定义自定义HTML元素,这些元素可以像标准HTML元素一样使用。
  13. 易于测试:Web Components的封装性使得测试组件变得更加简单,因为你可以独立于其他组件来测试它们。
  14. 更好的封装和抽象:Web Components提供了一种封装和抽象UI组件的方式,使得组件的实现细节对使用者是透明的。

Webcomponent入门

先来简单地过一下webcomponent的基础

官方文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components

示例

下面是一个最简单的示例,自定义了一种名为”simple-component“的元素,并且它没有shadowRoot(意味着它并没有与外界隔离样式)。

class SimpleComponent extends HTMLElement {
   
   
  constructor() {
   
   
    super();
    this.innerHTML = `<p>Hello, World!</p>`;
  }
}

customElements.define('simple-component', SimpleComponent);

下面是一个内容更丰富一些的示例,有基础的大概过一眼也知道大概了。

//  1.自定义标签都是用class 的形式去继承
class myDiv extends HTMLElement {
   
   
  // 监听
  static get observedAttributes() {
   
   
    return ['option']
  }
  constructor() {
   
   
    super()
    // 这样我们才能够去追加元素
    this.attachShadow({
   
    mode: 'open' })
  }
  // 重要:生命周期方法 开始
  connectedCallback() {
   
   
    console.log('connectedCallback生命周期')

    this.render({
   
   
      option: this.getAttribute('option'),
    })

    // 获取元素
    console.log(this.shadowRoot.querySelector('.content'))
    console.log('this.shadowRoot: ', this.shadowRoot)

    document.addEventListener('click', e => {
   
   
      // 重要:冒泡的顺序,通过这个可以判断有没有在鼠标内部进行点击
      if (e.composedPath().includes(this)) {
   
   
        console.log('点击了里面')
      }
    })

    this.shadowRoot.querySelector('.content').addEventListener('click', e => {
   
   
      console.log('e: ', e)
      // window.dispatchEvent
    })
  }
  // 重要:生命周期方法 重新渲染 .甚至还是第一次进行渲染,比connect还快
  // 会重新渲染 connectCallback
  attributeChangedCallback(attr, oldValue, newValue) {
   
   
    if (oldValue) {
   
   
      switch (attr) {
   
   
        case 'option':
          this.shadowRoot.querySelector('.title').textContent = newValue
      }
    }
    console.log('attributeChangeCallback', attr, oldValue, newValue)
  }

  borderAdd() {
   
   
    console.log('borderadd')
    this.shadowRoot.querySelector('.content').style.border = '3px solid green'
  }
  render(data) {
   
   
    let {
   
    option } = data

    // console.log()
    let nodeTemplate = document.createElement('template')
    nodeTemplate.innerHTML = `
      <div class="content" >
        <div class="title">${
     
     option} </div> 
        <slot name="container"></slot>
      </div>
  `
    let nodeStyles = document.createElement('style')

    // shadow dom 的样式绝对隔离
    // 重要: :host选择器可以选中根也就是my-div的样式。外面的选择器样式要高于这个
    nodeStyles.innerHTML = `
      :host(.active) .content{
        margin-top:20px;
        background:rgba(0,0,0,30%);
      }
      
      :host{
        display:block
      }
      
      .content{
        width:100px;
        height:100px;
        background:rgba(0,0,0,20%)
      }
        
      ::slotted([slot="container"]){
          display:none
      }

      ::slotted(.active){
        display:block
      }
 `

    this.shadowRoot.appendChild(nodeTemplate.content)
    this.shadowRoot.appendChild(nodeStyles)

    setTimeout(() => {
   
   
      this.borderAdd()
    }, 3000)
  }
}

// 名字必须小写 驼峰必须要转成横线
customElements.define('my-div', myDiv)

shadowRoot

一个Web组件可以有且仅有一个shadowRootshadowRoot是与该组件关联的影子DOM的根节点。当使用attachShadow方法创建影子DOM时,它会返回一个shadowRoot对象,这个对象是唯一的,并且与创建它的元素关联。

例如:

class MyComponent extends HTMLElement {
   
   
  constructor() {
   
   
    super();
    this.shadow = this.attachShadow({
   
   <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值