詳解vue3.2新增的defineCustomElement底層原理
Web Components
Web Components 是一套不同的技術,允許您創建可重用的定制元素(它們的功能封裝在您的代碼之外)並且在您的web應用中使用它們。
相當於是瀏覽器原生的定義組件的方式,不用通過vue或者react這些框架實現組件的定義
customElements
概述
customElements 是Window對象上的一個隻讀屬性,接口返回一個CustomElementRegistry 對象的引用,可用於註冊新的 custom elements,或者獲取之前定義過的自定義元素的信息。
HTMLTemplateElement 內容模板元素
概述
HTML內容模板(<template>)元素是一種用於保存客戶端內容機制,該內容在加載頁面時不會呈現,但隨後可以(原文為 may be)在運行時使用JavaScript實例化。
將模板視為一個可存儲在文檔中以便後續使用的內容片段。雖然解析器在加載頁面時確實會處理<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.createDocumentFragment('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-shadow-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-shadow-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元素可以從js外部訪問根節點 // 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', '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)}, ${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內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Web componentd組件內部事件回調及痛點剖析
- 微前端之Web組件自定義元素示例詳解
- Web Components實現類Element UI中的Card卡片
- 詳解JavaScript如何創建一個非自動播放的GIF網絡組件
- JS如何實現一個單文件組件