1. 程式人生 > 程式設計 >詳解vue3.2新增的defineCustomElement底層原理

詳解vue3.2新增的defineCustomElement底層原理

目錄
  • Web Components
    • customElements
      • 概述
    • HTMLTemplateElement內容模板元素
      • 概述
      • 常用屬性
    • ShadowRoot
      • 概述

Web Components

Web Components 是一套不同的技術,允許您建立可重用的定製元素(它們的功能封裝在您的程式碼之外)並且在您的web應用中使用它們。

相當於是瀏覽器原生的定義元件的方式,不用通過或者react這些框架實現元件的定義

customElements

概述

customElements 是Window物件上的一個只讀屬性,介面返回一個CustomElementRegistry 物件的引用,可用於註冊新的 custom elements,或者獲取之前定義過的自定義元素的資訊。

HTMLTemplateElement內容模板元素

概述

HTML內容模板(<template>)元素是一種用於儲存客戶端內容機制,該內容在載入頁面時不會呈現,但隨後可以(原文為 may be)在執行時使用例項化。
將模板視為一個可儲存在文件中以便後續使用的內容片段。雖然解析器在載入頁面時確實會處理<template>元素的內容,但這樣做只是為了確保這些內容有效;但元素內容不會被渲染。

常用屬性

content 獲取DocumentFragment元素片段的內容
相當於通過document.createDocumentFragment()建立的元素片段,

  <!-- 定義template片段 -->
  <template id="element-template">
    <div>test-template</div>
  </template>

  <script>
    /* 獲取template片段 */
    const ele = document.getElementById('element-template')
    ele.content instanceof DocumentFragment  //true

    /* 通過createDocumentFragment建立html片段*/
    const div = document.createDocumentFr
agment('div') div instanceof DocumentFragment //true /* 結論 */ // 定義在html上的template獲取它的content相當於和通過createDocumentFragment建立的html片段是一個東西 </script>

ShadowRoot

概述

Shadow DOM API 的 ShadowRoot 介面是一個 DOM 子樹的根節點,它與文件的主 DOM 樹分開渲染。
你可以通過使用一個元素的 Element.shadowRoot 屬性來檢索它的參考,假設它是由 Element.attachShadow() 建立的並使 mode 設定為 open.

通過 Element.attachShadow()掛載影子DOM

完整的演示程式碼

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <test-shadVofdLkGuiRow-root></test-shadow-root>

  <template id="temEle">
    <style>
      .main{
        color: #f00;
      }
    </style>
    <div class="main">
      我是template片段
      <!-- 使用插槽 -->
      <slot name="header"></slot>
    </div>
  </template>
  <test-template-ele>
    <!-- 定義插槽 -->
    <style>
      .slot{
        color: rgb(87,28,223);
      }
    </style>
    <div class="slot" slot="header">我是slot</div>
  </test-template-ele>

  <!-- 生命週期測試 -->
  <div id="moveDiv">
    <button id="add">新增</button>
    <button id="update">更新</button>
    <button id="move">移動</button>
    <button id="remove">刪除</button>
  </div>

  <!-- 通過is掛載 -->
  <div is="test-is-com">
    <div>AAA</div>
  </div>


  <script>
    /* 自定義web-components */
    customElements.define('test-shVofdLkGuiRadow-root',class extends HTMLElement {
      /* 當test-shadow-root元件被掛載到DOM上時,執行建構函式 */
      constructor() {
        super()
        const shadowRoot = this.attachShadow({mode: 'open'}) //給指定的元素掛載影子DOM
        // 當執行 this.attachShadow()方法時,shadowRoot被掛載建構函式中,可以通過this訪問
        // mode open shadow root元素可以從外部訪問根節點
        // mode closed  拒絕從js外部訪問關閉的shadow root節點
        // console.log('執行',this)
        const div = document.createElement('div')
        div.textContent = '我是div的內容'
        // shadowRoot.appendChild()
        // console.log('this',this.shadowRoot)
        shadowRoot.appendChild(div)
        // this.shadowRoot === shadowRoot  true
      }
    })

    /* 通過template自定義HTMLTemplateElement */
    customElements.define('test-template-ele',class extends HTMLElement {
      constructor() {
        super()
        const temEle = document.querySelector('#temEle')
        const templateContent = temEle.content //獲取html片段
        // console.log('AA',templateContent instanceof DocumentFragment) //true
        // templateContent
        // 建立影子DOM,用於掛載template的片段
        const shadowRoot = this.attachShadow({mode: 'open'})
        // console.log('shadowRoot',shadowRoot)
        shadowRoot.appendChild(templateContent)
      }
    })

    /* 通過js建立web-元件,測試生命週期函式 */
      class LifeCycle extends HTMLElement {
        static get observedAttributes() {  //必須新增元件上的屬性,才能觸發attributeChangedCallback
          return ['c','l'];
        }

        constructor() {
          super()
          const shadowRoot = this.attachShadow({mode: 'open'})
          const div = `<div>
            <heaher>我的頭</header>
            <div>內容</div>
            <footer>尾部</footer>
          </div>`
          shadowRoot.innerHTML = div
        }

        connectedCallback() {  //新增時,執行
          console.log('新增')
        }
        disconnectedCallback() {//刪除時,執行
          console.log('disconnectedCallback')
        }
        adoptedCallback() {
          console.log('adoptedCallback')
        }
        attributeChangedCallback() {  //屬性被改變時
          console.log('attributeChangedCallback')
        }
      }

      customElements.define('test-life-cycle',LifeCycle)

      const add = document.querySelector('#add')
      const update = document.querySelector('#update')
      const move = document.querySelector('#move')
      const remove = document.querySelector('#remove')
      const moveDiv = document.querySelector('#moveDiv')
      let testLifeDom = null

      function random(min,max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
      }

      add.addEventListener('click',() => {
        testLifeDom = document.createElement('test-life-cycle')  //建立上面定義的自定義元件
        // console.log('testLifeDom',testLifeDom)
        document.body.appendChild(testLifeDom);
        testLifeDom.setAttribute('l',www.cppcns.com '100');
        testLifeDom.setAttribute('c','red');
        console.log('add',testLifeDom)
      })

      update.addEventListener('click',() => {
        const div = '<div>更新後</div>'
        // console.log('update',testLifeDom.shadowRoot.innerHTML)
        testLifeDom.shadowRoot.innerHTML = div
        testLifeDom.setAttribute('l',random(50,200));
        testLifeDom.setAttribute('c',`rgb(${random(0,255)},${random(0,255)})`);
      })

      move.addEventListener('click',() => {
        console.log('moveDiv',moveDiv)
        moveDiv.appendChild(testLifeDom)
      })

      remove.addEventListener('click',() => {
        console.log('remove')
        document.body.removeChild(testLifeDom);
      })

      /* 通過is掛載元件 */

      customElements.define('test-is-com',class extends HTMLDivElement {
        constructor() {
          super()
          console.log('掛載',this.innerHTML)
          // 通過掛載,this,就是當前被掛載的元素例項,通過這種方式,可以實現一些操作
        }
      },{extends: 'div'})

  </script>
</body>
</html>

到此這篇關於詳解vue3.2新增的defineCustomElement底層原理的文章就介紹到這了,更多相關vue3.2 defineCustomElement內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!