国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

常州做網(wǎng)站麥策電商戶(hù)外廣告

常州做網(wǎng)站麥策電商,戶(hù)外廣告,seo網(wǎng)站點(diǎn)擊量排名優(yōu)化,wordpress素材模板[React]利用Webcomponent封裝React組件 為什么這么做 我個(gè)人認(rèn)為,最重要的點(diǎn)是可以很方便地跨框架掛載和卸載wc元素(至少我在項(xiàng)目里是這么玩的),此外,基于wc的css沙箱以及它的shadowRoot機(jī)制,可以提供一套…

[React]利用Webcomponent封裝React組件

為什么這么做

我個(gè)人認(rèn)為,最重要的點(diǎn)是可以很方便地跨框架掛載和卸載wc元素(至少我在項(xiàng)目里是這么玩的),此外,基于wc的css沙箱以及它的shadowRoot機(jī)制,可以提供一套隔離機(jī)制,保證每個(gè)渲染組件的邊界分明。

利用AI總結(jié)羅列了一下都有啥優(yōu)點(diǎn)…

  1. 封裝性:Web Components提供了一種封裝UI組件的方法,使得組件可以在不同的框架或無(wú)框架環(huán)境中重用。
  2. 可重用性:封裝為Web Components的React組件可以在任何支持Web Components的環(huán)境中使用,不限于React應(yīng)用。
  3. 封裝的樣式和行為:Web Components允許你封裝組件的HTML結(jié)構(gòu)、樣式和行為,確保樣式和行為不會(huì)泄露到父組件或全局作用域。
  4. 獨(dú)立性:Web Components封裝的組件具有獨(dú)立性,它們擁有自己的DOM樹(shù)和作用域,不會(huì)影響外部環(huán)境。
  5. 易于集成:Web Components提供了一種標(biāo)準(zhǔn)化的集成方式,可以更容易地將React組件集成到其他Web應(yīng)用中。
  6. 更好的性能:Web Components的自定義元素可以在不影響主線(xiàn)程的情況下進(jìn)行升級(jí)和渲染,這有助于提高應(yīng)用性能。
  7. 標(biāo)準(zhǔn)化:Web Components基于W3C標(biāo)準(zhǔn),這意味著它們?cè)诓煌臑g覽器和環(huán)境中具有更好的一致性和兼容性。
  8. 易于維護(hù):由于Web Components封裝的組件具有清晰的接口和封裝性,維護(hù)和更新組件變得更加容易。
  9. 樣式隔離:Web Components的Shadow DOM技術(shù)可以確保組件的樣式不會(huì)受到外部樣式的影響,同時(shí)也防止組件內(nèi)部樣式泄露到外部。
  10. 生命周期管理:Web Components允許你定義組件的生命周期鉤子,如connectedCallbackdisconnectedCallback等,這與React組件的生命周期方法類(lèi)似。
  11. 跨框架使用:封裝為Web Components的React組件可以被其他前端框架或庫(kù)使用,例如Vue、Angular或原生JavaScript。
  12. 自定義元素:Web Components允許開(kāi)發(fā)者定義自定義HTML元素,這些元素可以像標(biāo)準(zhǔn)HTML元素一樣使用。
  13. 易于測(cè)試:Web Components的封裝性使得測(cè)試組件變得更加簡(jiǎn)單,因?yàn)槟憧梢元?dú)立于其他組件來(lái)測(cè)試它們。
  14. 更好的封裝和抽象:Web Components提供了一種封裝和抽象UI組件的方式,使得組件的實(shí)現(xiàn)細(xì)節(jié)對(duì)使用者是透明的。

Webcomponent入門(mén)

先來(lái)簡(jiǎn)單地過(guò)一下webcomponent的基礎(chǔ)

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

示例

下面是一個(gè)最簡(jiǎn)單的示例,自定義了一種名為”simple-component“的元素,并且它沒(méi)有shadowRoot(意味著它并沒(méi)有與外界隔離樣式)。

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

