WebComponents.org Web Components

&

Polymer

박재성

About me

네이버 AU 개발랩

일반적인 웹 개발은?


HTML

CSS

JavaScript

Web Components?

재사용 가능한 컴포넌트를 만들 수 있는 표준 기술들의 모음

  1. Custom Elements : 커스텀 태그를 통한 요소 생성
  2. HTML Imports : HTML 페이지 로딩
  3. HTML Templates : 템플릿
  4. Shadow DOM : DOM과 스타일의 캡슐화

Custom Elements

커스텀 태그를 통한 요소 생성

  1. 새로운 HTML 요소를 생성
  2. 다른 요소를 확장해 생성가능
  3. 단일 태그에 커스텀 기능의 묶음 가능
  4. 기존 DOM 요소의 API를 확장

Custom Elements

기본 사용방법

태그명에 '-'(dash)는 반드시 포함해야 한다.
    // document.registerElement()를 통해 등록
    var NTag = document.registerElement('n-tag');

    // 또는 다음과 같이 특정 HTML 인터페이스를 사용해 등록할 수도 있다.
    // custom element는 기본적으로 'HTMLElement'를 상속한다.
    var NTag = document.registerElement('n-tag', {
        prototype: Object.create(HTMLElement.prototype)
    });

    // 문서에 추가
    document.body.appendChild(new NTag());
                        

Custom Elements

Custom Elements

기존 요소를 확장

    // <button> 태그를 확장
    var XButton = document.registerElement('x-button', {
        prototype: Object.create(HTMLButtonElement.prototype),
        extends: 'button'
    });

확장된 요소들은 type extension custom elements라 불리우며,
"element X is a Y"와 같이 사용

    <button is="x-button"></button>

Custom Elements

속성과 메서드 추가

Custom Elements

Lifecycle callback

콜백 발생시점
createdCallback 인스턴스가 생성될 때
attachedCallback 생성된 인스턴스가 문서에 추가될때
detachedCallback 인스턴스가 문서에서 제거될 때
attributeChangedCallback
(attrName, oldVal, newVal)
속성이 변경될 때(추가/삭제/수정)
    var proto = Object.create(HTMLElement.prototype);

    // 콜백 등록
    proto.createdCallback = function() { ... };
    proto.attachedCallback = function() { ... };

    var XFoo = document.registerElement('x-foo', {prototype: proto});

Custom Elements

컨텐츠가 포함된 custom element

Custom Elements

아직 요소의 등록이 제대로 되지 않은 상태인 경우,
FOUC* 상태로 페이지가 렌더링될 수 있다.

:unresolved pseudo class를 사용해 이를 방지할 수 있다.


    x-test { ... }
    x-test:unresolved { opacity: 0; }
                        
*FOUC(Flash Of Unstyled Content) - http://en.wikipedia.org/wiki/Flash_of_unstyled_content

HTML Imports

HTML 페이지를 로딩

  1. JS/HTML/CSS를 묶음 형태로 사용 → 단일 URL로 호출
  2. HTML Import를 통한 추가되는 컴포넌트들은 중복되는 경우라도 호출,
    파싱 및 실행은 단 한 번만 수행
  3. Import 내의 스크립트는 메인 문서의 파싱을 블럭하지 않음
  4. 스크립트는 import시 실행되나, 다른 요소(마크업,CSS 등)들은
    메인 페이지에 추가되는 시점에 활성화

HTML Imports

기본 사용방법

    <link rel="import" href="/path/file_name.html">

다른 도메인의 파일을 임포트 하기 위해선
CORS가 활성화 되어 있어야 한다.

HTML Imports

이벤트 (load & error)


    <link rel="import" href="/path/file_name.html"
            onload="handleLoad(event)"
            onerror="handleError(event)">

브라우저가 <link rel="import"> 태그를 만나면
즉시 로딩하기 때문에 이벤트 핸들러는
import 태그 이전에 선언되어야 한다.

HTML Imports

import된 컨텐츠의 활용


import가 수행된다고 해서 그 지점에 컨텐츠가
포함되는 형태는 아니며, 브라우저가 해당 파일을
파싱하고 사용할 수 있도록 준비되는 것.

import 속성을 사용해 컨텐츠 문서에 접근할 수 있다.

HTML Imports

페이지의 동작

main.html
<link rel="import" href="import.html">
import.html
<link rel="stylesheet" href="common.css">
<style>
    /* 이 블럭내의 스타일은 기본적으로 main.html에 반영됨 */
</style>
<script>
// import.html 문서를 가리킴. 즉, 현재 파일
var importDoc = document.currentScript.ownerDocument;

