본문 바로가기
front/vue

[vue] Vue and Web Components

by juniKang 2022. 8. 9.

웹 컴포넌트는 개발자가 재사용 가능한 커스텀 엘리먼트를 만들게 해주는 웹 네이티브 API의 모음을 지칭하는 포괄적인 용어이다. 

 

우리는 뷰와 웹 컴포넌트가 상호 보완적인 관계라고 생각한다. 뷰는 커스텀 엘리먼트를 생성하고 소비하는데 최적의 지원을 해준다. 커스텀 엘리먼트를 뷰 애플리케이션에 통합하려하거나, 뷰를 사용해서 커스텀한 엘리먼트를 구축하고 배포하는 것 모두 좋은 사용 예이다.

 


뷰에서 커스텀 엘리먼트를 사용하기

뷰는 커스텀 엘리먼트 테스트를 100% 통과했다. 뷰 애플리케이션 안에있는 커스텀 엘리먼트를 사용하는 것과 네이티브 HTML 엘리먼트를 사용하는 것은 몇가지만 주의하면 대부분 동일하게 동작한다:

 

컴포넌트 확인 스킵하기

기본적으로, 뷰는 네이티브 HTML 태그가 아닌 태그들을 커스텀 엘리먼트로 렌더링을 시도하기 전에 등록된 뷰 컴포넌트 중 하나인지 확인한다. 이것이 뷰가 개발도중 "faild to resolve component" 경고를 나타내는 이유이다. 뷰에게 특정 엘리먼트는 커스텀 엘리먼트로 다뤄서 컴포넌트 확인을 스킵하라고 알려줘야 한다. 우리는 compilerOptions.isCustomElement 옵션으로 특정할 수 있다.

 

만약 build setup 으로 뷰를 사용한다면, 옵션은 build configs를 통해 넘어갈 수 있다. 왜냐하면 옵션이 컴파일 타임 옵션이기 때문이다.

 

In-browser 설정 예제

// Only works if using in-browser compilation.
// If using build tools, see config examples below.
app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-')

Vite 설정 예제

// vite.config.js
import vue from '@vitejs/plugin-vue'

export default {
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // treat all tags with a dash as custom elements
          isCustomElement: (tag) => tag.includes('-')
        }
      }
    })
  ]
}

Vue CLI 설정 예제

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options => ({
        ...options,
        compilerOptions: {
          // treat any tag that starts with ion- as custom elements
          isCustomElement: tag => tag.startsWith('ion-')
        }
      }))
  }
}

DOM 프로퍼티들을 넘기기

돔 애트리뷰트는 문자열만 될 수 있기 때문에, 돔 프로퍼티로서 커스텀 엘리먼트로 복잡한 데이터를 넘겨야 한다. 커스텀 엘리먼트에 프로퍼티들을 세팅할 때, 뷰 3는 자동적으로 돔 프로퍼티 가 in 오퍼레이터를 사용했는지 체크하고, 키값이 존재한다면 돔 프로퍼티로서 값들을 세팅할 것이다. 이 말은, 대부분의 경우, 커스텀 엘리먼트가 추천되는 사용 사례를 따른다면 이것에 대해 생각할 필요가 없다는 것이다.

 

하지만, 커스텀 엘리먼트가 적절하게 정의되어 프로퍼티로 투영되지 않으면 발생하는 (in 체크가 실패), 데이터가 돔 프로퍼티로서 넘겨져야만 하는 특이 케이스가 있다. 이런 경우, .prop 제어자를 사용해서 v-bind 바인딩을 돔프로퍼티로서 강제로 지정할 수 있다.

 

<my-element :user.prop="{ name: 'jack' }"></my-element>

<!-- shorthand equivalent -->
<my-element .user="{ name: 'jack' }"></my-element>

뷰로 커스텀 엘리먼트 만들기

커스텀 엘리먼트의 주요 이점은 모든 프레임워크에서 사용될 수 있다는 것이다. 심지어 프레임워크 없이도 사용할 수 있다. 따라서 배포된 컴포넌트를 소비자들이 다른 프론트엔드 스택이어도 사용할 수 있고, 원한다면 컴포넌트의 상세 구현을 사용하는 애플리케이션에서 구현토록 할수도 있다.

 

defineCustomElement

뷰는 defineCustomElement 메소드를 통해서 완전히 같은 뷰 컴포넌트 APIs 를 사용해서 커스텀 엘리먼트를 만들 수 있다. 이 메소드는 defineComponent와 같은 아규먼트를 받는다. 하지만 대신 HTMLElement를 상속받은 커스텀 엘리먼트 생성자를 리턴한다:

<my-vue-element></my-vue-element>
import { defineCustomElement } from 'vue'

const MyVueElement = defineCustomElement({
  // 일반적인 뷰 컴포넌트 옵션은 여기에 적는다.
  props: {},
  emits: {},
  template: `...`,

  // defineCustomElement only: CSS는 shadow root로 삽입될 수 있다.
  styles: [`/* inlined css */`]
})

// 커스텀 엘리먼트를 등록한다.
// 등록 이후에, 페이지 위의 모든 `<my-vue-element>` 태그는
// 업그레이드 된다.
customElements.define('my-vue-element', MyVueElement)

// 또한 프로그램적으로 원소를 인스턴스화 할 수 있다:
// (등록 이후에만 가능하다.)
document.body.appendChild(
  new MyVueElement({
    // initial props (optional)
  })
)

라이프 사이클

  • 뷰 커스텀 엘리먼트는 엘리먼트의 connectedCallback이 처음 호출되면 shadow 루트안에 내부 뷰 컴포넌트 인스턴스를 마운트 한다.
  • 원소의 disconnectedCallback이 호출되면, 뷰는 마이크로테스크 틱 후에 document에서 엘리먼트를 분리할지 말지 결정한다.
    • 원소가 document에 있다면, 이동어고 컴포넌트 인스턴스는 보존된다.
    • 원소가 document로 부터 분리되면, 이건 제거되고 컴포넌트 인스턴스는 언마운트 된다.

'front > vue' 카테고리의 다른 글

[vue] Rendering Mechanism  (0) 2022.08.11
[vue] Render Functions & JSX  (1) 2022.08.10
[vue] TransitionGroup  (0) 2022.08.08
[vue] Transition  (0) 2022.08.08
[vue] watch()  (0) 2022.08.08

댓글