下面是一個(gè)內(nèi)容更豐富一些的示例,有基礎(chǔ)的大概過(guò)一眼也知道大概了。

//  1.自定義標(biāo)簽都是用class 的形式去繼承
class myDiv extends HTMLElement {// 監(jiān)聽(tīng)static get observedAttributes() {return ['option']}constructor() {super()// 這樣我們才能夠去追加元素this.attachShadow({ mode: 'open' })}// 重要:生命周期方法 開(kāi)始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 => {// 重要:冒泡的順序,通過(guò)這個(gè)可以判斷有沒(méi)有在鼠標(biāo)內(nèi)部進(jìn)行點(diǎn)擊if (e.composedPath().includes(this)) {console.log('點(diǎn)擊了里面')}})this.shadowRoot.querySelector('.content').addEventListener('click', e => {console.log('e: ', e)// window.dispatchEvent})}// 重要:生命周期方法 重新渲染 .甚至還是第一次進(jìn)行渲染,比connect還快// 會(huì)重新渲染 connectCallbackattributeChangedCallback(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 的樣式絕對(duì)隔離// 重要: :host選擇器可以選中根也就是my-div的樣式。外面的選擇器樣式要高于這個(gè)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)}
}// 名字必須小寫(xiě) 駝峰必須要轉(zhuǎn)成橫線(xiàn)
customElements.define('my-div', myDiv)

shadowRoot

一個(gè)Web組件可以有且僅有一個(gè)shadowRoot。shadowRoot是與該組件關(guān)聯(lián)的影子DOM的根節(jié)點(diǎn)。當(dāng)使用attachShadow方法創(chuàng)建影子DOM時(shí),它會(huì)返回一個(gè)shadowRoot對(duì)象,這個(gè)對(duì)象是唯一的,并且與創(chuàng)建它的元素關(guān)聯(lián)。

例如:

class MyComponent extends HTMLElement {constructor() {super();this.shadow = this.attachShadow({ mode: "open" });this.shadow.innerHTML = `<p>I am in the shadow DOM!</p>`;}
}customElements.define('my-component', MyComponent);

在這個(gè)例子中:

  • MyComponent類(lèi)擴(kuò)展了HTMLElement,定義了一個(gè)Web組件。
  • 在構(gòu)造函數(shù)中,通過(guò)調(diào)用this.attachShadow({ mode: "open" })創(chuàng)建了一個(gè)shadowRoot,并將其存儲(chǔ)在this.shadow變量中。
  • 這個(gè)shadowRoot是唯一的,并且與MyComponent實(shí)例關(guān)聯(lián)。

關(guān)鍵點(diǎn):

  • 唯一性:每個(gè)Web組件實(shí)例只能有一個(gè)shadowRoot。
  • 關(guān)聯(lián)性shadowRoot與創(chuàng)建它的Web組件實(shí)例是緊密關(guān)聯(lián)的,不能被其他組件實(shí)例訪(fǎng)問(wèn)。

因此,盡管可以在shadowRoot內(nèi)創(chuàng)建多個(gè)子元素和結(jié)構(gòu),但每個(gè)Web組件實(shí)例只能有一個(gè)shadowRoot。這有助于保持組件的封裝性和獨(dú)立性。

生命周期

  1. connectedCallback
    • 當(dāng)自定義元素被插入到文檔DOM樹(shù)中時(shí)調(diào)用此方法。這類(lèi)似于React中的componentDidMount。
  2. disconnectedCallback
    • 當(dāng)自定義元素從DOM樹(shù)中移除時(shí)調(diào)用此方法。類(lèi)似于React中的componentWillUnmount。
  3. attributeChangedCallback
    • 當(dāng)自定義元素的屬性被更改時(shí)調(diào)用此方法。它接收三個(gè)參數(shù):屬性名稱(chēng)、舊值和新值。這可以用于響應(yīng)屬性的變化,類(lèi)似于React中的componentDidUpdate,但是它是針對(duì)屬性而不是狀態(tài)。
  4. adoptedCallback
    • 當(dāng)自定義元素被移動(dòng)到新的文檔時(shí)調(diào)用此方法。這在Web Components中是特有的,因?yàn)樽远x元素可以跨文檔使用。