// main.html 문서를 가리킴. 즉, 현재 파일을 import 하는 파일
var mainDoc = document;

// import.html 문서내의 스타일시트를 main.html에 포함하고자 한다면, 아래와 같이 별도 추가작업이 필요
var styles = importDoc.querySelector('link[rel="stylesheet"]');
mainDoc.head.appendChild(styles.cloneNode(true));
</script>

HTML Imports

기억할 점

  • 스크립트는 window 문맥에서 실행
  • import는 메인 페이지의 파싱을 블럭하지는 않지만,
    렌더링은 블럭


    import되는 페이지에 style이 포함될 수 있으므로, 브라우저는 FOUC를 방지하기 위해 렌더링을 블럭

    비동기적으로 import 하고자 한다면 async 속성을 추가
    <link rel="import" href="/path/some.html" async>

  • import 파일들은 일반적인 리소스와 마찬가지로 브라우저 캐싱됨

HTML Imports


    
    <head><link rel="import" href="some.html"></head>
    <body>
    <script>
                var link = document.querySelector('link[rel="import"]');
                var content = link.import;
                var el = content.querySelector('div');
                document.body.appendChild(el.cloneNode(true));
        </script>
    </body>
                        

    
    <script>alert("파일이 import 되었습니다.");</script>
    <div>
          <style> h3 { color: red; } </style>
          <h3>HTML Imports</h3>
          <p>안녕하세요. 반갑습니다~!</p>
    </div>
                        

HTML Imports

HTML Templates

재사용을 위한 템플릿

  • 비활성화 상태의 복제 가능한 DOM chunk
  • 새로운 태그 : <template> … </template>
  • 태그 내의 태그들은 사용되기 전까진 파싱은 되나 렌더링되지 않음
  • 컨텐츠는 클론/사용 되기전까진 비활성
  • 페이지의 일부분이 아님
  • 중첩된 <template>은 동작하지 않음

HTML Templates


        // 1. content 속성을 사용해 템플릿의 노드(#document-fragment)에 접근할 수 있다.
        var content = document.getElementById("count").content;

        // 2. 템플릿내의 DOM에 대한 작업을 한다.
        var span = content.querySelector('span');
        span.textContent = parseInt(span.textContent, 10) + 1;

        // 3. 메인 DOM에 document.importNode()를 통해 추가한다.
        document.getElementById("content03").appendChild(
            document.importNode(content, true));
                        

        <!-- 템플릿 -->
        <template id="count">
          <div>Template used: <span>0</span></div>
          <script>alert('클릭하셨네요!');</script>
        </template>
                        

HTML Templates

Shadow DOM

DOM과 스타일의 캡슐화

  • 별도의 스코프를 갖는 DOM
  • 새로운 root node → "shadow root"
  • shadow root를 가지고 있는 요소는 "shadow host"라고 불리움
  • shadow host의 컨텐츠는 렌더링되지 않으며, 대신 shadow root의 컨텐츠가 렌더링됨
  • Polymer에서 생성하는 모든 요소들은 shadow DOM으로 처리

Shadow DOM

기본 사용방법

Shadow DOM

Pesudo class

Shadow DOM에 정의된 css 스타일은
기본적으로 ShadowRoot 스코프를 갖는다.

:host / :host(selector)

host 요소를 의미 (ShadowRoot context내에서만 사용가능)

Shadow DOM

Pesudo class

:host-context(selector)

host의 조상 요소들중 selector와 매칭되는 경우에만 host에 스타일을 지정

Shadow DOM

Pesudo element

::shadow

ShadowRoot를 갖는 요소

Shadow DOM

<content> : host의 특정 요소를 출력(포함)

Shadow DOM

몇가지 특징

  • 한개의 host에서 여러 개의 ShadowRoot를 만들 수 있음
  • 하지만, LIFO(Last In, First Out) 스택과 같이 마지막에 추가된
    ShadowRoot가 렌더링됨
  • 제일 처음 추가된 트리는 'older tree'
    → .olderShadowRoot 속성을 통해 접근
  • 제일 마지막에 추가된 트리는 'yonger tree'
    → .shadowRoot를 통해 접근

Shadow DOM

<shadow> : shadow root에서 shadow 요소를 출력(포함)

Shadow DOM

Element.getDistributedNodes()

<content>로 포함된 노드들을 접근할 수 있도록 한다.

Shadow DOM

Element.getDestinationInsertionPoints()

자신을 포함시킨 ShadowRoot 요소에 접근

Web Component를
사용하면?

