React PlatoBlockchain 데이터 인텔리전스에서도 작동하는 상호 운용 가능한 웹 구성 요소 구축. 수직 검색. 일체 포함.

React와도 작동하는 상호 운용 가능한 웹 구성 요소 구축

몇 년 이상 웹 개발자로 일한 사람들은 아마도 하나 이상의 JavaScript 프레임워크를 사용하여 코드를 작성했을 것입니다. React, Svelte, Vue, Angular, Solid 등 모든 선택이 가능하므로 불가피합니다. 여러 프레임워크에서 작업할 때 처리해야 하는 더 실망스러운 것 중 하나는 버튼, 탭, 드롭다운 등의 모든 저수준 UI 구성 요소를 다시 만드는 것입니다. 특히 실망스러운 것은 일반적으로 하나의 프레임워크에서 정의해야 한다는 것입니다. , React라고 말하지만 Svelte에서 무언가를 빌드하려면 다시 작성해야 합니다. 또는 뷰. 또는 솔리드. 등등.

이러한 저수준 UI 구성 요소를 프레임워크에 구애받지 않는 방식으로 한 번 정의한 다음 프레임워크 간에 재사용할 수 있다면 더 좋지 않을까요? 물론 그럴 것이다! 그리고 우리는 할 수 있습니다. 웹 구성 요소가 방법입니다. 이 게시물은 방법을 보여줍니다.

현재 웹 구성 요소에 대한 SSR 스토리는 약간 부족합니다. 선언적 그림자 DOM(DSD)은 웹 구성 요소가 서버 측에서 렌더링되는 방식이지만 이 글을 쓰는 시점에서 Next, Remix 또는 SvelteKit과 같은 즐겨 사용하는 애플리케이션 프레임워크와 통합되지 않았습니다. 그것이 당신을위한 요구 사항이라면 DSD의 최신 상태를 확인하십시오. 그러나 그렇지 않으면 SSR이 사용 중인 것이 아닌 경우 계속 읽으십시오.

첫째, 약간의 맥락

웹 구성 요소는 본질적으로 다음과 같이 스스로 정의하는 HTML 요소입니다. <yummy-pizza> 또는 무엇이든, 처음부터. CSS-Tricks(포함 Caleb Williams의 광범위한 시리즈하나는 존 레아) 프로세스를 간략하게 살펴보겠습니다. 기본적으로 JavaScript 클래스를 정의하고 다음에서 상속합니다. HTMLElement, 그런 다음 웹 구성 요소에 있는 속성, 속성 및 스타일과 궁극적으로 사용자에게 렌더링할 마크업을 정의합니다.

특정 구성 요소에 바인딩되지 않은 사용자 정의 HTML 요소를 정의할 수 있다는 것은 흥미로운 일입니다. 그러나 이 자유도 한계입니다. JavaScript 프레임워크와 독립적으로 존재한다는 것은 해당 JavaScript 프레임워크와 실제로 상호 작용할 수 없다는 것을 의미합니다. 일부 데이터를 가져온 다음 일부를 렌더링하는 React 구성 요소를 생각해보십시오. other 데이터를 따라 전달하는 React 구성 요소. 웹 구성 요소는 React 구성 요소를 렌더링하는 방법을 모르기 때문에 이것은 실제로 웹 구성 요소로 작동하지 않습니다.

웹 구성 요소는 특히 다음과 같이 탁월합니다. 잎 성분. 잎 성분 구성 요소 트리에서 렌더링되는 마지막 것입니다. 이것들은 일부 props를 수신하고 일부를 렌더링하는 구성 요소입니다. UI. 이것들은 지원 구성 요소 트리의 중간에 있는 구성 요소, 데이터 전달, 컨텍스트 설정 등 — 순수한 부분 UI 어떤 JavaScript 프레임워크가 앱의 나머지 부분을 지원하는지에 상관없이 동일하게 보일 것입니다.

우리가 만들고 있는 웹 컴포넌트