主要屬性

  1. 觀(guān)察者模式(Observed attributes)
    • 通過(guò)在自定義元素類(lèi)中定義一個(gè)靜態(tài)的observedAttributes屬性數(shù)組,可以指定哪些屬性的更改應(yīng)該觸發(fā)attributeChangedCallback。
  2. connecteddisconnected 屬性
    • 這些屬性可以用于檢查自定義元素是否已經(jīng)連接到文檔的DOM樹(shù)中。
  3. shadowRoot 屬性
    • 每個(gè)自定義元素都有一個(gè)shadowRoot屬性,它是一個(gè)Shadow DOM樹(shù)的根??梢栽谶@個(gè)屬性上使用生命周期回調(diào)來(lái)管理Shadow DOM的創(chuàng)建和更新。
  4. constructor
    • 雖然不是Web Components的生命周期回調(diào),但是自定義元素的構(gòu)造函數(shù)是定義元素屬性和方法的地方,并且在元素實(shí)例化時(shí)調(diào)用。

Lit框架入門(mén)

一般知道了上面的基礎(chǔ),就可以寫(xiě)wc組件了,但實(shí)際開(kāi)發(fā)中,肯定還是需要借助一些已有的開(kāi)發(fā)框架來(lái)輔助開(kāi)發(fā),而Lit就是目前最成熟且使用量最高的。

原理介紹

Web組件的更新并不是每次都進(jìn)行全量更新。Web組件的更新機(jī)制非常靈活,能夠根據(jù)組件的狀態(tài)和屬性的變化來(lái)決定是否需要更新。以下是一些關(guān)鍵點(diǎn):

  1. 屬性變化觸發(fā)更新

    • Web組件的更新通常是由屬性的變化觸發(fā)的。當(dāng)組件的屬性發(fā)生變化時(shí),瀏覽器會(huì)調(diào)用attributeChangedCallback方法來(lái)處理這些變化。
  2. 狀態(tài)變化觸發(fā)更新

    • 組件的內(nèi)部狀態(tài)變化也可能導(dǎo)致更新。例如,在LitElement中,當(dāng)使用@property裝飾器定義的屬性發(fā)生變化時(shí),會(huì)觸發(fā)更新。
  3. 生命周期方法

    • 組件的生命周期方法,如connectedCallback, disconnectedCallback, adoptedCallback, firstUpdated, updated等,都可以在特定時(shí)機(jī)觸發(fā)更新。
  4. 選擇性更新

    • 更新機(jī)制可以是選擇性的。例如,在LitElement中,可以通過(guò)使用requestUpdate方法來(lái)請(qǐng)求更新,而不必每次都進(jìn)行全量更新。
  5. 虛擬DOM

    • 一些Web組件框架(如LitElement)使用虛擬DOM技術(shù)來(lái)優(yōu)化更新過(guò)程。虛擬DOM可以比較組件的新舊狀態(tài),并只更新那些實(shí)際發(fā)生變化的部分。
  6. 優(yōu)化性能

    • 為了避免不必要的全量更新,Web組件通常會(huì)使用一些優(yōu)化技術(shù),例如節(jié)流(throttle)和防抖(debounce)來(lái)減少更新次數(shù)。
  7. 自定義渲染邏輯

    • 開(kāi)發(fā)者可以通過(guò)自定義渲染邏輯來(lái)控制組件的更新過(guò)程。例如,可以在render方法中手動(dòng)決定哪些部分需要重新渲染。
  8. 條件渲染

    • 組件可以通過(guò)條件渲染來(lái)決定是否需要更新某些部分。例如,只有當(dāng)特定條件滿(mǎn)足時(shí)才重新渲染某些元素。

示例

以下是一個(gè)使用LitElement的示例,展示了如何控制組件的更新:

