[2.0 rc.14] Server components: what's the intended setup for many calls of one server function on an SSR page (e.g. code highlighting in docs)?
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 12/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- typescript
Hướng nghiên cứu
The question is whether documentBoundary in @solidjs/web/frames should adopt later calls with different arguments, as described in frames-consistency-contract.md (C1 and pin c) and 11-server-components.md. Start with the claimedBoundaries check in documentBoundary and run the repro script to count /_server requests during hydration. Done means a decision on whether this is intended, and a fix or documented pattern that stops the extra requests, which needs maintainer input first.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Hi! This might not be a bug. It may well be me (and my coding agent, who did most of the digging) misreading how server components are meant to be used. If so, a pointer to the intended pattern would be great.
What we're doing
We render docs pages with many highlighted code blocks. Highlighting (Shiki) runs on the server through one server component, and each block calls it with its own source:
// render.tsx
import { GET } from '@solidjs/web/server-functions';
export const render = GET(async (text: string) => {
'use server';
return () => <p class="block">{text}</p>; // in the real app: Shiki's HTML
});
// App.tsx
import { dynamic } from '@solidjs/web';
import { Loading } from 'solid-js';
import { render } from './render.tsx';
function Block(props: { text: string }) {
const Content = dynamic(() => render(props.text));
return <Content />;
}
export default function App() {
return (
<Loading fallback="loading">
<Block text="one" />
<Block text="two" />
<Block text="three" />
</Loading>
);
}
// vite.config.ts
export default defineConfig({
// Without this, SSR fails with "Seroval caught an error during the parsing process" (cf. solid-vite-plugin#394).
ssr: { noExternal: ['seroval', 'seroval-plugins'] },
plugins: [solid({ ssr: true, start: true, serverFunctions: { components: true } })],
});
As far as I can tell this follows examples/start-ssr/src/frames/FramesApp.tsx, except that we call the same function from several places. We use GET(fn) because the design notes call it the idiom for server components; a plain module-level 'use server' function behaves the same.
What we see
All three blocks are in the SSR HTML, but hydration adopts only the first. The other two each request /_server/data/<id> and render again on the server. The final DOM is correct and nothing is logged.
| Case | Blocks in SSR HTML | Requests during hydration |
|---|---|---|
| rc.13, one call site | 1 | 0 |
| rc.13, three call sites | 3 | 2 |
| rc.14, three call sites | 3 | 2 |
rc.14, production build (node dist/server/node.js) |
3 | 2 |
rc.14, each block in its own <Loading> |
3 | 2 |
rc.14, module-level 'use server' (POST) instead of GET(fn) |
3 | 2 |
On a real docs page with ten blocks, that's nine extra highlighting requests on every load.
Why I think it might be intended
The three boundaries are stamped with the same function id:
<solid-frame data-fid="render-62d3f108" style="display:contents">
<solid-frame data-fid="render-62d3f108" style="display:contents">
<solid-frame data-fid="render-62d3f108" style="display:contents">
On the client, documentBoundary in @solidjs/web/frames adopts one element per id (claimedBoundaries.has(id)), and later mounts render fresh. frames-consistency-contract.md describes this under C1 ("client.ts:documentBoundary (a second mount goes fresh)"), and its pin (c) covers "a second mount of the same function while the first adopted mounts fresh".
But 11-server-components.md says "Boot makes zero server-function requests", and that identity is the call's (function, arguments) address, "so panes over different calls are independent". From those two docs I couldn't tell whether different arguments count as a "second mount of the same function". In #3603, the t = 0 case is also described as "covered by the document".
Questions
- Is fresh rendering for the second and later calls of one function the expected behavior, or should calls with different arguments be adopted too?
- If it's expected, what setup do you recommend for this case? Some options we considered:
- accept the extra requests
- return the HTML as data from a plain server function, which ships it twice (markup plus serialized data)
- batch all blocks on a page into one server component call
- something else we're missing
Repro
The four files above, plus a script that counts /_server requests while the page hydrates:
import { chromium } from 'playwright';
const url = process.env.URL ?? 'http://localhost:3300/';
const html = await (await fetch(url)).text();
console.log('blocks in SSR HTML:', (html.match(/class="block"/g) ?? []).length);
const browser = await chromium.launch();
const page = await browser.newPage();
const requests = [];
page.on('request', (r) => r.url().includes('/_server') && requests.push(r.url()));
await page.goto(url, { waitUntil: 'load' });
await page.waitForTimeout(2000);
console.log('blocks after hydration:', await page.locator('.block').allTextContents());
console.log('server-function requests during hydration:', requests.length, requests);
await browser.close();
blocks in SSR HTML: 3
blocks after hydration: [ 'one', 'two', 'three' ]
server-function requests during hydration: 2 [
'http://localhost:3300/_server/data/render-62d3f108?args=%5B%22two%22%5D',
'http://localhost:3300/_server/data/render-62d3f108?args=%5B%22three%22%5D'
]
Versions: solid-js / @solidjs/web / @solidjs/signals 2.0.0-rc.14 (also rc.13), @solidjs/vite-plugin 3.0.0-next.47, Vite 8.3.3, Node 24.20.0, macOS 26.2, Chrome for Testing 153.
Possibly related: #2973 (adopted frames keyed by call address), #3889 (two mounts of one server-component result), discussion #3603 (request batching; server-component request grouping is on hold there because t = 0 is covered by the document).
Thanks!
- Ngôn ngữ chính
- TypeScript
- Star
- 36.1k
- Fork
- 1.1k
- Merge trung bình
- 11 giờ 32 phút
- Pull request đã merge (30 ngày)
- 356
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của solidjs/solid
-
[2.0 / next] Error formatting and async iterator protocol failures escape memo error handlingĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 22/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 14/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 18/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[2.0 / next] Outside latest reads of a derivation expose errors and values at different timesĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 22/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của solidjs/solid
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
apache/rocketmq-dashboard#6872 ·
Maintainer thường phản hồi trong vòng 3 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
fab-geocommuns/RNB-site#539 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Guest multiview tile label leaks raw virtual-source-id "Whip" instead of the guest name / slot labelĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Eyevinn/open-live-studio#211 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
documentation good first issue
Độ khó 2/5 Nửa ngày Mức phù hợp với người mới 72/100
sujithputta02/Esapay#13 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
ehmpathy/rhachet-roles-bhuild#468 ·
Maintainer thường phản hồi trong vòng 1 ngày