SSR: non-ASCII characters in static template text make the whole rendered page a two-byte string
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ó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 42/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- babel, javascript, rust, typescript
- Lĩnh vực
- backend, performance
Hướng nghiên cứu
Start by reading the Rust SSR template compiler pass described in the spike, then trace the Babel plugin’s template handling and the ssrElement path for spread attributes. Update both compiler paths to emit character references only in static text and quoted attribute values, preserving tag internals, comments, and script/style bodies. Run the compiler fixture tests and check that rendered HTML decodes correctly; the issue reports 54 expected-output fixture failures and an uncovered spread-attribute path.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
What happens
A single non-ASCII character in static JSX text, such as the → in <a>View →</a>, makes the whole server-rendered page a two-byte string in V8. V8 stores a string at one byte per character only while every character is Latin-1, so once the → is concatenated in, the flattened page becomes UTF-16. That roughly doubles the cost of flattening the rendered rope and makes the TextEncoder.encode call in the response stream several times slower.
I found this while comparing Solid 2.0 (rc.19, @solidjs/vite-plugin next.51, router next.42) with SolidStart on the e18e framework-tracker benchmark app. Its /server-side-rendered page is a 1000-row table where each row ends in <a href={...}>View →</a>. The rendered page is about 300 KB.
On that page, TextEncoder.encode on the response takes about 1.3 ms of the roughly 3.4 ms of CPU per request: about 0.5 ms for V8 to flatten the rope and about 0.7 ms for the UTF-16 to UTF-8 conversion. The same encode on an ASCII-only version of the page is much cheaper. On a flat 170 KB string, TextEncoder.encode takes 0.06 ms when it is all ASCII and 0.35 ms when it contains one →.
How to reproduce
The effect is visible without Solid:
const te = new TextEncoder()
const page = (arrow) =>
Array.from({ length: 1000 }, () =>
`<tr><td>${crypto.randomUUID()}</td><td><a href="/x/${crypto.randomUUID()}">View ${arrow ? '→' : '->'}</a></td></tr>`
).join('')
for (const arrow of [false, true]) {
const strings = Array.from({ length: 200 }, () => page(arrow))
const start = performance.now()
for (const s of strings) te.encode(s)
console.log({ arrow, msPerEncode: (performance.now() - start) / 200 })
}
In a Solid app, render any component whose static template text contains a non-ASCII character next to a large amount of other markup, then profile renderToStream. Most of the encode time lands in encodeUtf8String.
Proposal
When the compiler builds SSR template strings, it can emit non-ASCII characters in static text and quoted attribute values as numeric character references (→ becomes →). Browsers decode these to the same characters, so the DOM, hydration and the client templates are unchanged. The work happens once, at build time. Tag internals, comments and <script>/<style> bodies have to keep their characters, because browsers do not decode references there.
This only helps when the non-ASCII characters come from templates. Non-ASCII in rendered data, such as a user's name, still makes the page two-byte.
Spike results
I tried this in the Rust compiler as a pass applied to each registered SSR template, with a small state machine for text, tags, quoted attributes, comments and raw-text elements. I then rebuilt the framework-tracker app with it:
/server-side-rendered, 1000 rows |
Current compiler | With character references |
|---|---|---|
| CPU per request, median of 6 runs | 3.40 ms | 3.00 ms |
| HTTP req/s, autocannon, 10 connections | 393 | 512 |
| p99 latency | 33 ms | 27 ms |
The page without the arrow (/ssr-throughput) was unchanged at 2.13 ms. The rendered HTML was identical apart from the character references, the client bundle was byte-identical, and the page hydrated and navigated client-side in Chrome with no console output.
Two things are still open in the spike. The Babel plugin needs the same change: 54 compiler fixture tests currently fail, every diff being only a character reference, because their expected output comes from Babel. Attribute strings built for spread elements (ssrElement) take a different path and are not covered yet.
I can open a PR with the compiler change if this direction is wanted.
- Ngôn ngữ chính
- TypeScript
- Star
- 36.2k
- Fork
- 1.1k
- Merge trung bình
- 10 giờ 28 phút
- Pull request đã merge (30 ngày)
- 379
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
-
`lazy()`: remounting an already-loaded component renders empty (or the Suspense fallback) since 1.9.14Có thể đã có người làm @itisrmk đã nhận 11 ngày trước. Đang mở1.x
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/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 51/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 45/100
solidjs/solid#4398 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Hydration runs an async memo over latest(...) of a hydrated chain with undefined and re-asksCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[2.0 / next] A memo reading latest(a) beside b() computes a torn pass on a batched write; if that pass throws, the error sticksCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 4/5 1-2 ngày Mức phù hợp với người mới 48/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 78/100
gnosis/gnosis_vpn#661 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
content-freshness: staleOwnedContent doc comments still say 'complete receipt' (follow-up to #7912)Đang mởtriage:bug-verified
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 92/100
Maintainer thường phản hồi trong vòng 1 ngày
-
fix: reject sourceStorage/packagedStorage bucket that differs from the stack default (fail loud)Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Eyevinn/open-videocore#1300 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
nextjs-tracking
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 67/100
cloudflare/vinext#4390 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
kind/bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
kubernetes-sigs/headlamp#8175 ·
Maintainer thường phản hồi trong vòng 2 ngày