import { LitElement, html, css, property } from 'lit';class MyComponent extends LitElement {@property({ type: String })message = '';render() {return html`<div><p>${this.message}</p></div>`;}updated(changedProperties) {super.updated(changedProperties);if (changedProperties.has('message')) {console.log('Message updated:', this.message);}}
}customElements.define('my-component', MyComponent);

在這個(gè)示例中:

  • message屬性使用@property裝飾器定義,當(dāng)其值發(fā)生變化時(shí),會(huì)觸發(fā)組件的更新。
  • render方法定義了組件的渲染邏輯,只有當(dāng)message屬性發(fā)生變化時(shí),相關(guān)的部分才會(huì)重新渲染。
  • updated方法在組件更新后被調(diào)用,可以在這里處理更新后的邏輯。

通過(guò)這種方式,Web組件可以有效地控制更新過(guò)程,避免不必要的全量更新,從而提高性能。

增加的生命周期和內(nèi)置屬性

Lit 相對(duì)于傳統(tǒng) Web 組件規(guī)范增加的一些生命周期鉤子和特性:

  1. render 方法

    • 這是 Lit 的核心特性之一。render 方法是一個(gè)返回組件模板的函數(shù),Lit 會(huì)根據(jù)這個(gè)方法的內(nèi)容來(lái)渲染組件的 UI。
  2. update 方法

    • 這個(gè)方法在組件的屬性或狀態(tài)發(fā)生變化時(shí)被調(diào)用。Lit 會(huì)調(diào)用這個(gè)方法來(lái)決定是否需要重新渲染組件。
  3. shouldUpdate 方法

    • 這個(gè)方法允許開(kāi)發(fā)者自定義更新邏輯,決定是否需要進(jìn)行更新。如果返回 false,則跳過(guò)更新。
  4. willUpdate 方法

    • 在組件更新之前被調(diào)用,可以用于執(zhí)行更新前的準(zhǔn)備工作。
  5. updated 方法

    • 在組件更新之后被調(diào)用,可以用于執(zhí)行更新后的邏輯處理。
  6. firstUpdated 方法

    • 在組件首次更新后被調(diào)用。這與 Web 組件的 connectedCallback 有些相似,但專(zhuān)門(mén)用于處理首次渲染后的邏輯。
  7. connectedCallback

    • 這是 Web 組件規(guī)范中的方法,Lit 也支持。當(dāng)組件被插入到文檔中時(shí)調(diào)用。
  8. disconnectedCallback

    • 這是 Web 組件規(guī)范中的方法,Lit 也支持。當(dāng)組件從文檔中移除時(shí)調(diào)用。
  9. attributeChangedCallback

    • 這是 Web 組件規(guī)范中的方法,Lit 也支持。當(dāng)組件的屬性發(fā)生變化時(shí)調(diào)用。
  10. adoptedCallback

    • 這是 Web 組件規(guī)范中的方法,Lit 也支持。當(dāng)組件被移動(dòng)到新文檔時(shí)調(diào)用。
  11. requestUpdate 方法

    • 這個(gè)方法可以被開(kāi)發(fā)者調(diào)用,以請(qǐng)求更新組件的屬性。Lit 會(huì)安排在下一個(gè)微任務(wù)中處理這些更新。
  12. updateComplete Promise

    • 一個(gè) Promise,當(dāng)組件的更新完成后會(huì)解析。這可以用于在更新完成后執(zhí)行異步操作。
  13. 樣式管理

    • Lit 提供了 CSSResultunsafeCSS 等 API,用于更安全和方便地管理組件的樣式。
  14. 屬性裝飾器

    • 使用 @property 裝飾器定義的屬性會(huì)觸發(fā)更新,并且可以指定屬性的類(lèi)型和是否同步到 DOM 屬性。
  15. 狀態(tài)管理

    • Lit 通過(guò) state 方法和 reactive 裝飾器,提供了一種聲明式的方式來(lái)管理組件的狀態(tài)。