버튼처럼 지루한(그리고 흔한) 것을 만드는 것보다 조금 다른 것을 만들어 봅시다. 내 게시물을 로딩 콘텐츠 리플로우를 방지하고 이미지가 로드되는 동안 사용자에게 적절한 UI를 제공하기 위해 흐릿한 이미지 미리보기를 사용하는 방법을 살펴보았습니다. 우리는 이미지의 흐릿하고 저하된 버전을 인코딩하고 실제 이미지가 로드되는 동안 UI에서 이를 보여주는 base64를 살펴보았습니다. 우리는 또한 블러해시.

그 게시물은 이러한 미리보기를 생성하고 React 프로젝트에서 사용하는 방법을 보여주었습니다. 이 게시물은 웹 구성 요소에서 이러한 미리 보기를 사용하여 다음에서 사용할 수 있도록 하는 방법을 보여줍니다. 어떤 자바스크립트 프레임워크.

그러나 달리기 전에 먼저 걸어야 하므로 웹 구성 요소가 어떻게 작동하는지 정확히 보기 위해 먼저 사소하고 어리석은 일을 살펴보겠습니다.

이 게시물의 모든 내용은 도구 없이 바닐라 웹 구성 요소를 빌드합니다. 즉, 코드에 약간의 상용구가 있지만 비교적 따라하기 쉬워야 합니다. 다음과 같은 도구 점등 or 원판 웹 구성 요소를 구축하기 위해 설계되었으며 이 상용구의 많은 부분을 제거하는 데 사용할 수 있습니다. 나는 당신이 그들을 확인하도록 촉구합니다! 그러나 이 게시물에서는 다른 종속성을 소개하고 가르칠 필요가 없는 대신 상용구를 조금 더 선호합니다.

간단한 카운터 구성 요소

JavaScript 구성 요소의 고전적인 "Hello World"인 카운터를 빌드해 보겠습니다. 우리는 값과 그 값을 증가시키는 버튼을 렌더링할 것입니다. 간단하고 지루하지만 가능한 가장 간단한 웹 구성 요소를 살펴보겠습니다.

웹 컴포넌트를 빌드하기 위한 첫 번째 단계는 다음에서 상속되는 JavaScript 클래스를 만드는 것입니다. HTMLElement:

class Counter extends HTMLElement {}

마지막 단계는 웹 구성 요소를 등록하는 것이지만 아직 등록하지 않은 경우에만 가능합니다.

if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter);
}

물론 다음과 같이 렌더링합니다.

<counter-wc></counter-wc>

그리고 그 사이에 있는 모든 것은 웹 구성 요소가 원하는 대로 하도록 만드는 것입니다. 한 가지 일반적인 수명 주기 방법은 다음과 같습니다. connectedCallback, 웹 구성 요소가 DOM에 추가될 때 실행됩니다. 그 방법을 사용하여 원하는 콘텐츠를 렌더링할 수 있습니다. 이것은 다음에서 상속하는 JS 클래스임을 기억하십시오. HTMLElement, 이는 우리의 this value 는 이미 알고 있고 좋아하는 모든 일반 DOM 조작 방법이 포함된 웹 구성 요소 요소 자체입니다.

가장 간단하게 다음과 같이 할 수 있습니다.

class Counter extends HTMLElement { connectedCallback() { this.innerHTML = "<div style='color: green'>Hey</div>"; }
} if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter);
}

... 잘 작동합니다.

녹색으로 "이봐"라는 단어.
React와도 작동하는 상호 운용 가능한 웹 구성 요소 구축

실제 콘텐츠 추가

유용한 대화형 콘텐츠를 추가해 보겠습니다. 우리는 필요합니다 <span> 현재 숫자 값을 유지하고 <button> 카운터를 증가시킵니다. 지금은 생성자에서 이 콘텐츠를 만들고 웹 구성 요소가 실제로 DOM에 있을 때 추가합니다.

