网站首页 > 技术文章 正文
大家好,很高兴又见面了,我是"高级前端?进阶?",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。
今天给大家带来的主题是使用 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 的安装过程
- 第一个 Bun 项目
- ? 创建第一个 Bun 服务器
- 使用 Bun 流和 React 进行服务器端渲染
- 获取第三方数据并渲染服务器端
项目设置
安装 Bun
可以将 Bun 与 Nodejs 一起安装从而不影响其他存储库。
// Install Bun
curl -fsSL https://bun.sh/install | bash
初始化 Bun 项目
接下来可以初始化一个新的 Bun 项目。
// Project setup
mkdir bun-httpserver
cd bun-httpserver
bun init
使用 bun init 将构建一个新项目,生成一个文件 bun.lockb,它取代了 yarn、npm 或 pnpm 锁定文件。 此外,index.ts 和 tsconfig.json 都是默认生成的,这意味着 TypeScript 支持已内置,无需额外设置。
第一个 Bun 服务器
设置 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} ...`);
使用 React 和 Bun 实现服务器端渲染 (SSR)
以 Bun 方式添加包
要在 Bun 中添加包只需使用 bun add 命令即可, 如果想要将其作为开发依赖项只需添加 -d 标志即可,这一点于 Node.js 极其相似。
bun add react react-dom
bun add @types/react-dom -d
// 添加react等相关包,用于服务端渲染
切换到 JSX
为了使用JSX的能力,下面命令将现有的 index.ts 服务器文件转换为 index.tsx,从而允许开发者直接返回 JSX 元素。
mv index.ts index.tsx
分析 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 />);
// 将 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 <div>Bun Forrest, Bun!</div>;
}
export default Pokemon;
启动 Bun 服务
bun index.tsx
// bun --watch index.tsx
// 也可以使用--watch标志位启用HMR热更新
浏览器打开地址 http://localhost:3000 将会看到 SSR 后的 Pokemon 组件!
利用 Pokémon Twist 构建动态路由
接下来将创建两条不同的路由,即/pokemon 和 /pokemon/[pokemonName]。
- 打开 /pokemon 将触发对 Pokémon API 的 fetch 请求,将结果渲染为可点击的锚标记列表。
- 点击锚点将导航到 /pokemon/[pokemonName],其中会获取特定的 Pokémon、进行服务器端渲染 (SSR),然后进行流式传输。
增强型 index.tsx
到这一步,当前应用将包含动态路由,可以显示从 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(<PokemonList pokemon={results} />);
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(<Pokemon name={name} height={height} weight={weight} img={front_default} />);
// renderToReadableStream 函数将 React 组件转换为可读流,然后作为 HTML 响应返回。
return new Response(stream, {
headers: { "Content-Type": "text/html" },
});
}
// 404状态码处理
return new Response("Not Found", { status: 404 });
},
});
console.log("Listening ...");
PokemonList 组件
PokemonList 组件获取 Pokemon 列表并将它们转换为可点击的列表项。每个列表项都是一个锚标记,单击时将用户路由到 /pokemon/[name],渲染各个 Pokémon 详细信息。
import React from 'react';
function PokemonList({
pokemon,
}: {
pokemon: { name: string, url: string }[],
}) {
return (
<ul>
{pokemon.map(({ name }) => (
<li key={name}>
<a href={`/pokemon/${name}`}>{name}</a>
</li>
))}
</ul>
);
}
export default PokemonList;
Pokemon 组件
Pokemon 组件负责获取单个 Pokemon 的高度、重量、名称和图像 URL,并准确返回显示单个 Pokemon 的方式。
import React from 'react';
function Pokemon({
height,
weight,
name,
img,
}: {
height: number,
weight: number,
name: string,
img: string,
}) {
return (
<div>
<h1>{name}</h1>
<img src={img} alt={name} />
<p>Height: {height}</p>
<p>Weight: {weight}</p>
</div>
);
}
export default Pokemon;
参考资料
https://alexkates.dev/server-side-rendering-ssr-with-bun-and-react
猜你喜欢
- 2024-10-06 使用 Nuxt.js实现SSR服务器端渲染之@nuxtjs/axios的使用
- 2024-10-06 【前端开发】前端30年的发展历程及影响
- 2024-10-06 深入浅出通过vue-cli3构建一个SSR应用程序【实践】
- 2024-10-06 进阶成高级前端的四个方法 高级前端是什么
- 2024-10-06 [Day 01] 浅谈 CSR,SSR 与 SSG ssr和ssg
- 2024-10-06 「长文慎」一文吃透React SSR服务端同构渲染
- 2024-10-06 使用 Nuxt.js实现SSR服务器端渲染之nuxt.config实战配置详解
- 2024-10-06 SSR 它到底香不香?细数 SSR 的利与弊
- 2024-10-06 使用 Nuxt.js实现SSR服务器端渲染之页面渲染流程详解
- 2024-10-06 SSR 的升级版:流式服务端渲染原理!
你 发表评论:
欢迎- 501℃几个Oracle空值处理函数 oracle处理null值的函数
- 499℃Oracle分析函数之Lag和Lead()使用
- 495℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 484℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 478℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 474℃【数据统计分析】详解Oracle分组函数之CUBE
- 455℃Oracle有哪些常见的函数? oracle中常用的函数
- 452℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端富文本编辑器 (47)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)