核心:結(jié)合Lit框架實(shí)現(xiàn)React組件封裝

那么基于以上,我們可以很容易地就實(shí)現(xiàn)利用Lit框架創(chuàng)造出一個(gè)webcomponent容器,然后用來(lái)包裹React組件。

Base基礎(chǔ)類(lèi)

import { LitElement, ReactiveElement, adoptStyles, unsafeCSS, PropertyValues } from 'lit'
import { property } from 'lit/decorators.js'type ThrottleFn = (...args: any[]) => void
type DelayFn = (fn: ThrottleFn) => voidconst throttleWith = <T extends ThrottleFn>(fn: T,delayFn: DelayFn,leading = false
): T => {let lastArgs: Parameters<T>, lastThis: unknown, isWaiting = falseconst throttledFn = (...args: Parameters<T>) => {lastArgs = args// eslint-disable-next-linelastThis = thisif (!isWaiting) {if (leading) {fn.apply(lastThis, lastArgs)}isWaiting = truedelayFn(() => {fn.apply(lastThis, lastArgs)isWaiting = false})}}return throttledFn as T
}export default class Base extends LitElement {private _wcStyle?: string@property({ attribute: 'wc-style' })get wcStyle() {return this._wcStyle}set wcStyle(val: string | undefined) {this._wcStyle = valthis.adoptStyles()}/*** 使事件不能跨越ShadowDOM邊界傳播*/@property({ type: Boolean, attribute: 'prevent-compose' })protected preventCompose = false/*** 使事件不冒泡*/@property({ type: Boolean, attribute: 'prevent-bubbles' })protected preventBubbles = false// 應(yīng)用樣式protected adoptStyles = throttleWith(() => {const apply = () => {if (this.renderRoot instanceof ShadowRoot) {const styles = (this.constructor as typeof ReactiveElement).elementStyles.slice() // 獲取原有樣式this.wcStyle && styles.push(unsafeCSS(this.wcStyle))adoptStyles(this.renderRoot, styles) // 重新應(yīng)用樣式}}this.renderRoot ? apply() : this.updateComplete.then(apply)},(fn: any) => Promise.resolve().then(fn))// 派發(fā)事件emit(eventName: string, detail?: any, options?: CustomEventInit) {let event = new CustomEvent(eventName, {detail,composed: !this.preventCompose,bubbles: !this.preventBubbles,cancelable: false,...options,})this.dispatchEvent(event)return event}// 判斷 slot 是否傳入內(nèi)容hasSlot(name?: string) {if (name && name !== 'default') {return !![...this.childNodes].find(node => node.nodeType === node.ELEMENT_NODE && (node as Element).getAttribute('slot') === name)}return [...this.childNodes].some(node => {if (node.nodeType === node.TEXT_NODE && !!node.textContent?.trim()) {return true}if (node.nodeType === node.ELEMENT_NODE) {const el = node as HTMLElementif (!el.hasAttribute('slot')) {return true}}return false})}// 各個(gè)生命周期// 掛載時(shí)connectedCallback() {super.connectedCallback()console.log('Custom element added to page.')// 第一次被插入文檔時(shí)執(zhí)行,跳過(guò)節(jié)點(diǎn)刪除后又重新插入的情形if (!this.hasUpdated) {this.setAttribute('wc-component', '')this.setAttribute('wc-pending', '')}}// 卸載時(shí)disconnectedCallback() {super.disconnectedCallback()console.log('Custom element removed from page.')}// 移動(dòng)到另一個(gè)文檔的時(shí)候adoptedCallback() {console.log('Custom element moved.')}// 元素的屬性被添加、刪除或修改時(shí)調(diào)用attributeChangedCallback(name: string, oldValue: any, newValue: any) {super.attributeChangedCallback(name, oldValue, newValue)console.log(`Attribute ${name} has changed.`)}// 或使用靜態(tài)屬性代替get方法static get observedAttributes() {// 指定要監(jiān)聽(tīng)的元素的屬性數(shù)組// 對(duì)應(yīng)的attr改變后,會(huì)觸發(fā)attributeChangedCallback// return ['name', 'date']return []}// 是否應(yīng)該更新protected shouldUpdate(_changedProperties: PropertyValues): boolean {return true}// 即將更新protected willUpdate(_changedProperties: PropertyValues): void {super.willUpdate(_changedProperties)console.log('willUpdate')}// 首次更新元素時(shí)調(diào)用。實(shí)現(xiàn)在更新后對(duì)元素執(zhí)行一次性工作protected firstUpdated(changedProperties: PropertyValues) {super.firstUpdated(changedProperties)console.log('this.hasUpdated: ', this.hasUpdated)// this.requestUpdate()// 兩幀數(shù)后執(zhí)行requestAnimationFrame(() => {requestAnimationFrame(() => {this.removeAttribute('wc-pending')})})}protected updated(_changedProperties: PropertyValues): void {super.updated(_changedProperties)this.updateComplete.then((res) => {console.log('updateComplete', res)})}
}

withProperties封裝

import type { LitElement, PropertyValues } from 'lit'type Constructor<T> = new (...args: any[]) => Texport default <T extends Constructor<LitElement>>(superClass: T) => {class WithPropertiesElement extends superClass {props: Record<string, any> = {}willUpdate(changedProperties: PropertyValues) {const obj = [...changedProperties.entries()].reduce<any>((obj, [key]) => ((obj[key] = (this as any)[key]), obj),{})this.props = { ...this.props, ...obj }super.willUpdate(changedProperties)}}return WithPropertiesElement as Constructor<{props: Record<string, any>}> & T
}

這段代碼定義了一個(gè)高階組件(Higher-Order Component,HOC),用于增強(qiáng) LitElement 組件的功能。具體來(lái)說(shuō),它的作用是:

  1. 創(chuàng)建一個(gè)帶有額外屬性管理功能的組件類(lèi)

    • 通過(guò)擴(kuò)展傳入的基類(lèi)(比如 LitElement 或其子類(lèi)),添加一個(gè) props 屬性來(lái)存儲(chǔ)組件的屬性值。
  2. 在組件更新前處理屬性變化

    • 重寫(xiě) willUpdate 生命周期方法,這個(gè)方法在組件的屬性發(fā)生變化并且組件即將更新之前被調(diào)用。
  3. 收集并存儲(chǔ)屬性變化

    • 使用 changedProperties 對(duì)象(一個(gè) Map 類(lèi)型的對(duì)象,包含屬性名和屬性變化的信息)來(lái)收集屬性的變化。
    • 將變化的屬性存儲(chǔ)到 this.props 對(duì)象中,這樣可以通過(guò) props 屬性訪(fǎng)問(wèn)組件的所有屬性值。
  4. 保持基類(lèi)的 willUpdate 方法的調(diào)用

    • 調(diào)用 super.willUpdate(changedProperties) 以確?;?lèi)的 willUpdate 方法也能正常執(zhí)行。

