Closed13

Svelteでweb components

ピン留めされたアイテム
miyanokomiyamiyanokomiya

https://zenn.dev/tnzk/articles/835d3252ce01ed

素晴らしくまとめてくれている記事が登場のでこちらも参考。
webcomponentにもできるけどsvelteコンポーネントとの暗黙の仕様変換が多く、現状だとなかなかに茨の道であることに違いはない。

miyanokomiyamiyanokomiya

https://svelte.dev/docs#Custom_element_API

設定

compilerOptionsにcustomElement: trueを付ける。

宣言

component側にはこういう宣言を追加する。

Child.svelte
<svelte:options tag="my-child" />

<script>
export let name = ''
</script>

<p>{name}</p>

この宣言を付けたcomponentを利用する親componentにも同様の宣言を付けておかないとエラーになるっぽい。
importすればcustom elementとしてそのまま設置可能。

Parent.svelte
<svelte:options tag="my-parent" />

<script>
import './Child.svelte'
</script>

<my-child name="abc" />
miyanokomiyamiyanokomiya

デフォルト値なしのpropsだと、値を渡していてもなぜか~ was created without expected prop ~という警告が表示される。
値の受け渡し自体は問題なく行われている。

<script>
export let name
</script>

それっぽいissueもあったが今のところ仕方ない感じか?

https://github.com/sveltejs/svelte/issues/4457

miyanokomiyamiyanokomiya

vite環境でlibとしてビルドするには設定が必要。

vite.config.js
export default defineConfig({
  plugins: [svelte({
    compilerOptions: { customElement: true }
  })],
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/Target.svelte'),
      name: 'target-element',
    }
  }
})

これで.es.js.umd.jsが吐き出される。読み込めばcustom elementとしてそのまま利用可能。
.d.tsを吐き出す方法は見つけられず。

miyanokomiyamiyanokomiya

custom elementにするとキャメルなpropsに値を渡すことができないっぽい。

MyComp.svelte
export let someData;

下記のどちらでも値を渡せていなかった。

<my-comp someData={1} />
<my-comp some-data={1} />

関連issue。
現時点では変換処理を自作しないと対応できなさそう。
https://github.com/sveltejs/svelte/issues/3852

miyanokomiyamiyanokomiya

こういう記法によってcustom elementをscript側でmountすることができる。
ただしpropsが渡されるのはなぜかコンポーネント側のonMountが終わった後になる。省略可能な状態にしておいて、watchなどして渡されるのを待たないといけない。

import App from './App.svelte'

const app = new App({
  target: document.body,
  props: { count: 1 }
})

.es.jsとしてビルドされたモジュールをimportしてこういう風に使えるのかは試していないので不明。
.es.jsをimportしても同様にマウントできる。

miyanokomiyamiyanokomiya

propsの挙動やらがどうしてもsvelteとcustom elementで仕様上の違いがあるからその差異を把握していないと思わぬ落とし穴にはまる。
結局のところcustom element作るなら素でclass書いたほうがいいのではないか。

miyanokomiyamiyanokomiya

とはいえcustom elementになるルートコンポーネントでだけ気をつければ、後はsvelteに乗れるメリットが大きいか?なんともいえない。

miyanokomiyamiyanokomiya

svelteコンポーネントをnewでマウントする場合、そのコンポーネントのonMountが実行されるときにbind:thisに指定した変数への代入がまだ行われていない。
await tick()で待たないといけない。

孫コンポーネントでも同様の挙動になるとしたら非常に面倒。

miyanokomiyamiyanokomiya

↑はnewを親コンポーネントのonMountで行っている場合に起きるっぽい。
main.tsgetElementByIdしてnewを書いた場合は起きない。

このスクラップは2021/07/08にクローズされました