constructor() { super(); const container = document.createElement('div'); this.valSpan = document.createElement('span'); const increment = document.createElement('button'); increment.innerText = 'Increment'; increment.addEventListener('click', () => { this.#value = this.#currentValue + 1; }); container.appendChild(this.valSpan); container.appendChild(document.createElement('br')); container.appendChild(increment); this.container = container;
} connectedCallback() { this.appendChild(this.container); this.update();
}

수동 DOM 생성에 정말 지쳤다면 다음을 설정할 수 있음을 기억하십시오. innerHTML또는 웹 구성 요소 클래스의 정적 속성으로 템플릿 요소를 한 번 생성하고 복제하고 새 웹 구성 요소 인스턴스에 대한 내용을 삽입할 수도 있습니다. 내가 생각하지 못한 다른 옵션이 있을 수 있습니다. 또는 항상 다음과 같은 웹 구성 요소 프레임워크를 사용할 수 있습니다. 점등 or 원판. 그러나 이 게시물에서는 계속 간단하게 유지하겠습니다.

계속해서 설정 가능한 JavaScript 클래스 속성이 필요합니다. value

#currentValue = 0; set #value(val) { this.#currentValue = val; this.update();
}

값을 보유하는 두 번째 속성과 함께 setter가 있는 표준 클래스 속성일 뿐입니다. 한 가지 재미있는 반전은 이러한 값에 대해 개인 JavaScript 클래스 속성 구문을 사용하고 있다는 것입니다. 즉, 웹 구성 요소 외부의 누구도 이 값을 만질 수 없습니다. 이것은 표준 자바스크립트입니다 모든 최신 브라우저에서 지원됩니다., 그래서 그것을 사용하는 것을 두려워하지 마십시오.

아니면 편하게 전화주세요 _value 너가 선호한다면. 그리고 마지막으로 우리의 update 방법:

update() { this.valSpan.innerText = this.#currentValue;
}

효과가있다!

카운터 웹 구성 요소.
React와도 작동하는 상호 운용 가능한 웹 구성 요소 구축

분명히 이것은 대규모로 유지하려는 코드가 아닙니다. 여기 전체가 있습니다 작업 예 자세히 보고 싶다면. 내가 말했듯이, Lit 및 Stencil과 같은 도구는 이것을 더 간단하게 만들기 위해 설계되었습니다.

기능 추가

이 게시물은 웹 구성 요소에 대한 심층 분석이 아닙니다. 모든 API와 수명 주기를 다루지는 않을 것입니다. 우리는 덮지 않을 것입니다 그림자 루트 또는 슬롯. 그 주제에 대한 끝없는 콘텐츠가 있습니다. 여기에서 제 목표는 실제로 몇 가지 유용한 지침과 함께 약간의 관심을 불러일으킬 수 있는 적절한 소개를 제공하는 것입니다. 사용 이미 알고 있고 좋아하는 인기 있는 JavaScript 프레임워크가 포함된 웹 구성 요소.

이를 위해 카운터 웹 구성 요소를 약간 개선해 보겠습니다. 받아주도록 하자 color 속성, 표시되는 값의 색상을 제어합니다. 그리고 받아들이도록 합시다. increment 속성이므로 이 웹 구성 요소의 소비자는 한 번에 2, 3, 4씩 증가할 수 있습니다. 그리고 이러한 상태 변경을 유도하기 위해 Svelte 샌드박스에서 새 카운터를 사용하겠습니다. 잠시 후 React에 대해 알아보겠습니다.

이전과 동일한 웹 구성 요소로 시작하여 색상 속성을 추가합니다. 속성을 수락하고 응답하도록 웹 구성 요소를 구성하기 위해 정적 observedAttributes 웹 구성 요소가 수신 대기하는 속성을 반환하는 속성입니다.

static observedAttributes = ["color"];

그 자리에서 우리는 추가할 수 있습니다 attributeChangedCallback 목록에 나열된 속성 중 하나라도 실행될 때마다 실행되는 수명 주기 메서드 observedAttributes 설정되거나 업데이트됩니다.

attributeChangedCallback(name, oldValue, newValue) { if (name === "color") { this.update(); }
}

이제 우리는 우리의 update 실제로 사용하는 방법:

update() { this.valSpan.innerText = this._currentValue; this.valSpan.style.color = this.getAttribute("color") || "black";
}

마지막으로 우리의 increment 재산:

increment = 1;

단순하고 겸손합니다.

Svelte에서 카운터 구성 요소 사용

방금 만든 것을 사용합시다. Svelte 앱 구성 요소로 이동하여 다음과 같이 추가합니다.

<script> let color = "red";
</script> <style> main { text-align: center; }
</style> <main> <select bind:value={color}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc color={color}></counter-wc>
</main>

그리고 그것은 작동합니다! 카운터가 렌더링되고 증가하며 드롭다운이 색상을 업데이트합니다. 보시다시피 Svelte 템플릿에서 color 속성을 렌더링하고 값이 변경되면 Svelte가 호출의 레그워크를 처리합니다. setAttribute 기본 웹 구성 요소 인스턴스에서. 여기에는 특별한 것이 없습니다. 이것은 이미 속성에 대해 수행하는 것과 동일합니다. 어떤 HTML 요소.

상황이 약간 흥미로워집니다. increment 소품. 이것은 지원 웹 구성 요소의 속성 웹 구성 요소의 클래스에 있는 소품입니다. 즉, 웹 구성 요소의 인스턴스에서 설정해야 합니다. 조금만 더 있으면 일이 훨씬 간단해질 테니 조금만 참아주세요.

먼저 Svelte 구성 요소에 몇 가지 변수를 추가합니다.

let increment = 1;
let wcInstance;

강력한 카운터 구성 요소를 사용하면 1 또는 2씩 증가할 수 있습니다.

<button on:click={() => increment = 1}>Increment 1</button>
<button on:click={() => increment = 2}>Increment 2</button>

그러나, 이론에 의하면, 웹 구성 요소의 실제 인스턴스를 가져와야 합니다. 이것은 우리가 추가할 때마다 항상 하는 것과 같습니다. ref 리액트와 함께. Svelte를 사용하면 간단합니다. bind:this 지령:

<counter-wc bind:this={wcInstance} color={color}></counter-wc>

이제 Svelte 템플릿에서 구성 요소의 증분 변수에 대한 변경 사항을 수신하고 기본 웹 구성 요소 속성을 설정합니다.

$: { if (wcInstance) { wcInstance.increment = increment; }
}

당신은 그것을 테스트 할 수 있습니다 이 라이브 데모에서.

우리가 관리해야 하는 모든 웹 구성 요소나 소품에 대해 이 작업을 수행하고 싶지는 않습니다. 우리가 그냥 설정할 수 있다면 좋지 않을까요? increment 일반적으로 구성 요소 props에 대해 수행하는 것처럼 웹 구성 요소의 마크업에 있습니다. 그냥 일이야? 즉, 의 모든 사용을 삭제할 수 있으면 좋을 것입니다. wcInstance 대신 다음과 같은 더 간단한 코드를 사용하세요.

<counter-wc increment={increment} color={color}></counter-wc>

우리가 할 수 있다는 것이 밝혀졌습니다. 이 코드는 작동합니다. Svelte는 우리를 위해 그 모든 수고를 처리합니다. 이 데모에서 확인하십시오. 이것은 거의 모든 JavaScript 프레임워크의 표준 동작입니다.

그렇다면 웹 컴포넌트의 prop을 수동으로 설정하는 방법을 왜 보여줬을까요? 두 가지 이유: 이러한 것들이 어떻게 작동하는지 이해하는 것이 유용하며, 저는 이것이 "거의" 모든 JavaScript 프레임워크에서 작동한다고 말했습니다. 그러나 우리가 방금 본 것처럼 웹 컴포넌트 소품 설정을 지원하지 않는, 미치도록 한 프레임워크가 있습니다.

반응은 다른 짐승입니다

React PlatoBlockchain 데이터 인텔리전스에서도 작동하는 상호 운용 가능한 웹 구성 요소 구축. 수직 검색. 일체 포함.
React와도 작동하는 상호 운용 가능한 웹 구성 요소 구축

반응합니다. 지구상에서 가장 인기 있는 JavaScript 프레임워크는 웹 구성 요소와의 기본 상호 운용성을 지원하지 않습니다. 이것은 React 고유의 잘 알려진 문제입니다. 흥미롭게도 이것은 실제로 React의 실험 브랜치에서 수정되었지만 어떤 이유로 버전 18에 병합되지 않았습니다. 그것의 진행 상황을 추적. 그리고 당신은 이것을 직접 시도 할 수 있습니다 라이브 데모.

물론 해결책은 다음을 사용하는 것입니다. ref, 웹 구성 요소 인스턴스를 잡고 수동으로 설정 increment 그 값이 변경될 때. 다음과 같습니다.

import React, { useState, useRef, useEffect } from 'react';
import './counter-wc'; export default function App() { const [increment, setIncrement] = useState(1); const [color, setColor] = useState('red'); const wcRef = useRef(null); useEffect(() => { wcRef.current.increment = increment; }, [increment]); return ( <div> <div className="increment-container"> <button onClick={() => setIncrement(1)}>Increment by 1</button> <button onClick={() => setIncrement(2)}>Increment by 2</button> </div> <select value={color} onChange={(e) => setColor(e.target.value)}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc ref={wcRef} increment={increment} color={color}></counter-wc> </div> );
}

논의한 바와 같이 모든 웹 구성 요소 속성에 대해 수동으로 코딩하는 것은 단순히 확장할 수 없습니다. 그러나 몇 가지 옵션이 있기 때문에 모든 것이 손실된 것은 아닙니다.

옵션 1: 모든 곳에서 속성 사용

우리에게는 속성이 있습니다. 위의 React 데모를 클릭했다면 increment 소품이 작동하지 않았지만 색상이 올바르게 변경되었습니다. 속성으로 모든 것을 코딩할 수는 없나요? 슬프게도, 아닙니다. 속성 값은 문자열일 수 있습니다. 여기에서는 충분하며 이 접근 방식을 사용하면 어느 정도 도달할 수 있습니다. 다음과 같은 숫자 increment 문자열로 변환할 수 있습니다. JSON을 문자열화/파싱할 수도 있습니다. 그러나 결국에는 웹 구성 요소에 함수를 전달해야 하며 그 시점에서 옵션이 없습니다.

옵션 2: 포장

간접 참조 수준을 추가하면 컴퓨터 과학의 모든 문제를 해결할 수 있다는 오래된 속담이 있습니다(너무 많은 간접 참조 수준 문제 제외). 이러한 소품을 설정하는 코드는 매우 예측 가능하고 간단합니다. 도서관에 숨기면 어떻게 될까요? Lit 뒤에 있는 똑똑한 사람들 하나의 솔루션을 가지고. 이 라이브러리는 웹 구성 요소를 제공하고 필요한 속성을 나열한 후 새로운 React 구성 요소를 생성합니다. 영리하지만 나는 이 접근 방식의 팬이 아닙니다.

웹 구성 요소를 수동으로 만든 React 구성 요소에 일대일로 매핑하는 것보다 내가 선호하는 것은 웹 구성 요소를 전달하는 React 구성 요소 태그 이름 ~까지counter-wc 우리의 경우) — 모든 속성 및 속성과 함께 — 이 구성 요소가 웹 구성 요소를 렌더링하려면 다음을 추가합니다. ref, 그런 다음 prop이 무엇이며 속성이 무엇인지 알아내십시오. 그것이 제 생각에 이상적인 솔루션입니다. 이 작업을 수행하는 라이브러리는 모르지만 생성하기는 간단해야 합니다. 한번 해보자!

