Arkhi 框架的核心基於島嶼架構(Islnads Architecture)建立。在需要獲得互動性的元件上,我們需要主動標記其為島嶼。此類元件在前端獨立被 Hydrate,而同頁面中未標記的部份則保持靜態。
import "arkhi/client";
const IslandComponent = Island(Component);
import React, { useState } from "react"
import { Island } from "vite/client";
import type { PropsWithChildren } from "vite/client";
// 元件:客戶端不具備互動性
function Counter({ ..props }: PropsWithChildren) {
const [count, setCount] = useState(0);
const increment = () => setCount((count) => count + 1)
return (
<button onClick={increment} {...props}>
counter {count}
</button>
);
}
// 島嶼元件:客戶端具備互動性
const CounterIsland = Island(Counter);
Note
{...props}
負責執行 SSR 時將每個 island 的獨特 id 填入,這對於作為 island 被使用的元件是必要的。如果將任何元件島嶼化時遇到問題,建議優先檢查這個部份。
因為需要在前端重新渲染島嶼,所有的 Props 都需要是可以被序列化的內容。為了增加可以被序列化的資料種類,我們使用了 superjson (詳細可使用的資料類別請參見 superjson 說明)。
// ❌ 錯誤範例 ❌
import type { PropsWithChildren } from "react";
export default function Component({ children }:PropsWithChildren) {
return (
<div className="w-10 bg-black flex">
{ children }
</div>
)
}
以上範例中我們在傳入的 Children 外包上一層 div
後加上了一些 styling,雖然這是相當常見的 Pattern,但這在我們的環境中會遇上一些問題。
島嶼在前端渲染時仰賴將事先序列化的資料作為他的 props,這代表無法被序列化的資料(像是複雜的元件)就會遇上渲染錯誤。
Note
superjson 所支援的資料型別
就像大海中一個小島,外部能提供的資源補給有限,需要自給自足。若是島嶼有需要外部的其他元件作為其 children,可以改為透過 props 選用預先設定好的元件。
import type { PropsWithChildren } from "react";
import { Foo, Bar } from "./Components.tsx";
const options = {
foo: <Foo />,
bar: <Bar />,
} as const;
export default function Component({ child, ...props }: { child: keyof typeof options }) {
return (
<div className="w-10 bg-black flex">
{ options[child] }
</div>
)
}
Pitfull
為了更好的發揮島嶼的特性,請參閱 管理島嶼。
在大部份的框架中,會在 HTML body 中放置一個 <div id="root"></div>
,於客戶端使用 React-DOM 對此 Element 進行 Hydrate。
在島嶼架構中,我們只對於需要互動性的區塊進行 Hydrate,形成一個一個具備互動性的區塊、一個個「島嶼」。