You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Preact Island组件为何无法读取服务端Context值?

问题:Preact Context在Islands组件中无法获取服务端Provider的值

我在routes/index.tsx中编写了服务端组件代码:

import GrandChildComponent from "../components/GrandChildComponent.tsx";
import { Username } from "./context.ts";

export default function App() {
    return (
        <Username.Provider value="Bob">
            <GrandChildComponent />
        </Username.Provider>
    )
}

./context.ts的Context定义:

import { createContext } from 'preact'
export const Username = createContext('alice')

当GrandChildComponent.tsx放在components目录(服务端组件)时,不管用Consumer还是useContext都能正常显示Bob:

// Consumer写法
import { Username } from "../context.ts";

export default function GrandChildComponent(){
    return (
        <Username.Consumer>
            {username => (
              <span>{username}</span>
            )}
        </Username.Consumer>
    )  
} 
// useContext写法
import { useContext } from "preact/hooks";
import { Username } from "../context.ts";

export default function GrandChildComponent(){
  const username = useContext(Username)
  return <span>{username}</span>
} 

但把这个组件移到islands文件夹(客户端组件)后,显示的就变成了默认值alice,两种写法都失效了。我需要从服务端读取配置传递给客户端组件处理,请问这是为什么?


原因分析

核心问题在于Islands组件的渲染机制:

  • components目录下的组件是服务端渲染的,完全处于服务端Provider的上下文树中,能正常读取Context值。
  • islands目录下的组件是客户端独立挂载的——服务端只会渲染它的占位符,客户端JS加载完成后,会在浏览器中单独把这个组件挂载到DOM里,此时它并不在服务端渲染时的Provider上下文树中,自然拿不到Provider传递的Bob,只能用Context的默认值。

解决方案

根据你的需求(服务端读取配置传给客户端组件),推荐以下几种做法:

1. 直接通过Props传递值

这是最简单直接的方式:服务端组件在渲染Islands组件时,把Context的值作为props传进去。

修改routes/index.tsx:

import GrandChildComponent from "../islands/GrandChildComponent.tsx"; // 路径改成islands
import { Username } from "./context.ts";

export default function App() {
    const usernameValue = "Bob"; // 这里可以替换成从服务端配置读取的值
    return (
        <Username.Provider value={usernameValue}>
            <GrandChildComponent username={usernameValue} />
        </Username.Provider>
    )
}

然后修改Islands组件:

export default function GrandChildComponent({ username }: { username: string }){
  return <span>{username}</span>
} 

2. 将服务端配置序列化后注入客户端

如果需要在多个客户端组件中共享服务端配置,可以在服务端渲染页面时,把配置序列化成全局变量,客户端组件直接读取这个变量。

比如在routes/index.tsx中添加脚本注入:

import { Head } from "$fresh/runtime.ts";
import GrandChildComponent from "../islands/GrandChildComponent.tsx";

export default function App() {
    const serverConfig = { username: "Bob" }; // 从服务端配置读取
    return (
        <>
            <Head>
                <script>
                    window.__SERVER_CONFIG__ = ${JSON.stringify(serverConfig)};
                </script>
            </Head>
            <GrandChildComponent />
        </>
    )
}

然后在Islands组件中读取:

export default function GrandChildComponent(){
  const username = (window as any).__SERVER_CONFIG__.username;
  return <span>{username}</span>
} 

3. 客户端层面重新创建Context并注入值

如果需要在客户端组件树中共享这个值,可以在客户端根组件中创建Context,把从服务端拿到的值作为Provider的value,然后其他客户端组件从这个客户端Context读取。

比如创建islands/ClientContext.tsx:

import { createContext, useContext } from "preact";

export const ClientUsername = createContext("");

export function ClientUsernameProvider({ children, value }: { children: any, value: string }) {
  return <ClientUsername.Provider value={value}>{children}</ClientUsername.Provider>;
}

export function useClientUsername() {
  return useContext(ClientUsername);
}

然后在routes/index.tsx中用这个客户端Provider包裹Islands组件:

import GrandChildComponent from "../islands/GrandChildComponent.tsx";
import { ClientUsernameProvider } from "../islands/ClientContext.tsx";

export default function App() {
    const usernameValue = "Bob"; // 服务端配置的值
    return (
        <ClientUsernameProvider value={usernameValue}>
            <GrandChildComponent />
        </ClientUsernameProvider>
    )
}

最后在Islands组件中使用:

import { useClientUsername } from "./ClientContext.tsx";

export default function GrandChildComponent(){
  const username = useClientUsername();
  return <span>{username}</span>
} 

内容的提问来源于stack exchange,提问作者Ooker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:05:17