





























Okay, so in Astro you have two things:
.astro extension. They execute exclusively on the server or during the build. In other words, their code is never shipped to the client. So they can do things that client code cannot do—read from the filesystem, hit the internal services, even read from a database. But they can’t do interactive things aside from whatever exists natively in the HTML or your own <script>. Astro Components can render either other Astro Components or Client Islands.Here’s a PostPreview.astro Astro Component rendering a LikeButton Island:
---
import { readFile } from 'fs/promises';
import { LikeButton } from './LikeButton';
const { slug } = Astro.props;
const title = await readFile(`./posts/${slug}/title.txt`, 'utf8');
---
<article>
<h1>{title}</h1>
<LikeButton client:load />
</article>import { useState } from 'react';
export function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'} Like
</button>
);
}Notice how Astro Components and Client Islands essentially live in two different “worlds”, and the data only ever flows down. Astro Components are where all the preprocessing happens; they “hand off” the interactive bits to the Client Islands.
Now let’s look at React Server Components (RSC).
In RSC, the same two things are called Server Components and Client Components. Here is how you’d write the above Astro Component as a React Server Component:
import { readFile } from 'fs/promises';
import { LikeButton } from './LikeButton';
async function PostPreview({ slug }) {
const title = await readFile(`./posts/${slug}/title.txt`, 'utf8');
return (
<article>
<h1>{title}</h1>
<LikeButton />
</article>
);
}'use client';
import { useState } from 'react';
export function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'} Like
</button>
);
}The mental model behind these two are remarkably similar! If you know Astro, you already have 80% of the mental model for React Server Components. (Even if you think React Server Components are a terrible idea, Astro is worth learning.)
Let’s note a few syntactic differences you might have noticed above:
Astro.props, and there is no separate “template”..astro files. Once you import a Client Island, you’re not in an .astro file anymore and thus you’re “leaving” the Astro world. In RSC, the same purpose is achieved by the 'use client' directive. The 'use client' directive marks where the Server world “ends”—it is a door between the worlds.client:load that let you treat Islands either as static HTML or as hydratable on the client. React Server Components does not expose this distinction to the user code. From React’s perspective, if a component was written to be interactive, it would be a mistake to remove this interactivity. If a component truly does not require interactivity, just remove 'use client' from it, and then importing it from the Server world would already keep it Server-only.The last point is interesting. In Astro, the different syntax (.astro files vs Client Islands) creates a sharp and obvious visual distinction between the two worlds. The same component can’t act as both an Astro Component and a Client Island depending on the context—they’re two distinct things with distinct syntaxes.
But in RSC, the “Astro” part is also “just React”. So if you have a component that doesn’t use any client-specific or server-specific features, it could play either role.
Consider a <Markdown /> component that does its own parsing. Since it doesn’t use any client features (no State) or any server features (no reading DB), it could be imported on either side. So if you import it from a Server world, it’ll act like an “Astro Component”, but if you import it from a Client world, it’ll act like a “Client Island”. This isn’t some new concept, it’s just how importing functions works!
In RSC, stuff imported from the Server world will run in the Server world; stuff that’s imported from the Client world will run in the Client world; and stuff that’s not supported in either world (e.g. DB on the Client or useState on the Server) will cause a build error so you’ll be forced to “cut a door” with 'use client'.
This is both a blessing and a curse.
It is a curse because it makes learning to wield RSC rather unintuitive. You keep worrying about “which world you’re in”. It takes practice to embrace that it doesn’t matter because you can always reason locally. You can use server features like DB in files that need them, use client features like State in files that need them, and rely on build-time assertions causing errors if something is wrong. Then you look at the module stack trace and decide where to “cut a new door” for your “islands”.
This is a curse, but it is also a blessing. By embracing React on both sides, the RSC model solves some Astro limitations that you might encounter along the way:
'use client' and/or move it up or down the import chain with little friction. You do decide where to “cut the door”, but there’s no “converting” back and forth.In Astro, the fundamental output format is HTML. Since frontend frameworks don’t fundamentally operate the HTML itself (they operate a stateful DOM that can be initialized with HTML), Astro follows a “one-time handoff” model. This makes it arguably easier to learn but limits server features to what the “first render” (to HTML) needs and mostly leaves you on your own with the interactive bits. As you make more things interactive, you might feel yourself running into Astro model’s limitations, possibly choosing to move more logic to SPA-like but isolated Islands.
In RSC, the fundamental output format is a React tree (which can be turned to HTML, but can also be (re)fetched as JSON). Since RSC uses React on both sides with no visual distinction between the two worlds, it is more challenging to learn to wield it. The upside is that once you get the hang of moving the boundaries, they become very fluid and solve the problem where you have to move code “into Astro” or “back into Islands” because something ended up more static or dynamic than expected. You also can retain the same “just map the data to UI” mental model whether the UI is read-only or needs to refetch in response to mutations. The Server parts grow deeper into the tree—interleaved with their Client parts.
And because it’s React on both sides, all React features are integrated end-to-end: for example, a <Suspense> declarative loading state on the Client will “know” to wait for async data (from the Server), JS and CSS (as the Client loads them), fonts and images (with reasonable timeouts), and even trigger the View Transitions (see here). In React, every feature is designed so that the Server and Client pieces are arbitrarily nestable, composable, and refreshable in-place. It’s a single tree. The downside is that buying into RSC means buying into React. RSC is full-stack React.
Finally, it is worth noting that Astro is a framework, but RSC itself is lower-level—think of it as a building block for a framework, or a standard that a framework can implement. The two officially supported implementations of RSC right now include Next.js App Router (a framework) and Parcel RSC (not a framework).
Personally, I think that the developer experience with RSC is still somewhat raw, but I also think you might want to learn it anyway. It has some interesting ideas.
Also, if you’ve never used Astro, give it a try! If RSC is giving you a hard time, Astro might offer a gentler onramp to the same ideas. And if you’ve only ever used client-side React, Astro might solve some problems you never realized you had.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。