이것은 용법 우리는 찾고 있습니다:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

wcTag 웹 구성 요소 태그 이름입니다. 나머지는 우리가 전달하고자 하는 속성과 속성입니다.

내 구현은 다음과 같습니다.

import React, { createElement, useRef, useLayoutEffect, memo } from 'react'; const _WcWrapper = (props) => { const { wcTag, children, ...restProps } = props; const wcRef = useRef(null); useLayoutEffect(() => { const wc = wcRef.current; for (const [key, value] of Object.entries(restProps)) { if (key in wc) { if (wc[key] !== value) { wc[key] = value; } } else { if (wc.getAttribute(key) !== value) { wc.setAttribute(key, value); } } } }); return createElement(wcTag, { ref: wcRef });
}; export const WcWrapper = memo(_WcWrapper);

가장 흥미로운 줄은 끝에 있습니다.

return createElement(wcTag, { ref: wcRef });

이것이 React에서 동적 이름으로 요소를 만드는 방법입니다. 사실 이것이 React가 일반적으로 JSX를 변환하는 것입니다. 우리의 모든 div는 다음으로 변환됩니다. createElement("div") 전화. 일반적으로 이 API를 직접 호출할 필요는 없지만 필요할 때 있습니다.

그 외에도 구성 요소에 전달한 모든 소품을 통해 레이아웃 효과와 루프를 실행하고 싶습니다. 우리는 그들 모두를 반복하고 그것이 다음을 가진 속성인지 확인합니다. in 웹 구성 요소 인스턴스 개체와 해당 프로토타입 체인을 확인하여 클래스 프로토타입에 포함된 모든 getter/setter를 포착하는지 확인하십시오. 그러한 속성이 없으면 속성으로 간주됩니다. 두 경우 모두 값이 실제로 변경된 경우에만 설정합니다.

