리액트를 사용하면서 컴포넌트 라는 단어를 흔하게 사용했는데,
web component?
뭔가 익숙한 듯.. 낯선..
(설명할 수 없다는 것은 역시 아직 모른다는 것)
모를 땐 역시 정의와 공식 문서⬇️
웹 컴포넌트 - Web API | MDN
기능을 나머지 코드로부터 캡슐화하여 재사용 가능한 커스텀 엘리먼트를 생성하고 웹 앱에서 활용할 수 있도록 해주는 다양한 기술들의 모음
대표적으로 세가지가 있습니다.
1. Custom Elements 사용하기
2. Shadow DOM 사용하기
3. templates & slots 사용하기
HTMLElements 를 extends 한 JavaScript Class 를 우리는 사용자 정의 요소 혹은 사용자 정의 태그 라고 부릅니다.
export default function HelloWorld extends
HTMLElement {
connectedCallback(){
<!-- 뒤에서 설명 -->
window.requestAnimationFrame(()=>{
this.innerHTML = '<div>Hello World!</div>'
})
}
disconnectedCallback(){
<!-- 뒤에서 설명 -->
}
<!-- ... -->
}
위와 같이 새로 생성된 Custom Element를 사용하기 위해서는
window.customElements 속성의 define 메서드를 사용하여
브라우저 구성 요소 레지스트리에 추가해야 합니다.
import HelloWorld from './components/HelloWorld.js';
window.customElements.define('hello-world', HelloWorld)
custom elements를 사용할 때는,
- 로 구분된, 두 단어 이상의 태그를 사용해야 합니다.
한 개의 단어로 이루어진 태그는 W3C에서만 단독으로 사용 가능하기 때문입니다. (기존 html 마크업과 구분하기 위해)
<hello-world></hello-world>
표준 요소(Standard/Native Elements)의 경우에 속성을 설정하는 방법은 총 세가지가 있습니다.
*HTML 표준 요소는 영어로 "HTML Standard Elements" 또는 "HTML Native Elements"라고 부릅니다. 때로는 "HTML Built-in Elements"라고도 하며, 브라우저에 기본적으로 내장되어 있다는 의미에서 "HTML Native Controls" 또는 "HTML Built-in Controls"라고도 합니다.
<input type="text" value="Frameworkless" />
input.value = "Frameworkless"
setAttribute 메서드 사용하기input.setAttribute("value","Frameworkless")
위의 세가지 방법으로 변경된 value의 값은 서로 동기화가 되어 있습니다.
마크업을 통해 value를 입력해도 getter나 getAttribute를 통해 동일한 값을 얻어올 수 있다는 것입니다! custom elements 또한 동일하게 동작합니다.
위에서 custom elements를 설명하기 위해 작성한 코드에 connectedCallback, disconnectedCallback 이라는 메서드를 보셨을 겁니다.
이번에는 custom elements의 라이프사이클 메서드에 대해 알아보도록 하겠습니다.
구성 요소(element)가 DOM에 연결될 때 호출됩니다. React의 componentDidMount를 떠올리면 이해가 쉬울 것 같습니다.
콘텐츠를 렌더링하거나 타이머를 시작하거나, 또는 네트워크에서 데이터를 fetching하기 좋습니다.
마찬가지로 element가 DOM에서 삭제될 때 disconnectedCallback이 호출됩니다.
해당 메서드는 정리 작업에서 유용합니다.
*여기서 말하는 해당 속상 값이란, observedAttributes 라는 배열에 나열된 속성을 말합니다.(밑에 예시 코드 참고)
const DEFAULT_COLOR = 'black'
export default class HelloWorld extends HTMLElement {
static get observedAttributes(){
return ['color']
}
get color(){
return this.getAttribute('color') || DEFAULT_COLOR
}
set color(value){
this.setAttribute('color',value)
}
attributeChangedCallback(name,oldValue,newValue){
if(!this.div){return}
if(name === 'color'){
this.div.style.color = newValue
}
}
connectedCallback(){
window.requestAnimationFrame(()=>{
this.div = document.createElement('div')
this.div.textContent = 'Hello World"
this.div.style.color = this.color
this.appendChild(this.div)
})
window.addEventListener('resize',()=>{
console.log('view is resizing')
})
}
disconnectedCallback(){
window.removeEventListener('resize',()=>{
console.log('view is resizing')
})
}
}
사용자 정의 이벤트 API는 브라우저와 사용자 간 상호작용이 아닌 도메인에 국한된 DOM 이벤트를 생성할 수 있게 해줍니다.
// 커스텀 이벤트 생성
const AVATAR_LOAD_COMPLETE = 'AVATAR_LOAD_COMPLETE'
const AVATAR_LOAD_ERROR = 'AVATAR_LOAD_ERROR'
export const EVENTS = { AVATAR_LOAD_COMPLETE,AVATAR_LOAD_ERROR }
export default class GithubAvatar extends HTMLElement {
//...
onLoadAvatarComplete(){
const event = new CustomEvent(AVATAR_LOAD_COMPLETE,{
detail: { avatar: this.url }
})
this.dispatchEvent(event) // 커스텀 이벤트 AVATAR_LOAD_COMPLETE 발생
}
onLoadAvatarError(error){
const event = new CustomEvent(AVATAR_LOAD_ERROR, {
detail: { error }
})
this.dispatchEvent(event) // 커스텀 이벤트 AVATAR_LOAD_ERROR 발생
}
connectedCallback(){
document.querySelectorAll('github-avatar')
.forEach((avatar)=> {
avatar.addEventListener(EVENTS.AVATAR_LOAD_COMPLETE, (e) => {
console.log('avatar loaded', e.detail.avatar)})
avatar.addEventListener(EVENTS.AVATAR_LOAD_ERROR, (e)=>{
console.log('avatar load fail', e.detail.error)}
})
}
}