代碼詳解

  • 定義了一個(gè)默認(rèn)導(dǎo)出的函數(shù),它接受一個(gè)構(gòu)造函數(shù) superClass(應(yīng)該是 LitElement 或其子類(lèi)的構(gòu)造函數(shù))。
  • 創(chuàng)建一個(gè)新類(lèi) WithPropertiesElement,繼承自 superClass
  • WithPropertiesElement 類(lèi)中定義了一個(gè) props 屬性,用于存儲(chǔ)屬性值。
  • 重寫(xiě) willUpdate 方法,在組件更新前處理屬性變化,并將變化的屬性存儲(chǔ)到 this.props 中。
  • 返回 WithPropertiesElement 類(lèi),并通過(guò)類(lèi)型斷言確保它具有額外的 props 屬性。

使用示例

假設(shè)你有一個(gè)基礎(chǔ)的 LitElement 組件:

import { LitElement, html } from 'lit';class MyElement extends LitElement {count = 0;render() {return html`<p>Count: ${this.count}</p>`;}
}customElements.define('my-element', MyElement);

你可以使用這個(gè)高階組件來(lái)增強(qiáng)它:

import { WithPropertiesElement } from './WithPropertiesElement';
import { LitElement, html } from 'lit';const EnhancedElement = WithPropertiesElement(MyElement);customElements.define('enhanced-element', EnhancedElement);const element = new EnhancedElement();
document.body.appendChild(element);console.log(element.props); // { count: 0 }