사용하는 이유가 궁금하시다면 useLayoutEffect 대신 useEffect, 콘텐츠가 렌더링되기 전에 이러한 업데이트를 즉시 실행하기를 원하기 때문입니다. 또한, 우리는 우리의 의존성 배열이 없다는 점에 유의하십시오. useLayoutEffect; 이것은 우리가 이 업데이트를 실행하기를 원한다는 것을 의미합니다 렌더링할 때마다. React는 다시 렌더링하는 경향이 있으므로 위험할 수 있습니다. 많이. 나는 전체를 감싸서 이것을 개선한다. React.memo. 이것은 본질적으로 최신 버전의 React.PureComponent즉, 구성 요소는 실제 props가 변경된 경우에만 다시 렌더링되며 간단한 동등성 검사를 통해 변경되었는지 여부를 확인합니다.

여기서 유일한 위험은 재할당하지 않고 직접 변경하는 개체 소품을 전달하는 경우 업데이트를 볼 수 없다는 것입니다. 그러나 이것은 특히 React 커뮤니티에서 매우 권장되지 않으므로 이에 대해 걱정하지 않을 것입니다.

계속 진행하기 전에 마지막으로 한 가지만 말씀드리고 싶습니다. 사용법이 마음에 들지 않을 수 있습니다. 다시 말하지만, 이 구성 요소는 다음과 같이 사용됩니다.

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

