t28.dev

Vue2, Vue3 による Web components の出力について深ぼる

2021/11/12
Tech
  • 例によって UIT INSIDE に向けて、 Vue の Web components ビルドについて色々文書化しておく
  • これを書いた時点の Vue3 は v3.2.21

defineCustomElement の概要

Vue v3.2 で追加された defineCustomElement

公式ドキュメント: Vue and Web Components - defineCustomElement

  • defineCustomElement === VueComponent を HTMLElement で wrap するための関数
    • 正確には…
  • wrap したHTMLElementcustomElements.define に渡して Web components を定義する

Vue v2 では @vue/web-component-wrapper があった

Repo: github.com/vuejs/vue-web-component-wrapper

  • 使い方は defineCustomElement とほぼ同じ
    • VueComponent と Vue Object を渡して、返ってきた HTMLElementcustomElements.define する。

Web components の作り方

Vue2 & Vue CLI

Vue3 & Vue CLI

  1. Vue Component から HTMLElement を作って、登録する (公式ドキュメントのママ)

    import { defineCustomElement } from "vue";
    import Example from "./Example.ce.vue";
    
    const ExampleElement = defineCustomElement(Example);
    customElements.define("my-example", ExampleElement);
    
  2. @vitejs/plugin-vuevue-loader の設定

    • vue-plugin, webpack-loader は通常、 style 文字列を <head> 内に挿入しますが、Web components の場合は shadow root 内に挿入して欲しいです。
    • そのためのおまじない機能が defineCustomElement のリリースに合わせて追加されています。
    // vite.config.ts
    export default defineConfig({
      plugins: [vue({ customElement: true })],
    });
    
    • ちなみに、上記 import Example from "./Example.ce.vue"; の用に ファイル名から対象を選択することも出来ます。

defineCustomElement の実装を見てみる

考慮しないといけないポイント

Vue の props と Web components の attribute の扱い

  • Vue は js で props の定義を行うので、この props を読み取って、いい感じに Web components の attribute に変換してくれると思った
  • => 違った 😭
    • Number で扱えそうなら(parseFloat)、Number に 変換
    • Number で扱えなさそうなら、そのまま
  • 困ったこと
    • 構文解析に使っている parseFloat() は厳密な解析ではないので…出来る範囲で Number 化してしまう。
      • 先頭文字が数字であれば、とりあえず数字の部分を抜き出して Number
      • 1000-0000-0000 みたいな文字列のクーポンコードも Number として解釈される
  • PR で修正したいが…。

style の扱い

  • 子要素の SFC 内の style もまとめて ShadowRoot に挿入してくれると思った。
  • => 違った 😭
    • defineCustomElement は wrap する SFC 自身の style のみを読み込んで、ShadowRoot に挿入する
    • wrap する SFC のコンポーネントの style は挿入されない
  • 困ったこと
    • 入れ子構造になっている SFC で構成された VueComponent をそのままでは Web components 化出来ない。
  • Evan さん的には、子コンポーネントも Web components 化して使って欲しいらしい
    • ref: #4309
    • Vue から Web components を出力するケースって、対象のコンポーネントが比較的大きい(複数の SFC に分けたい)ものだと思うけれど…。
  • PR で修正したいが…。