在這個(gè)示例中,EnhancedElement 繼承自 MyElement 并添加了屬性管理功能。可以通過(guò) element.props 訪(fǎng)問(wèn)組件的所有屬性值。

這種模式在需要在組件中統(tǒng)一管理屬性或在組件更新前進(jìn)行額外處理時(shí)非常有用。

存放React組件的webcomponent基類(lèi)

重頭戲來(lái)了

import { ChildPart, html, PropertyValues } from 'lit'
import { query } from 'lit/decorators.js'
import { Fragment, createElement as h } from 'react'
import ReactDOM from 'react-dom'
import withProperties from '../mixin/withProperties'
import LightBase from './Base'type H = typeof hconst Root: React.FC<any> = props => {return h(Fragment, {...props,})
}const omit = (obj: Record<string, any>, filter: string[] = []) =>Object.fromEntries(Object.entries(obj).filter(([key]) => !filter.includes(key)))// React組件基類(lèi)
export default class extends withProperties(LightBase) {// 子類(lèi)要重寫(xiě)這個(gè)方法來(lái)渲染自己的組件protected renderReact(h: H): React.ReactNode {return null}protected customContainer(): Element | undefined {return this.$reactRoot}protected getReactProps(props: Record<string, any>) {return omit(props, ['preventCompose', 'preventBubbles', 'localeMessages'])}protected extraStyle = ''@query('.react-root')$reactRoot?: HTMLElementupdated(changed: PropertyValues) {super.updated(changed)this.doRender()}connectedCallback() {super.connectedCallback()// 節(jié)點(diǎn)刪除后重新插入的情形if (this.hasUpdated) {this.doRender()}}disconnectedCallback() {super.disconnectedCallback()this.doUnmount()}private container?: Elementprivate doRender() {const container = this.customContainer()if (!container) {this.doUnmount() // 卸載前一次渲染的內(nèi)容} else {this.container = containerReactDOM.render(h(Root, {}, this.renderReact(h)), container, () => {// hack for error: https://github.com/lit/lit/blob/f8ee010bc515e4bb319e98408d38ef3d971cc08b/packages/lit-html/src/lit-html.ts#L1122// 在React中使用此組件且非首次更新時(shí)會(huì)報(bào)錯(cuò),因?yàn)閘it默認(rèn)會(huì)在組件下創(chuàng)建一個(gè)注釋節(jié)點(diǎn),更新時(shí)會(huì)對(duì)這個(gè)節(jié)點(diǎn)進(jìn)行操作,而React渲染時(shí)把這個(gè)注釋節(jié)點(diǎn)干掉了,這里要把他加回去const childPart = (this as any).__childPart as ChildPart | undefinedchildPart?.startNode && this.appendChild(childPart.startNode)})}}private doUnmount() {if (this.container) {ReactDOM.unmountComponentAtNode(this.container)}}render() {return html` <div class="react-root"></div> `}
}

使用Demo

import { unsafeCSS } from 'lit'
import { customElement, property } from 'lit/decorators.js'
import ReactBase from './ReactBase'// 自己的React組件
import Component from './Component'import style from './index.less?inline'@customElement('my-diy-react-wc')
export default class DataReport extends ReactBase {static get styles() {return unsafeCSS([style])}/*** 自定義屬性*/@property()language: string = 'zh-CN'// ReactBase中用來(lái)渲染React,不要?jiǎng)h除renderReact() {return <Component language={this.language} />}
}

參考文章

https://juejin.cn/post/7296850940404580364?searchId=2024071620331848BC966F0D2051B9C533#heading-9

