Arkhi 採取全面的 Client Routing 來進行同源的頁面切換,因此在切換時並不會重新載入頁面,以此提升使用者的切換體驗。
主要運作流程由下面的內容更新函數 updatePageContext
以及 Link
元件實現。
在 Arkhi 搜尋島嶼並重建時,會將原有的 Anchor <a>
標籤 用 <Link>
進行替換,以覆蓋掉原始 <a>
所包含的切換頁面行為。 Link
被點擊時將會透過 go
函數來調用 updatePageContext
,並根據傳遞的 path 或 url 透過 fetch 從 Server 端取得渲染完成的對應頁面內容。 並在解析過後將其與現有顯示頁面的 body 內容進行替換。
private async updatePageContext({
href,
mode,
data,
}: {
href: string;
mode: string;
data?: {};
}): Promise<boolean> {
const path = this.getPath(href);
try {
const response = await fetch(path);
const htmlString = await response.text();
if (mode === "go") {
window.history.pushState(
{ prev: this.getPath(window.location.href) },
"",
path
);
}
const parser = new DOMParser();
const html = parser.parseFromString(htmlString, "text/html");
const PrefetchSettingJson =
html
.getElementById("prefetch-setting")
?.getAttribute("data-setting") || "";
var g = document.createElement("script");
var s = document.getElementsByTagName("script")[0];
g.text =
html.body.getElementsByTagName("script")[0].textContent || "";
s.parentNode?.insertBefore(g, s);
const PrefetchSetting = JSON.parse(PrefetchSettingJson);
PrefetchSetting && this.setPagePrefetchRule(path, PrefetchSetting);
document.body = html.body;
this.render();
return true;
} catch (error: any) {
console.error("Fetch Error:", error.message);
return false;
}
}
...
...
const Link_ = ({
children,
className,
href,
...props
}: PropsWithChildren & { className?: string; href: string }) => {
const clientRouter =
typeof window === "undefined" ? null : window.clientRouter;
const pageSetting =
clientRouter?.pageSettingMap.get(
clientRouter.getPath(window.location.href)
) || clientRouter?.setting;
const isSettingHover = pageSetting?.mode === "hover";
const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
const target = event.currentTarget;
clientRouter && clientRouter.go(target.getAttribute("href") || "");
};
const handleMouseOver = (event: MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
const target = event.currentTarget;
clientRouter && clientRouter.prefetchHover(target);
};
return (
<a
onClick={clientRouter ? handleClick : undefined}
onMouseOver={isSettingHover ? handleMouseOver : undefined}
className={className ?? ""}
href={href}
data-link
{...props}
>
{children}
</a>
);
};
ClientRouter
對外主要提供 go
, back
, forward
, <Link>
等功能,而 ClientRouter
的實體只應該在 your_project\renderer\_default.page.client.tsx
中被建立。
go()
跳轉到輸入的路徑
public go(path: string): Promise<boolean> {
path = this.getPath(path);
return this.updatePageContext({ href: path, mode: "go" });
}
//example
if(....){
window.clientRouter.go('/path_you_want_to_go');
}
back()
跳轉回到上一頁面
public back(): void {
window.history.back();
}
//example
if(....){
window.clientRouter.back();
}
forward()
跳轉前往下一個頁面
public forward(): void {
window.history.();
}
//example
if(....){
window.clientRouter.forward();
}
<Link>
雖然 Arkhi 搜尋島嶼並重建時,會主動將原有的 Anchor <a>
標籤 用 <Link>
進行替換,但 <Link>
也可以直接匯入後使用。
import { Link } from "#/arkhi/client";
export { Page };
function Page() {
return (
<>
<Link href="/link_you_want_to_go">Back To Home</Link>
</>
);
}