Svelteでweb components
素晴らしくまとめてくれている記事が登場のでこちらも参考。
webcomponentにもできるけどsvelteコンポーネントとの暗黙の仕様変換が多く、現状だとなかなかに茨の道であることに違いはない。
設定
compilerOptionsにcustomElement: true
を付ける。
宣言
component側にはこういう宣言を追加する。
<svelte:options tag="my-child" />
<script>
export let name = ''
</script>
<p>{name}</p>
この宣言を付けたcomponentを利用する親componentにも同様の宣言を付けておかないとエラーになるっぽい。
import
すればcustom elementとしてそのまま設置可能。
<svelte:options tag="my-parent" />
<script>
import './Child.svelte'
</script>
<my-child name="abc" />
デフォルト値なしのprops
だと、値を渡していてもなぜか~ was created without expected prop ~
という警告が表示される。
値の受け渡し自体は問題なく行われている。
<script>
export let name
</script>
それっぽいissueもあったが今のところ仕方ない感じか?
vite環境でlibとしてビルドするには設定が必要。
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
を吐き出す方法は見つけられず。
この記事にあるようなmissing-custom-element-compile-options
が警告される問題がまだ発生している。
探した限りでは根本的な対応方法はなさそう。
custom elementにするとキャメルなpropsに値を渡すことができないっぽい。
export let someData;
下記のどちらでも値を渡せていなかった。
<my-comp someData={1} />
<my-comp some-data={1} />
関連issue。
現時点では変換処理を自作しないと対応できなさそう。
Svelte + WebComponents を調べていて辿り着きました。詳細な情報をまとめて頂いていたおかげで助かりました。ありがとうございます。
ご存知かもしれませんが、ケバブケースでの props の取得は {$$props["header-text"]}
でできるようです。ご参考になれば幸いです。
こういう記法によって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
しても同様にマウントできる。
propsの挙動やらがどうしてもsvelteとcustom elementで仕様上の違いがあるからその差異を把握していないと思わぬ落とし穴にはまる。
結局のところcustom element作るなら素でclass書いたほうがいいのではないか。
とはいえcustom elementになるルートコンポーネントでだけ気をつければ、後はsvelteに乗れるメリットが大きいか?なんともいえない。
svelteコンポーネントをnew
でマウントする場合、そのコンポーネントのonMount
が実行されるときにbind:this
に指定した変数への代入がまだ行われていない。
await tick()
で待たないといけない。
孫コンポーネントでも同様の挙動になるとしたら非常に面倒。
↑はnew
を親コンポーネントのonMount
で行っている場合に起きるっぽい。
main.ts
でgetElementById
してnew
を書いた場合は起きない。
onMount
内でbind:this
された変数をtarget
にしてnew
するのはどうも挙動が怪しい。
relpで試してみたらなぜか無限ループっぽくなってしまった。
svelte内で使う場合は素直にtemplateとしてマウントしたほうがよさそう。