lit官網(wǎng):https://lit.dev/docs/components/styles/

webcomponent文檔:https://developer.mozilla.org/en-US/docs/Web/API/Web_components

http://m.aloenet.com.cn/news/992.html

相關(guān)文章:

  • 網(wǎng)站信息內(nèi)容建設(shè) 宣傳部門(mén)游戲優(yōu)化大師
  • 網(wǎng)站開(kāi)發(fā)高級(jí)工程師專(zhuān)業(yè)怎樣申請(qǐng)網(wǎng)站注冊(cè)
  • 新聞網(wǎng)站建設(shè)評(píng)比規(guī)則2023重大新聞事件10條
  • 網(wǎng)站圖片上的水印怎么做網(wǎng)絡(luò)營(yíng)銷(xiāo)專(zhuān)業(yè)就業(yè)公司
  • seo擦邊球網(wǎng)站什么廣告推廣最有效果
  • dw用層還是表格做網(wǎng)站快色盲圖
  • 做外貿(mào)哪些網(wǎng)站可以發(fā)免費(fèi)信息大批量刷關(guān)鍵詞排名軟件
  • 海外永久網(wǎng)站百度一下你就知道移動(dòng)官網(wǎng)
  • 論壇網(wǎng)站開(kāi)發(fā)外包公司什么意思
  • 網(wǎng)頁(yè)游戲人生重開(kāi)模擬器如何把一個(gè)關(guān)鍵詞優(yōu)化到首頁(yè)
  • 網(wǎng)站滾動(dòng)效果怎么做站長(zhǎng)工具怎么關(guān)掉
  • 淘寶開(kāi)放平臺(tái)怎么做淘寶客網(wǎng)站網(wǎng)絡(luò)推廣費(fèi)用一般多少
  • 杭州網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)安卓?jī)?yōu)化大師app下載安裝
  • 運(yùn)城網(wǎng)站建設(shè)公司有多少錢(qián)愛(ài)站網(wǎng)seo
  • 邢臺(tái)網(wǎng)站建設(shè)要多少錢(qián)揚(yáng)州百度關(guān)鍵詞優(yōu)化
  • 境外網(wǎng)站開(kāi)發(fā)北京it培訓(xùn)機(jī)構(gòu)哪家好
  • 做視頻能賺錢(qián)的網(wǎng)站seoheuni
  • 如何注冊(cè)公司微信公眾號(hào)網(wǎng)站seo系統(tǒng)
  • 國(guó)外做建材的網(wǎng)站有哪些手機(jī)端競(jìng)價(jià)惡意點(diǎn)擊能防止嗎
  • 深圳做h5網(wǎng)站設(shè)計(jì)百度關(guān)鍵詞排名批量查詢(xún)工具
  • 做網(wǎng)站模塊百度一下首頁(yè)極簡(jiǎn)版
  • 福州網(wǎng)站建設(shè)公司哪家好推廣優(yōu)化師
  • dz網(wǎng)站收款即時(shí)到賬怎么做的保定網(wǎng)站建設(shè)報(bào)價(jià)
  • 貝爾利網(wǎng)站網(wǎng)絡(luò)推廣內(nèi)容
  • 描述建設(shè)一個(gè)網(wǎng)站的具體步驟制作網(wǎng)站
  • 開(kāi)發(fā)一個(gè)網(wǎng)站多少錢(qián)?上海seo關(guān)鍵詞優(yōu)化
  • 做網(wǎng)站找云無(wú)限seo查詢(xún)?cè)诰€(xiàn)
  • 基于A(yíng)SP與Access數(shù)據(jù)庫(kù)的網(wǎng)站開(kāi)發(fā)東莞網(wǎng)絡(luò)推廣托管
  • 做高效能的父母網(wǎng)站金華seo扣費(fèi)
  • 幫人做網(wǎng)站要怎么賺錢(qián)嗎臨沂seo全網(wǎng)營(yíng)銷(xiāo)