🧊

Astroで条件付きレンダリングしてるのに`<script>`が出力されてしまう

どういうことかというと、

  • 特定のpages/[slug].astroの中で
  • getStaticPaths()から複数のページを返しつつ
  • とあるslugの場合のみ、<script>を使ったコンポーネントを利用してる
  • はずなのに、すべてのslugのHTMLで<script>が出力されてしまう

というケース。

問題のコード

簡略化するとこういう感じ。

---
import V1OnlyScript from "../components/v1-only-script.astro";

export const getStaticPaths = () => ([
  { params: { slug: "v1-1", } }
  // ...
  { params: { slug: "v2-1", } }
  { params: { slug: "v2-2", } }
  // ...
]);

const { params: { slug } } = Astro;
---

{slug.startsWith("v1") && <V1OnlyScript />}
{slug.startsWith("v2") && <p>0JS</p>}

3つのHTMLが出力されるけど、slug: v1-*のページでだけ、<script>が出力されてほしい。

が、実際には、slug: v2-*のページでも、<script>が出力されてしまう。

バグではなく仕様

直感的じゃないし、バグでは?って最初は思ったけど、どうやら仕様らしい。

https://docs.astro.build/en/guides/troubleshooting/#an-unexpected-script-or-style-is-included https://docs.astro.build/en/guides/client-side-scripts/#script-bundling

.astroファイルにおける<script>は、処理されるルールが決まってて、そのせいでこうなる。

ワークアラウンド

いくつか考えてみたが、どれもすっきりしない。

is:inlineにする案

その名の通り、インライン化してしまう。

そうすれば、それぞれのページで必要なところだけで出力される。

が、もちろん、そのアセットはサイト単位で見た場合に重複することになる。

publicディレクトリなんかに逃がしてsrcで参照するようにすれば、この問題は解決できるけど、コロケーションできない不都合は残る。

異なる.astroファイルに分ける案

[slug].astroを、[slugV1].astro[slugV2].astroに分けるってこと。

---
import V1OnlyScript from "../components/v1-only-script.astro";

export const getStaticPaths = () => ([
  { params: { slugV1: "v1-1", } }
]);

const { params: { slugV1 } } = Astro;
---

<V1OnlyScript />

---
export const getStaticPaths = () => ([
  { params: { slugV2: "v2-1", } }
  { params: { slugV2: "v2-2", } }
]);

const { params: { slugV2 } } = Astro;
---

<p>0JS</p>

に分けてしまうと、想定通りの出力にはなる。

が、共通部のコードをコンポーネントに切り出すなどの手間は必要。

今回はこの方法を採用したけど、もっといいやり方はないもんだろうか。