HTML 사용자 정의 요소에서 템플릿의 다양한 방법
33964 단어 htmlwebdevtypescriptjavascript
{{substitutions}}
를 어디에 두었는지 알려주면 거기서부터 시작할 거야."하.
#1점: 이 교체는 템플릿 엔진의 일부이며, 이 프레임에는 템플릿 엔진이 없습니다.본고에서 나는 구성 요소의 HTML 부분을 처리할 때 많은 선택을 상세하게 소개할 것이다. 예를 들어 구성 요소의 형식을 저장하고, 어떻게 매개 변수화하는지, 언제 사용하는지
<template>
표시를 하고, 어떻게 보여주고 다시 보여주는지, 그리고 그것을 언제 다시 보여주는지 어떻게 알 수 있는지.이 예시에서, 나는 제로 의존적인 단일 파일 구성 요소를 사용하기 때문에, HTML을
fetch
ed 코드와 분리하지 않을 것이다.나는 문서 작성에 약간의 Typescript를 사용할 것이다.나는 또한 항상 음영DOM을 사용하고 HTML/CSS를 코드 파일의 밑에 보존하기를 원한다. 이것은 그것이 간단하기 때문이 아니라 어렵기 때문이다.우리의 예는
import
구성 요소입니다. flexbox의 줄 정렬에만 하위 항목의 스타일을 설정합니다.이것은 하위 노드를 입력으로 받아들이기 때문에, 우리는 <flex-row>
표시가 본 컴퓨터 프레임워크가 이 하위 노드를 어디에 두었는지 표시할 것이다.사용 예:<flex-row>
<div>Left sidebar.</div>
<div>Main content, containing a very long text that wraps around the viewport multiple times. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
</flex-row>
가장 간단한 예
customElements.define('flex-row', class extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' }).innerHTML = /*html*/`<slot style="display: flex; flex-direction: row; flex: 1 1 auto; align-items: flex-start;"></slot>`;
}
})
<slot>
정의를 최종 class
호출에 직접 연결함으로써, 우리는 실제적으로 클래스를 명명할 필요가 없다..define
그림자 DOM을 만들고 인용을 되돌려줍니다. 우리는 즉시 .attachShadow
문자열을 사용하여 그것을 만들 수 있습니다..innerHTML
주석은 IDE를 알려 줍니다. 백틱 문자열은 색 문법으로 HTML로 강조해야 합니다.CSS는 내연이지만 /*html*/
이후<style></style>
도 일할 수 있다.그러나 만약 우리의 템플릿이 매우 크다면, 우리는 그것을 코드에서 파일의 밑부분으로 옮길 수 있다.틀을 밑에 놓다
customElements.define('flex-row', class extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' }).innerHTML = template();
}
})
function template() {
return /*html*/`<slot style="display: flex; flex-direction: row; flex: 1 1 auto; align-items: flex-start;"></slot>`;
}
템플릿을 파일 끝으로 이동하면 더욱 깨끗해 보입니다.그러나 우리가 </slot>
를 사용한다면 너무 일찍 .define
에 전화를 걸면 template() is undefined
와 오류가 발생할 수 있습니다.그래서 여기서 const template = () => ...
를 사용해야 승급할 수 있습니다.또는 나는 이 종류를 명명하고 function
호출을 맨 밑으로 옮길 수 있다.잊기 쉬운 곳.어쨌든, 템플릿에 매개 변수
.define
를 추가해서 논쟁을 진행합시다.<flex-box wrap='nowrap'>
에 매개변수 추가하기customElements.define('flex-row', class extends HTMLElement {
connectedCallback() {
const wrap = this.getAttribute('wrap');
this.attachShadow({ mode: 'open' }).innerHTML = template(wrap);
}
);
function template(flexWrap: string) {
return /*html*/`<slot style="display: flex; flex-direction: row; flex: 1 1 auto; align-items: flex-start; ${flexWrap ? `flex-wrap: ${flexWrap};` : ''}"></slot>`;
}
flex-wrap
에 음영을 추가하는 것은 connectedCallback
에 음영을 추가하는 것이 아니라 원소의 속성이 준비되어 있다는 차이점이다.우리의 템플릿 함수는 어떤 함수처럼 매개 변수화되어 있다.지금 이 기능을 사용하는 것은 매우 이상하다.왜
constructor
아니면<flex-box wrap='nowrap'>
또는 <flex-box nowrap>
입니까?그것 또한 영원히 다시 과장되지 않을 것이다.이 두 가지 기능을 추가합시다.렌더링 방법 및 속성 모니터링
customElements.define('flex-row', class FlexRow extends HTMLElement {
connectedCallback() {
this.wrap = typeof this.getAttribute('wrap') === 'string' ? 'wrap' : typeof this.getAttribute('nowrap') === 'string' ? 'nowrap' : '';
this.attachShadow({ mode: 'open' }).innerHTML = template(this.wrap);
}
#wrap = '';
get wrap() { return this.#wrap; }
set wrap(v) { this.#wrap = v; FlexRow.render(this); };
static render({ shadowRoot, wrap }: FlexRow) {
if (shadowRoot) shadowRoot.innerHTML = template(wrap);
}
});
function template(flexWrap: string) {
return /*html*/`<slot style="display: flex; flex-direction: row; flex: 1 1 auto; align-items: flex-start; ${flexWrap ? `flex-wrap: ${flexWrap};` : ''}"></slot>`;
}
여기 열어야 할 게 많아요.1) 우리는 init에서 두 개의 속성
<flex-box wrap>
과 wrap
을 검사한다.2) 우리는 적당한 문자열을 자바스크립트의 개인 변수
nowrap
에 저장할 것이다.(Typescript는 브라우저 지원이 아직 완료되지 않았으나, 내 다음 예제에서는 사용하지 않을 것입니다.)3) 외부 코드가 속성을 변경할 때 반응할 수 있도록 Getter/setter 쌍을 만듭니다.
4) 정적
this.#wrap
함수를 다시 렌더링하기 위해 설명했습니다.현재 소비자들은
render
과 <flex-row wrap>
등 짧은 속성을 사용하여 우리의 구성 요소를 초기화할 수 있지만 언제든지 속성<flex-row nowrap>
을 사용하여 변경할 수 있다.만약 우리가 지원 필드
ourElement.wrap = 'nowrap';
를 개인 구성원으로 설정하지 않았다면 소비자들은 직접 #wrap
로 그것을 클릭한 후에 왜 아무것도 작동하지 않는지, 왜 브라우저 devtools에 ourElement._wrap = ...
속성이 있고 wrap
속성이 있는지 알 수 없을 것이다.이와 유사하게, 우리는 렌더링 함수를 정적으로 해서 그것을 숨긴다.이것은 확실히 명명류가 필요하지만, 보답으로 우리는 대량의
_wrap
접두사를 사용하지 않도록 파라미터를 분해할 수 있다.두 번째: 속성과 속성은 HTML 사용자 정의 요소에서 자동으로 서로 대칭복사되지 않습니다.생각해 봐
this.
의.value
: 속성은 초기 값을 가지고 실제로는 상수이며 속성만 사용자에 따라 업데이트된다.HTML 사용자 정의 요소에서는 모든 것이 이렇습니다.그러나 만약에 우리가 일부 렌즈를 원한다면, 특히 CSS는 flex행이 현재 포장 중인지 여부에 따라 자신을 바꿀 수 있다면 어떻게 해야 합니까?CSS는 속성이 아니라 속성만 봅니다...
렌더링 방법 및 속성 모니터링 2
class FlexRow extends HTMLElement {
connectedCallback() {
this.wrap = typeof this.getAttribute('wrap') === 'string' ? 'wrap' : typeof this.getAttribute('nowrap') === 'string' ? 'nowrap' : '';
this.attachShadow({ mode: 'open' }).innerHTML = template(this.wrap);
}
get wrap() { return this.getAttribute('wrap'); }
set wrap(v) { this.setAttribute('wrap', v || ''); render(this); };
}
const render = ({ shadowRoot, wrap }: FlexRow) => shadowRoot ? shadowRoot.innerHTML = template(wrap) : null
const template = (flexWrap: string | null) =>
/*html*/`<slot style="display: flex; flex-direction: row; flex: 1 1 auto; align-items: flex-start; ${flexWrap ? `flex-wrap: ${flexWrap};` : ''}"></slot>`;
customElements.define('flex-row', FlexRow);
여기 몇 가지 일이 있습니다.속성이 변경될 때 속성을 업데이트해야 하기 때문에 이 속성도 지원 필드일 수 있습니다.
예를 들기 위해서, 나는
<input type=text value='initialVal' />
를 맨 밑으로 옮겼고, 화살표 함수를 사용하여 .define
를 표시할 수 있도록 허락했다.이것은 아름다운 단행 프로그램이기 때문에 우리는 정적 프로그램과 같은 방법을 사용할 것이다template()
.새로운 기능이 몇 가지 문제를 가져왔다.
render()
속성은 일치하는 속성이 부족하고 nowrap
속성은 문자열'nowrap'을 받아들여 사용자를 곤혹스럽게 합니다.나는 이 설계 문제를 토론하는 것을 연기할 것이다. 왜냐하면 그것은 틀과 무관하기 때문이다. 그러나 나는 확실히 그것을 말하고 싶다.더 중요한 것은 앞으로
wrap
또는 ourElement.attributes['wrap'] = 'wrap'
를 통해 속성을 업데이트하는 것은 아무런 역할을 하지 않을 것이다. 설령 이런 속성이 처음에 작용하더라도 우리 사용자를 실망시킬 수 있다.속성 및 속성 모니터링
class FlexRow extends HTMLElement {
connectedCallback() {
this.wrap = typeof this.getAttribute('wrap') === 'string' ? 'wrap' : typeof this.getAttribute('nowrap') === 'string' ? 'nowrap' : '';
this.attachShadow({ mode: 'open' }).innerHTML = template(this.wrap);
}
get wrap() { return this.getAttribute('wrap'); }
set wrap(v) { this.setAttribute('wrap', v || ''); render(this); };
static observedAttributes = ['wrap', 'nowrap'];
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (oldValue !== newValue) render(this);
}
}
const render = ({ shadowRoot, wrap }: FlexRow) => shadowRoot ? shadowRoot.innerHTML = template(wrap) : null
const template = (flexWrap: string | null) =>
/*html*/`<slot style="display: flex; flex-direction: row; flex: 1 1 auto; align-items: flex-start; ${flexWrap ? `flex-wrap: ${flexWrap};` : ''}"></slot>`;
customElements.define('flex-row', FlexRow);
추가 ourElement.setAttribute('wrap','wrap')
, static (Required) attributeChangedCallback
에 첫 번째 인자의 가능한 값을 표시하여 속성 변경에 반응할 수 있도록 합니다.일반적으로, 우리는 이 함수에서 값을 속성에서 속성으로 복사하기 위해 observedAttributes
를 사용해야 하지만, 우리의 속성은 이미 이 속성을 가리키는 Getter/setter를 사용하기 때문에 필요없다.물론 우리는 무한 순환을 검사해야 한다.나는 본문 첫머리에 React와 Angular와 같은 간단한 교체가 없다고 말했다.템플릿 문자열 텍스트와
this[name] = newValue;
를 사용하여 문자열을 해석해 왔습니다.구성 요소가 영원히 다시 렌더링되지 않는다면, 이것은 매우 좋은 것입니다. 왜냐하면 이것은 영구적이며, 주 메뉴 표시줄과 같은 일회용이기 때문입니다.그러나 리셋을 도입할 때 문자열의 재해석은 성능에 큰 영향을 미칠 수 있다.초기 렌더링에 대한 단일 문자열 해석과 다시 렌더링에 대한 정확한 업데이트를 통해 성능 문제를 해결할 수 있습니다.우리는
.innerHTML
HTML 표기와 <template>
방법으로 이 점을 실현해야 한다.HTML과 해당 준비 단계는 여전히 파일의 맨 아래에 있습니다.미리 컴파일된 템플릿, 최소 다시 렌더링
class FlexRow extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' }).appendChild(createdTemplateNode!.content.cloneNode(true));
updateTemplateNode(this);
}
get wrap() { return this.getAttribute('wrap'); }
set wrap(v) { this.setAttribute('wrap', v || ''); this.removeAttribute('nowrap'); updateTemplateNode(this); };
static observedAttributes = ['wrap', 'nowrap'];
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (oldValue !== newValue) updateTemplateNode(this);
}
}
const txt = /*html*/`
<template>
<slot style="display: flex; flex-direction: row; flex: 1 1 auto; align-items: flex-start;"></slot>
</template>
`;
const createdTemplateNode = new DOMParser().parseFromString(txt, 'text/html').head.querySelector('template');
const updateTemplateNode = ({ shadowRoot, wrap }: FlexRow) => {
const node = shadowRoot?.firstElementChild as HTMLElement;
if (!node) return;
if (wrap) node.style.setProperty('flex-wrap', wrap);
else node.style.removeProperty('flex-wrap');
}
customElements.define('flex-row', FlexRow);
우리는 .cloneNode
명명render
을 updateTemplateNode
으로 바꾸고 createdTemplateNode
명명과 평행하며 암시render
가 전체 이야기라는 것을 피할 것이다.우리의
template()
함수는 현재 txt
라는 변수일 뿐이며, 매개 변수는 한 번만 사용되기 때문에 더 이상 받아들일 수 없습니다.이 내용은 <template>
표시에 포장되어 있습니다. 현재 DOMParser가 텍스트를 분석하고 연결을 끊은 노드 트리를 되돌려주기 때문입니다.그리고 우리는 즉시 <template>
표시를 위해 검색을 선택한 다음, 지금부터 이 나무는 우리 clone
의 모든 실례에 있는 나무가 될 것이다.일단 템플릿의 주요 부분이 자리를 잡으면, 우리는
<flex-row>
변경이 필요한 곳에서만 그것을 만질 수 있다.이 예는 updateTemplateNode
매개 변수만 있기 때문에, 우리의 업데이트는 단지 하나의 코드일 뿐입니다. 정확한 노드의 스타일 속성을 설정하거나 삭제하는 데 사용되며, 이 노드는 항상 루트 노드 이외의 첫 번째 하위 노드입니다.그것의 유형과 복잡성은 약간 높지만, 다시 렌더링할 때sole
flex-wrap
는 문자열을 만들고 해석하는 속도보다 훨씬 빠르며, 특히 상기 문자열이 다른 사용자 정의 구성 요소를 호출할 때보다 빠르다.어느 것이 가장 좋아요?
사실 나는 마지막 예를 계속 사용하는 것을 건의하지 않는다.기본 UI 프레임워크가 아닌 HTML 사용자 정의 요소의 장점 중 하나는 제로 의존적이고 제로 라이브러리의 구성 요소를 만들 수 있다는 것이다.이것은 마이크로 전단을 만드는 데 매우 유용하다.그러나 우리가 속도를 최적화하려고 시도할 때 추가 코드 자체는 메모리와 안내 시간을 소모하고 모든 구성 요소 간에 같은 코드를 공유할 수 있는 라이브러리가 없으면 원가가 배로 증가한다.
여기까지 읽어줘서 고마워요.나는 네가 나의 작은 여행이 매우 재미있다고 느끼기를 바란다.
Reference
이 문제에 관하여(HTML 사용자 정의 요소에서 템플릿의 다양한 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ronnewcomb/the-many-ways-of-templates-in-html-custom-elements-41i7텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)