태그 형태로 특정 기능을 갖는 UI 컴포넌트들을
삽입할 수 있어, 손쉬운 재사용이 가능해집니다!

Polymer?

The Polymer library is designed to make it easier and faster
for developers to create great,
reusable components for the modern web.

Is built on Web Components.
https://www.polymer-project.org/

Polymer Architecture

Polyfill?

브라우저가 네이티브 하게 지원하지 않는 기능을
사용 가능하도록 만들어주는 코드 모음
http://remysharp.com/2010/10/08/what-is-a-polyfill/

브라우저 호환성

Native link

(Chrome 36+ 부터 웹컴포넌트 스펙들은 네이티브하게 지원)


Polyfill link


Polymer

Evergreen Browser*에서

문제없는 실행을 목표.

*Evergreen Web Browser is a web browser
that updates itself without prompting the user.

http://tomdale.net/2013/05/evergreen-browsers/

Polymer Elements : Iron Elements

유틸리티 요소와 공통적 UI 요소들의 모음
Ajax, 애니메이션, 드래그&드롭, 아이콘 모음, 툴팁, etc.

https://elements.polymer-project.org/browse?package=iron-elements

Polymer Elements : Paper Elements

Material design*이 적용된 UI 요소들의 모음
버튼, 체크박스, 다이얼로그, 입력요소, 탭, 토스트, etc.

https://elements.polymer-project.org/browse?package=paper-elements

Material Design?

"머티리얼 디자인에서 표면과 그림자는 물리적인 구조를 형성하여,
사용자들이 화면 상의 어떤 부분을 터치할 수 있고
움직일 수 있는지 쉽게 이해할 수 있도록 돕습니다."
  • 다양한 디바이스를 아우르는 일관된 디자인
  • 질감이 느껴지는 표면(tactile surfaces)
  • 대담하고 선명한 그래픽 디자인 (bold graphic design)
  • 자연스러운 애니메이션

사용방법 #1

이미 만들어진 요소들을 사용하는 방법

  1. 1. webcomponents.js를 페이지에 로딩
    <script src="bower_components/webcomponentsjs/webcomponents.js"></script>
  2. 2. 사용할 요소를 페이지에 로딩
    <link rel="import" href="bower_components/paper-input/paper-input.html">
  3. 3. 페이지 내에 새로 추가된 요소를 태그로 선언
    <paper-input></paper-input>

이미 만들어진 요소들을 사용하는 방법

사용방법 #2 - 직접 요소를 생성

Step 1: 요소에 해당되는 페이지 작성

  1. 1. Polymer core를 페이지에 삽입
    <link rel="import" href="bower_components/polymer/polymer.html">
  2. 2. <dom-element>를 사용해 새로운 태그 등록
    <dom-element name="사용자정의-태그" noscript>
          <template>
              <span>내용</span>
          </template>
    </dom-element>
    <script>
        // element registration
        Polymer({
            is: "사용자정의-태그",
    
            ready:function(e) {
                // when element is ready
            },
    
            hi: function() {
                alert("안녕하세요~");
            },
    
            // add properties and methods on the element's prototype
            properties: {
                // declare properties for the element's public API
                greeting: {
                    type: String,
                    value: "Hello!"
                }
            }
        });
    </script>
    

사용방법 #2 - 직접 요소를 생성

Step 2: 사용될 페이지에서 요소 페이지 삽입 후, 태그로 선언

<head>
      <script src="bower_components/webcomponentsjs/webcomponents.js"></script>
      <link rel="import" href="./파일.html">
</head>
<body>
      <사용자정의-태그></사용자정의-태그>
</body>

직접 생성한 요소를 사용하는 방법

Polymer demo App

Topeka / More on : Built with Polymer

Vulcanize

사용되는 웹 컴포넌트 파일들을 병합해
HTTP request를 줄일 수 있도록 해주는 도구


    # 설치
    $ sudo npm install -g vulcanize

    # 사용
    $ vulcanize 대상파일.html --inline-scripts > 결과파일.html
                    
https://github.com/Polymer/vulcanize

Web Components Ecosystem

다양한 웹컴포넌트 생태계

Bower

대다수의 웹컴포넌트들은
Bower*를 통한 손쉬운 설치를 제공

$ bower install 컴포넌트명

*A package manager for the web - http://bower.io/

<clock-face>

$ bower install clock-face

<google-map>

$ bower install GoogleWebComponents/google-map

Mozilla Brick

모질라에서 개발한 UI 웹컴포넌트

https://mozbrick.github.io/

Reference

<thank-you>
 고맙습니다.
</thank-you>