大家好,很高兴又见面了,我是"高级前端 进阶 ",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。
今天给大家带来的主题是使用 Bun 和 React 实现 SSR 服务端渲染,文章大部分内容来自于Alex Kates发布的 《Server-Side Rendering (SSR) with Bun and React》,但是对部分内容进行了修改,欢迎大家指正。
关于Bun的更多内容可以在我的主页查看,后续也会持续关注Bun等各种运行时的最新动态。话不多说,直接进入正题!
2023 年 9 月 8 日,期待已久的 JavaScript 运行时 Bun 终于发布了 1.0 版本,Bun 可以作为一体化 JavaScript 运行时和工具包,专为运行速度而设计。 它配备了打包器、测试运行器、本机 TypeScript 和 JSX 支持,甚至还有 Node.js 兼容的包管理器。
本文将从 Bun 开发服务端渲染的简单示例展开,主要包括以下几个方面:
可以将 Bun 与 Nodejs 一起安装从而不影响其他存储库。
// Install Bun
curl -fsSL https://bun.sh/install | bash
接下来可以初始化一个新的 Bun 项目。
// Project setup
mkdir bun-httpserver
cd bun-httpserver
bun init
使用 bun init 将构建一个新项目,生成一个文件 bun.lockb,它取代了 yarn、npm 或 pnpm 锁定文件。 此外,index.ts 和 tsconfig.json 都是默认生成的,这意味着 TypeScript 支持已内置,无需额外设置。
设置 Bun 服务器非常简单,只需几行代码即可启动并运行。
const server = Bun.serve({
// Bun.serve() 初始化服务器并将其设置为侦听端口 3000
port: 3000,
// 定义一个处理所有传入 HTTP 请求的函数。 当请求到来时,它会返回一个新的 HTTP 响应,
// 其中包含文本“Bun!”
fetch(req) {
return new Response(`Bun!`);
},
});
console.log(`Listening on http://localhost:${server.port} ...`);
要在 Bun 中添加包只需使用 bun add 命令即可, 如果想要将其作为开发依赖项只需添加 -d 标志即可,这一点于 Node.js 极其相似。
bun add react react-dom
bun add @types/react-dom -d
// 添加react等相关包,用于服务端渲染
为了使用JSX的能力,下面命令将现有的 index.ts 服务器文件转换为 index.tsx,从而允许开发者直接返回 JSX 元素。
mv index.ts index.tsx
修改后的 index.tsx 使用 react-dom/server 中的 renderToReadableStream 来渲染 Pokemon 组件。然后,将此Stream流包装在 Response 对象中,同时需要确保内容类型设置为“text/html”。
import { renderToReadableStream } from 'react-dom/server';
// 从react-dom/server包中导入函数renderToReadableStream用于服务器端React渲染。
import Pokemon from './components/Pokemon';
// 从相对文件路径导入名为 Pokemon 的 React 组件。
Bun.serve({
// 使用 Bun.serve() 方法设置 HTTP 服务器。 它包括一个异步获取函数来处理传入的 HTTP 请求。
async fetch(request) {
// 一个异步函数,每个到达服务器的 HTTP 请求都会触发该函数。
const stream = await renderToReadableStream( );
// 将 Pokemon React 组件异步渲染到可读流。
return new Response(stream, {
headers: { 'Content-Type': 'text/html' },
});
// 返回具有可读流的新 HTTP 响应对象,并将“Content-Type”标头设置为“text/html”。
},
});
console.log('Listening ...');
这一步将构建一个简单的 React 组件,该组件将在服务器端渲染(SSR)并直接流回客户端。
// Pokemon组件
import React from 'react';
type PokemonProps = {
name?: string,
};
function Pokemon() {
return Bun Forrest, Bun!;
}
export default Pokemon;
bun index.tsx
// bun --watch index.tsx
// 也可以使用--watch标志位启用HMR热更新
浏览器打开地址 http://localhost:3000 将会看到 SSR 后的 Pokemon 组件!
接下来将创建两条不同的路由,即/pokemon 和 /pokemon/[pokemonName]。
到这一步,当前应用将包含动态路由,可以显示从 Pokémon API 获取的 Pokémon 列表,也可以根据 URL 显示特定的 Pokémon。 两个路由的组件都会在服务器端渲染,然后流回客户端。
import { PokemonResponse } from "./types/PokemonResponse";
import { PokemonsResponse } from "./types/PokemonsResponse";
import { renderToReadableStream } from "react-dom/server";
import Pokemon from "./components/Pokemon";
import PokemonList from "./components/PokemonList";
// 设置 HTTP 服务器并指定异步获取函数来处理传入请求,有效地充当所有 HTTP 流量的入口点。
Bun.serve({
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/pokemon") {
const response = await fetch("https://pokeapi.co/api/v2/pokemon");
const { results } = (await response.json()) as PokemonsResponse;
const stream = await renderToReadableStream( );
return new Response(stream, {
headers: { "Content-Type": "text/html" },
});
}
const pokemonNameRegex = /^/pokemon/([a-zA-Z0-9_-]+)$/;
const match = url.pathname.match(pokemonNameRegex);
// 使用正则表达式匹配指定特定 Pokémon 名称的 URL 路径(例如 /pokemon/pikachu)。
// 如果检测到这样的路径,服务器会获取该特定 Pokémon 的详细信息并使用 Pokemon React 组件进行渲染。
if (match) {
const pokemonName = match[1];
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonName}`);
if (response.status === 404) {
return new Response("Not Found", { status: 404 });
}
const {
height,
name,
weight,
sprites: { front_default },
} = (await response.json()) as PokemonResponse;
const stream = await renderToReadableStream( );
// renderToReadableStream 函数将 React 组件转换为可读流,然后作为 HTML 响应返回。
return new Response(stream, {
headers: { "Content-Type": "text/html" },
});
}
// 404状态码处理
return new Response("Not Found", { status: 404 });
},
});
console.log("Listening ...");
PokemonList 组件获取 Pokemon 列表并将它们转换为可点击的列表项。每个列表项都是一个锚标记,单击时将用户路由到 /pokemon/[name],渲染各个 Pokémon 详细信息。
import React from 'react';
function PokemonList({
pokemon,
}: {
pokemon: { name: string, url: string }[],
}) {
return (
{pokemon.map(({ name }) => (
-
{name}
))}
);
}
export default PokemonList;
Pokemon 组件负责获取单个 Pokemon 的高度、重量、名称和图像 URL,并准确返回显示单个 Pokemon 的方式。
import React from 'react';
function Pokemon({
height,
weight,
name,
img,
}: {
height: number,
weight: number,
name: string,
img: string,
}) {
return (
{name}
Height: {height}
Weight: {weight}
);
}
export default Pokemon;
https://alexkates.dev/server-side-rendering-ssr-with-bun-and-react
页面更新:2024-03-07
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号