특히 웹 구성 요소 태그 이름을 <WcWrapper> 구성 요소를 선택하고 대신 선호합니다. @lit-labs/react 각 웹 구성 요소에 대한 새로운 개별 React 구성 요소를 생성하는 위의 패키지. 그것은 전적으로 공정하며 가장 편안한 것을 사용하는 것이 좋습니다. 하지만 저에게 이 접근 방식의 한 가지 장점은 삭제. 어떤 기적에 의해 React가 실험 분기에서 적절한 웹 구성 요소 처리를 다음으로 병합한다면 main 내일은 위의 코드를 다음과 같이 변경할 수 있습니다.

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

…이에:

<counter-wc ref={wcRef} increment={increment} color={color} />

당신은 아마도 모든 곳에서 그것을 수행하기 위해 단일 codemod를 작성하고 삭제할 수 있습니다. <WcWrapper> 전부. 사실, 그것을 긁으십시오. 전역 검색 및 RegEx로 바꾸기가 아마도 작동할 것입니다.

구현

알아요, 여기까지 오기까지 여행이 필요했던 것 같아요. 기억하시겠지만, 우리의 원래 목표는 내 문서에서 본 이미지 미리보기 코드를 가져오는 것이었습니다. 게시물을 로딩, 모든 JavaScript 프레임워크에서 사용할 수 있도록 웹 구성 요소로 이동합니다. React의 적절한 상호 운용성 부족은 믹스에 많은 세부 사항을 추가했습니다. 그러나 이제 웹 구성 요소를 만들고 사용하는 방법에 대한 적절한 처리가 있으므로 구현은 거의 반절정적입니다.

