詳解vue3.2新增的defineCustomElement底層原理
Web Components
Web Components 是一套不同的技術(shù),允許您創(chuàng)建可重用的定制元素(它們的功能封裝在您的代碼之外)并且在您的web應(yīng)用中使用它們。
相當(dāng)于是瀏覽器原生的定義組件的方式,不用通過vue或者react這些框架實(shí)現(xiàn)組件的定義
customElements
概述
customElements 是Window對(duì)象上的一個(gè)只讀屬性,接口返回一個(gè)CustomElementRegistry 對(duì)象的引用,可用于注冊(cè)新的 custom elements,或者獲取之前定義過的自定義元素的信息。
HTMLTemplateElement 內(nèi)容模板元素
概述
HTML內(nèi)容模板(<template>)元素是一種用于保存客戶端內(nèi)容機(jī)制,該內(nèi)容在加載頁面時(shí)不會(huì)呈現(xiàn),但隨后可以(原文為 may be)在運(yùn)行時(shí)使用JavaScript實(shí)例化。
將模板視為一個(gè)可存儲(chǔ)在文檔中以便后續(xù)使用的內(nèi)容片段。雖然解析器在加載頁面時(shí)確實(shí)會(huì)處理<template>元素的內(nèi)容,但這樣做只是為了確保這些內(nèi)容有效;但元素內(nèi)容不會(huì)被渲染。
常用屬性
content 獲取DocumentFragment 元素片段的內(nèi)容
相當(dāng)于通過document.createDocumentFragment()創(chuàng)建的元素片段,
<!-- 定義template片段 -->
<template id="element-template">
<div>test-template</div>
</template>
<script>
/* 獲取template片段 */
const ele = document.getElementById('element-template')
ele.content instanceof DocumentFragment //true
/* 通過createDocumentFragment創(chuàng)建html片段*/
const div = document.createDocumentFragment('div')
div instanceof DocumentFragment //true
/* 結(jié)論 */
// 定義在html上的template獲取它的content相當(dāng)于和通過createDocumentFragment創(chuàng)建的html片段是一個(gè)東西
</script>
ShadowRoot
概述
Shadow DOM API 的 ShadowRoot 接口是一個(gè) DOM 子樹的根節(jié)點(diǎn), 它與文檔的主 DOM 樹分開渲染。
你可以通過使用一個(gè)元素的 Element.shadowRoot 屬性來檢索它的參考,假設(shè)它是由 Element.attachShadow() 創(chuàng)建的并使 mode 設(shè)置為 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">移動(dòng)</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 {
/* 當(dāng)test-shadow-root組件被掛載到DOM上時(shí),執(zhí)行構(gòu)造函數(shù) */
constructor() {
super()
const shadowRoot = this.attachShadow({mode: 'open'}) //給指定的元素掛載影子DOM
// 當(dāng)執(zhí)行 this.attachShadow()方法時(shí),shadowRoot被掛載構(gòu)造函數(shù)中,可以通過this訪問
// mode open shadow root元素可以從js外部訪問根節(jié)點(diǎn)
// mode closed 拒絕從js外部訪問關(guān)閉的shadow root節(jié)點(diǎn)
// console.log('執(zhí)行', this)
const div = document.createElement('div')
div.textContent = '我是div的內(nèi)容'
// 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
// 創(chuàng)建影子DOM,用于掛載template的片段
const shadowRoot = this.attachShadow({mode: 'open'})
// console.log('shadowRoot', shadowRoot)
shadowRoot.appendChild(templateContent)
}
})
/* 通過js創(chuàng)建web-組件,測試生命周期函數(shù) */
class LifeCycle extends HTMLElement {
static get observedAttributes() { //必須添加組件上的屬性,才能觸發(fā)attributeChangedCallback
return ['c', 'l'];
}
constructor() {
super()
const shadowRoot = this.attachShadow({mode: 'open'})
const div = `<div>
<heaher>我的頭</header>
<div>內(nèi)容</div>
<footer>尾部</footer>
</div>`
shadowRoot.innerHTML = div
}
connectedCallback() { //添加時(shí),執(zhí)行
console.log('添加')
}
disconnectedCallback() {//刪除時(shí),執(zhí)行
console.log('disconnectedCallback')
}
adoptedCallback() {
console.log('adoptedCallback')
}
attributeChangedCallback() { //屬性被改變時(shí)
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') //創(chuàng)建上面定義的自定義組件
// 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,就是當(dāng)前被掛載的元素實(shí)例,通過這種方式,可以實(shí)現(xiàn)一些操作
}
}, {extends: 'div'})
</script>
</body>
</html>
到此這篇關(guān)于詳解vue3.2新增的defineCustomElement底層原理的文章就介紹到這了,更多相關(guān)vue3.2 defineCustomElement內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-cli是什么及創(chuàng)建vue-cli項(xiàng)目的方法
vue-cli是 vue 官方提供的、快速生成 vue 工程化項(xiàng)目的工具,支持創(chuàng)建vue2和vue3的項(xiàng)目,本文給大家詳細(xì)講解vue-cli是什么及創(chuàng)建vue-cli項(xiàng)目的方法,感興趣的朋友跟隨小編一起看看吧2023-04-04
詳解vue-router2.0動(dòng)態(tài)路由獲取參數(shù)
本篇文章主要介紹了詳解vue-router2.0動(dòng)態(tài)路由獲取參數(shù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
vue+el-select?多數(shù)據(jù)分頁搜索組件的實(shí)現(xiàn)
本文主要介紹了vue+el-select?多數(shù)據(jù)分頁搜索組件的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
vue如何使用媒體查詢實(shí)現(xiàn)響應(yīng)式
這篇文章主要介紹了vue如何使用媒體查詢實(shí)現(xiàn)響應(yīng)式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
vue中給el-radio添加tooltip并實(shí)現(xiàn)點(diǎn)擊跳轉(zhuǎn)方式
這篇文章主要介紹了vue中給el-radio添加tooltip并實(shí)現(xiàn)點(diǎn)擊跳轉(zhuǎn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
import.meta.glob() 如何導(dǎo)入多個(gè)目錄下的資源(最新推薦)
import.meta.glob() 其實(shí)不僅能接收一個(gè)字符串,還可以接收一個(gè)字符串?dāng)?shù)組,就是匹配多個(gè)位置,本文給大家介紹import.meta.glob() 如何導(dǎo)入多個(gè)目錄下的資源,感興趣的朋友一起看看吧2023-11-11

