웹 컴포넌트란?
바닐라 JS/HTML/CSS 등 웹표준 기반 언어를 사용하여 재사용 가능한 구성 요소를 만드는 APIs
무엇을 할 수 있을 까?
프레임워크에 종속되지 않는 독립적이며 재사용 가능한 컴포넌트 라이브러리 만들기
장점
- 커스텀화
- 재사용화
- 캡슐화
필수 개념
Custom element
HTML을 확장하여 새롭게 커스텀 엘레멘트를 만들 수 있도록 지원하는 JS APIs. 라이프사이클도 지원.
class AppDrawer extends HTMLElement { ... }
window.customElements.define('app-drawer', AppDrawer);
<app-drawer></app-drawer>
Custom Elements LifeCycle Methods
- constructor() : 엘레멘트의 인스턴스가 생성되거나 업그레이드될 때 호출
- connectedCallback() : 엘레멘트가 DOM에 삽입될 때 마다 호출
- disconnectedCallback() : DOM에서 엘레멘트가 삭제될 때 마다 호출
- attributeChangeCallback(attributeName, old, Value, newValue) : 속성이 추가, 삭제, 업데이트, 대체될 때 마다 호출
Shadow Dom
- HTML 캡슐화와 Scoped CSS 를 위해 사용한다.
- element.attachShadow({ mode: open}) 을 이용해 만들 수 있다.
- shadowRoot를 생성해, Shadow Dom에 접근하여 상호작용을 할 수 있다.
Template
- 캡슐화된 마크업을 정의하기 위해 사용한다.
- 템플릿 태그에 마크업과 스타일을 저장할 수 있다.
- slot을 이용하면, Shadow Dom 내부를 커스텀 할 수 있다.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Components Example</title>
</head>
<style>
h3 {
color: purple;
}
</style>
<body>
<h3>Hello World</h3>
<user-card
name="Jane"
avatar="https://randomuser.me/api/portraits/men/1.jpg"
>
<div slot="email">minsangtech.kim@gmail.com</div>
<div slot="phone">010-1234-5678</div>
</user-card>
<user-card
name="merry"
avatar="https://randomuser.me/api/portraits/men/2.jpg"
>
<div slot="email">eungyeol.lee@gmail.com</div>
<div slot="phone">010-9101-1121</div>
</user-card>
<script src="userCard.js"></script>
</body>
</html>
const template = document.createElement('template');
template.innerHTML = `
<style>
.user-card {
background: #f4f4f4;
width: 500px;
display: grid;
grid-template-columns: 1fr 2fr;
grid-gap: 10px;
margin-bottom: 15px;
border-bottom: darkorchid 5px solid;
}
.user-card img {
width: 100%;
}
.user-card button {
cursor: pointer;
background: darkorchid;
color: #fff;
border: 0;
border-radius: 5px;
padding: 5px 10px;
}
</style>
<div class="user-card">
<img />
<div>
<h3></h3>
<div class="info">
<p><slot name="email"/></p>
<p><slot name="phone"/></p>
</div>
<button id="toggle-info">Hide Info</button>
</div>
</div>
`;
class UserCard extends HTMLElement {
constructor() {
super();
this.showInfo = true;
this.attachShadow({ mode: 'open'});
this.shadowRoot.appendChild(template.content.cloneNode(true)); // 이게 뭐람?
this.shadowRoot.querySelector('h3').innerText = this.getAttribute('name');
this.shadowRoot.querySelector('img').src = this.getAttribute('avatar');
// 이렇게 하면 외부 DOM이랑 공유되는 상황
// this.innerHTML = `<style>h3 { color: red; }</style><h3>${this.getAttribute('name')}</h3>`;
}
toggleInfo = () => {
this.showInfo = !this.showInfo;
const info = this.shadowRoot.querySelector('.info');
const toggleBtn = this.shadowRoot.querySelector('#toggle-info');
if(this.showInfo) {
info.style.display = 'block';
toggleBtn.innerText = 'Hide Info';
} else {
info.style.display = 'none';
toggleBtn.innerText = 'Show Info'
}
}
connectedCallback() {
this.shadowRoot.querySelector('#toggle-info')
.addEventListener('click', () => this.toggleInfo());
}
disconnectCallback() {
this.shadowRoot.querySelector('#toggle-info')
.removeEventListener();
}
}
window.customElements.define('user-card', UserCard);
Custom element와 Shadow Dom의 관계
If custom elements are the way to create a new HTML (with a JS API), shadow DOM is the way you provide its HTML and CSS. The two APIs combine to make a component with self-contained HTML, CSS, and JavaScript. -> 커스텀 엘레멘트를 만드는데, 그 안의 내용을 캡슐화하면서 만들고 싶으니까 조합해서 사용하는 구나. 내부에 JS,CSS,HTML이 다 들어 있는 거임.s