Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Vue3 で TypeScript と slots を活用した 堅牢かつ柔軟な UI コンポー...

Ryoya HIRAYAMA
November 02, 2022
1k

Vue3 で TypeScript と slots を活用した 堅牢かつ柔軟な UI コンポーネントづくり

Reject Conference - Vue Fes Japan Online 2022
https://vuejs-meetup.connpass.com/event/259621/

上記のイベントで登壇した際に発表した資料です。

Ryoya HIRAYAMA

November 02, 2022
Tweet

Transcript

  1. 1 2 3 4 5 6 7 8 < </

    > template template > < = = = = /> Button icon icon-position icon-color text "bookmark" "left" "#ffffff" "ボタン" いろいろと受け付けることになりそう
  2. 1 2 3 4 5 < </ > template template

    > < = > < = /> </ > RouterLink to Button text RouterLink "/" "ボタン" RouterLink(NuxtLink)使いたい!
  3. 1 2 3 < </ > template template > <

    >{{ }}</ > button text button Button Component の中身
  4. 1 2 3 < > < > </ > </

    > a button button a = ボタン href "/" Rendering された HTML
  5. 1 2 3 < </ > template template > <

    : =" ">{{ }}</ > component is tagName text component component を利用して、タグ名を動的にしてみる
  6. 1 2 3 4 5 < </ > template template

    > < = > < = = /> </ > RouterLink to Button tag-name text RouterLink "/" "span" "ボタン" これだったら大丈夫そう!
  7. 1 2 3 < </ > template template > <

    = = = /> Button tag-name href text "span" "/" "ボタン" ただ、span タグで href 使われたらHTMLエラーだ
  8. 1 2 3 4 5 < </ > template template

    > < > < /> </ > div slot div MyComponent という名で以下のように定義すると…
  9. 1 2 3 4 5 < </ > template template

    > < > < >どんな要素でも受け付けられる</ > </ > MyComponent span span MyComponent 子要素を受け付けられるようになります
  10. 1 2 3 4 5 6 7 < </ >

    template template > < = = = = /> Button icon icon-position icon-color text "bookmark" "left" "#ffffff" "ボタン" いろいろと受け付けることになりそう
  11. 1 2 3 4 5 6 < </ > template

    template > < > < /> < >ボタン</ > </ > Button BookmarkIcon span span Button
  12. 1 2 3 4 5 6 7 8 < </

    > template template > < > < > < # >< /></ > < # >ボタン</ > </ > </ > Button SymbolText template symbol BookmarkIcon template template text template SymbolText Button SymbolText という Component を作ってみました
  13. 1 2 3 4 5 6 7 8 < </

    > template template > < > < > < # >< /></ > < # >ボタン</ > </ > </ > Button SymbolText template symbol BookmarkIcon template template text template SymbolText Button slots は name を決めて入れる場所を限定できる!
  14. 1 2 3 4 5 6 7 8 9 10

    < </ > template template > < = > < = > < = /> </ > < = > < = /> </ > </ > div class div class slot name div div class slot name div div "symbolText" "symbolText__item" "symbol" "symbolText__item" "text" slots を受け付ける SymbolText 側
  15. シンプル 1 2 3 4 5 6 7 8 <

    </ > template template > < > < > < # >※</ > < # >この文章は「ポラーノの広場」...</ > </ > </ > Button SymbolText template symbol template template text template SymbolText Button symbol として指定するものは Icon 以外でも良い
  16. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 16 17 18 19 20 < > script type type type type = = { : ... } = { : ... ?: ?: } = { : ... } = { : ... } lang tagName tagName href for tagName tagName "ts" 'a' 'button' 'label' 'span' ATagProps ButtonTagProps never never LabelTagProps SpanTagProps さらに、tagName の型を、 各タグ名の文字列で固定しておく。 それぞれのタグの Props を用意し、 タグごとに受け付けたくない Props を にする。 「never | undefined 型」
  17. 20 21 22 23 24 25 26 27 28 29

    30 31 type extends extends extends extends extends < | | | > = ? : ? : ? : ? : Props T T ATagProps T ButtonTagProps T LabelTagProps T SpanTagProps never 'a' 'button' 'label' 'span' 'a' 'button' 'label' 'span' Generics T を受け付け、T に応じて型を分岐できる
  18. 1 2 3 < </ > template template > <

    = /> Button tag-name "button" こうするだけで、Props<T> が と解釈される Props<’button’> 各 Props の tagName の型が決まっているので 連鎖的に T が決まるようになっている
  19. 31 32 33 34 35 36 37 38 39 40

    type extends => script = < >( : < > ) { : < > : {} } ({}) DefineComponentType T Props T Props T DefineComponentType new export default as 'a' | 'button' | 'label' | 'span' props $props $emit defineComponent </ > Props<T> を Volar に認識させる