여기에서 전체 웹 구성 요소를 삭제하고 흥미로운 부분을 불러오겠습니다. 실제 작동하는 모습을 보고 싶다면 여기 작업 데모. 내가 가장 좋아하는 세 가지 프로그래밍 언어에 대한 세 권의 책 사이를 전환합니다. 각 책의 URL은 매번 고유하므로 미리보기를 볼 수 있지만 실제로 일어나는 일을 보기 위해 DevTools 네트워크 탭에서 항목을 조절하고 싶을 것입니다.

전체 코드 보기
class BookCover extends HTMLElement { static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); } } set preview(val) { this.previewEl = this.createPreview(val); this.render(); } createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); } } createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () =&gt; { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img; } connectedCallback() { this.render(); } render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe); }
}

먼저 관심 있는 속성을 등록하고 변경될 때 반응합니다.

static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); }
}

이렇게 하면 이미지 구성 요소가 생성되어 로드될 때만 표시됩니다.

createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () => { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img;
}

다음으로 base64 미리보기 문자열 또는 blurhash 패킷:

set preview(val) { this.previewEl = this.createPreview(val); this.render();
} createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); }
}

이것은 우리가 필요로 하는 도우미 함수를 따릅니다.

function base64Preview(val) { const img = document.createElement('img'); img.src = val; return img;
} function blurHashPreview(preview) { const canvasEl = document.createElement('canvas'); const { w: width, h: height } = preview; canvasEl.width = width; canvasEl.height = height; const pixels = decode(preview.blurhash, width, height); const ctx = canvasEl.getContext('2d'); const imageData = ctx.createImageData(width, height); imageData.data.set(pixels); ctx.putImageData(imageData, 0, 0); return canvasEl;
}

그리고 마지막으로 우리의 render 방법:

connectedCallback() { this.render();
} render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe);
}

그리고 모든 것을 하나로 묶는 몇 가지 도우미 방법:

export function syncSingleChild(container, child) { const currentChild = container.firstElementChild; if (currentChild !== child) { clearContainer(container); if (child) { container.appendChild(child); } }
} export function clearContainer(el) { let child; while ((child = el.firstElementChild)) { el.removeChild(child); }
}

프레임워크에서 이것을 빌드하는 경우 필요한 것보다 약간 더 많은 상용구입니다. 그러나 장점은 우리가 원하는 모든 프레임워크에서 이것을 재사용할 수 있다는 것입니다. 비록 지금은 React에 래퍼가 필요하지만 논의한 대로 .

잡동사니

나는 이미 Lit's React 래퍼를 언급했습니다. 그러나 스텐실을 사용하고 있다면 실제로 스텐실을 지원합니다. React만을 위한 별도의 출력 파이프라인. Microsoft의 훌륭한 직원들도 Lit의 래퍼와 비슷한 것을 만들었습니다., Fast 웹 구성 요소 라이브러리에 첨부되었습니다.

내가 언급했듯이 React라는 이름이 없는 모든 프레임워크는 웹 구성 요소 속성 설정을 처리합니다. 일부는 구문의 특별한 풍미를 가지고 있다는 점에 유의하십시오. 예를 들어 Solid.js를 사용하면 <your-wc value={12}> 항상 가정합니다 value 다음으로 재정의할 수 있는 속성입니다. attr 접두사, 같은 <your-wc attr:value={12}>.

최대 포장

웹 구성 요소는 흥미롭고 종종 웹 개발 환경에서 잘 사용되지 않는 부분입니다. UI 또는 "리프" 구성 요소를 관리하여 단일 JavaScript 프레임워크에 대한 의존도를 줄이는 데 도움이 될 수 있습니다. Svelte 또는 React 구성 요소와 달리 웹 구성 요소로 생성하는 것은 인체 공학적이지 않지만 널리 재사용할 수 있다는 장점이 있습니다.


React와도 작동하는 상호 운용 가능한 웹 구성 요소 구축 원래에 게시 CSS 트릭. 너는해야한다. 뉴스레터 받기.

타임 스탬프 :

더보기 CSS 트릭