pluginIsland
ページの一部をReact App化するプラグイン。AstroのIslandを再現しています。
Table of Contents
How To Use
動作させるには以下パッケージのいずれが必要です。
@vitejs/plugin-react@vitejs/plugin-react-swc
これによってIslandのJavaScriptにReactのimportが自動挿入され、同時に開発モードでIsland内のHMRが効くようになります。
$ npm install --save-dev @vitejs/plugin-reactimport { pluginIsland } from "minista"
import react from "@vitejs/plugin-react"
export default {
plugins: [pluginIsland(), react()],
}import { Counter } from "../components/counter"
export default function () {
return <Counter client:load />
}Options
pluginIsland({
useSplitPages: true,
outName: "island-[index]",
rootAttrName: "island",
rootDOMElement: "div",
rootStyle: { display: "contents" },
})useSplitPages
- 型:
boolean - デフォルト:
true
ページ毎に必要なJavaScriptを分割して読み込みます。
outName
- 型:
string - デフォルト:
"island-[index]"
出力ファイル名。拡張子は含みません。以下の動的出力タグを使用できます。
[index]:生成された順番(開始: 1)
rootAttrName
- 型:
string - デフォルト:
"island"
ハイドレーションを行うルート要素のデータ属性名に使われる名前。
- 例:
data-island-client-directive
rootDOMElement
- 型:
"div" | "span" - デフォルト:
"div"
ハイドレーションを行うルート要素のHTMLタグ。
rootStyle
- 型:
React.CSSProperties - デフォルト:
{ display: "contents" }
ハイドレーションを行うルート要素に付与するスタイル。
Client Island
ministaはReactのJSXテンプレートを 静的なHTMLに変換し、クライアントサイドのJavaScriptを取り除きます。
import { Counter } from "../components/counter"
export default function () {
return <Counter />
}しかし、UIコンポーネントをインタラクティブにしたいシーンもあります。その場合は client:* ディレクティブを付与してください。ministaはその部分だけのJavaScriptを生成してハイドレーションします。client:* ディレクティブの種類と動作は次項を参照ください。
import { Counter } from "../components/counter"
export default function () {
return <Counter client:load />
}※ハイドレーションを行うコンポーネン配下で読み込んだ画像パスはルートパス・または絶対パスとなり、埋め込み用の相対パスは反映されません
Client Directive
静的なHTMLに変換されるJSXの一部をReact Appとして動作させる効果があります。client:* ディレクティブ配下ではReactの useState useEffect などインタラクティブな実装がすべて使えます。client:* ディレクティブはReact ComponentsまたはHTMLタグに付与でき、同時にpropsを渡すことも可能です。
client:load
ページの読み込みと同時に、コンポーネントをハイドレーションします。
import { Counter } from "../components/counter"
export default function () {
return <Counter client:load />
}client:idle
ページの初期読み込みが終わり、ブラウザが待機状態になったときにコンポーネントをハイドレーションします。
import { Counter } from "../components/counter"
export default function () {
return <Counter client:idle />
}timeout:初期読み込みがこの時間に到達した場合にハイドレーションを優先して実行- 実装:
requestIdleCallbackのtimeout
- 実装:
import { Counter } from "../components/counter"
export default function () {
return <Counter client:idle={{ timeout: 5000 }} />
}client:visible
コンポーネントが画面内に表示されたときにハイドレーションします。
import { Counter } from "../components/counter"
export default function () {
return <Counter client:visible />
}rootMargin:コンポーネントの認識範囲を拡大- 実装:
IntersectionObserverのrootMargin
- 実装:
import { Counter } from "../components/counter"
export default function () {
return <Counter client:visible={{ rootMargin: "200px" }} />
}client:media
CSSメディアクエリの条件が満たされたときに、コンポーネントをハイドレーションします。
import { Counter } from "../components/counter"
export default function () {
return <Counter client:media="(max-width: 500px)" />
}client:only
静的HTMLを出力せずに、ReactDOM.createRoot でコンポーネントをレンダリングします。タイミングは client:load と同じくページの読み込みと同時。静的なHTMLを初期表示させたくない場合に有効ですが、レンダリング前の高さを確保できないためCumulative Layout Shift(CLS)による画面のガタつきに注意が必要です。
import { Counter } from "../components/counter"
export default function () {
return <Counter client:only />
}slot="fallback":直下要素に付与することでレンダリング前に表示
import { Counter } from "../components/counter"
export default function () {
return (
<div client:only>
<div slot="fallback">Loading...</div>
<Counter />
</div>
)
}