웹 Components 분위기 조성

오래되고 우수한 DOM 작업


이전에 함수형 js는 실패했고 트리 구조에 집합되어 움직이기 쉬운 상태로 가상 DOM을 선언적으로 토로하는view 시대였다.
하지만 말단의 상태는 말단의 구성요소로 파악되며, 상류에서부터 필요할 때 필요한 것을 꺼내면 좋을 것 같으니 명령적으로 웹 컴포니언스에 적어보자.
마스토돈의 타임라인을 구상하면서 자꾸 삭감하는 예로 분위기를 대충 잡았다.

툴트 모듈


먼저 툴트 하나부터 시작해.
웹 Components는 Class 구조 상속 HTML Element에서 시작합니다.
class StatusItem extends HTMLElement {
    constructor() {
        super()

        this.attachShadow({mode: "open"})
    }
반드시 구조기로 슈퍼라고 불러야 한다.섀도우 루트 등의 준비도 진행한다.아직 렌더라고 안 불렀어.
또한, 자청상태로 자청의 id와 자청이 마음에 드는지 여부가 있습니다.
이것들은 속성으로 가지고 있다.자청 본문은 자 요소로 삽입해 주세요.
    get statusId() {
        return this.getAttribute("status-id")
    }
    set statusId(id) {
        this.setAttribute("status-id", id)
    }

    get favorite() {
        return this.hasAttribute("favorite")
    }
    set favorite(boolean) {
        this.toggleAttribute("favorite", boolean)
    }
connectdCallback에서 렌더링이 수행됩니다.
Shadow DOM 컨텐트를 변경하려는 경우 나중에 렌더링합니다.자 요소의 변경이 라이트덤이라면 신경 쓰지 않아도 된다.
색상만 살짝 바꾸면 CSS가 가능하기 때문에 이번에는 connected Callback만 실시합니다.
생DOM으로 전부 쓰면 정기의 문제가 아니기 때문에lit-}로 빠르게 쓴다.
    connectedCallback() {
        render(html`
            <style>
                :host { --button-color: white; }
                :host([favorite]) { --button-color: pink; }

                button { background-color: var(--button-color) }
            </style>

            <div>
                [${this.statusId}]
                <slot></slot>
                <button type="button" @click=${this.favor.bind(this)}>
                    Fav.
                </button>
            </div>
        `, this.shadowRoot)
    }
자청을 좋아하는 처리라고 써도 돼요.
    favor(e) {
        e.preventDefault()

        this.favorite = true;

        const event = new CustomEvent("favorite", {
            bubbles: true,
            cancelable: true,
            composed: true
        })

        this.dispatchEvent(event)
    }
툴트 어셈블리에서 API를 직접 두드리는 것이 아니라 원하는 이벤트에 불을 붙인다.
행사로 방류하다 보니 좋아하는 곳에서 탈락한 행사를 주웠다.이벤트를 사용하지 않고 onfavorite 속성만 정의하면 손의 물통 릴레이가 시작되기 때문에 할 수 없습니다.
이벤트의 target이 국경을 넘을 때 Shadow Root의 호스트로 다시 설정됩니다. 즉 사용자 정의 요소 자체입니다.또한 맞춤형 요소는 이벤트 처리에 필요한 정보(즐겨찾기 폴더의 푸른 id)를 저장합니다.이것이 바로'필요할 때 필요한 것을 끌어낸다'는 구조다.
주의사항으로 현장 HTML에 onfavorite 같은 것을 써도 프로세서가 등록되지 않는다.jsx와lit-} 같은 방법으로 렌더링할 때ddEvent Listener로 변환되기 때문에 문제없을 것입니다.또 섀도우 루트composed: true를 뛰어넘기 위해서도 필수다.
이렇게 등록하면 HTML에 이렇게 적힌 요소가 사용자 정의 요소로 승격된다.
customElements.define("status-item", StatusItem)
<status-item status-id="1000000000000">hello!</status-item>

타임라인 어셈블리


자청은 맞춤형 요소로 제작된 것이기 때문에 당연히 자청을 묶는 부품에서 자요소로 자청을 가지고 있다.
하지만 자청은 때때로 사라진다.
보이기 싫어서 사라졌는데 역시 O(1)로 푸는 게 친근하죠.
다수의 툴트에서 id로 그린 구조가 있다는 것이다.
그 구조는 구성 요소 안에 숨길 수 있다.
우선 constructor로 준비하세요.
사전으로 사용할 맵과 Mutation Observer를 준비했습니다.
class StatusCollection extends HTMLElement {
    constructor() {
        super()

        this.observer = new MutationObserver(this.mutationCallback.bind(this))
        this.statusMap = new Map()
        this.attachShadow({mode: "open"})
    }
connected Callback에 자신을 observer에 등록한 다음 생성된 하위 요소를 사전에 업로드합니다.
disconnected.
    connectedCallback() {
        this.observer.observe(this, {childList: true});

        [...this.children]
            .filter(x => x.localName == "status-item")
            .forEach(x => {
                // status-itemがアップグレードされていない可能性がある
                this.statusMap.set(x.getAttribute("status-id"), x)
            })

        render(html`
            <slot></slot>
        `, this.shadowRoot)
    }

    disconnectedCallback() {
        this.observer.disconnect()
    }
가까스로 status Id를 정의했는데 이 Getter는 Get Attribute라고 합니다. 이 때 status-item이 업그레이드되지 않았기 때문입니다.나는 undefind 때문에 잠시 고민했다.
그리고 하위 요소가 변경될 때 사전을 업데이트합니다.
    mutationCallback(mutations) {
        mutations.forEach(mutation => {
            [...mutation.addedNodes]
                .filter(x => x.localName == "status-item")
                .forEach(status => {
                    this.statusMap.set(status.statusId, status)
                });

            [...mutation.removedNodes]
                .filter(x => x.localName == "status-item")
                .forEach(status => {
                    this.statusMap.delete(status.statusId)
                });
        })
    }
이렇게 되면 자요소 내용element.statusMap.get(id)에서 자청을 끌어낼 수 있다.당기면 리모브차일드로 닦아.
이후 자청 부품과 마찬가지로 등록 후 사용합니다.
customElements.define("status-collection", StatusCollection)
    <status-collection>
        <status-item status-id="1000000000000">hello!</status-item>
    </status-collection>
맞춤형 요소의 명칭을 이용자의 종파에 의뢰하는 사람은 속성으로 아이에게 기대하는 이름을 미리 부여할 수 있다.
    get statusElementName() {
        return this.getAttribute("status-element-name") || "status-item"
    }
    set statusElementName(localName) {
        this.setAttribute("status-element-name", localName)
    }
입력만 제출할 수 있는 표를 배열해 보았다.앱 피드가 잊혀질 거라고 말해줬으면 좋겠어.
    <form onsubmit="return false">
        <input name="note" />
        <button type="button"
            onclick="
                const element = document.createElement('status-item')
                element.statusId = Date.now()
                element.innerText = this.form.note.value
                document.querySelector('status-collection').appendChild(element)
            "
        >
            toot!
        </button>
    </form>

좋은 웹페이지 즐겨찾기