使用 create-vite 构建 React 应用
React 是最广泛使用的 JavaScript 前端库。
在本教程中,我们将使用 Deno 构建一个简单的 React 应用。该应用将显示一个恐龙列表。当您点击其中一个时,它将带您进入一个包含更多详细信息的恐龙页面。您可以在 GitHub 上查看完成的应用仓库
使用 Vite 和 Deno 创建 React 应用 跳转到标题
本教程将使用 create-vite 快速搭建 Deno 和 React 应用。Vite 是一个用于现代 Web 项目的构建工具和开发服务器。它与 React 和 Deno 搭配使用效果良好,利用 ES 模块并允许您直接导入 React 组件。
在您的终端中运行以下命令,使用 typescript 模板创建一个新的 React 应用
deno run -A npm:create-vite@latest --template react-ts
当提示时,给您的应用命名,并 cd 进入新创建的项目目录。然后运行以下命令来安装依赖项
deno install
现在您可以通过运行以下命令来启动您的新 React 应用:
deno task dev
这将启动 Vite 服务器,点击输出链接到 localhost 以在浏览器中查看您的应用。如果您安装了 VSCode 的 Deno 扩展,您可能会注意到编辑器突出显示了代码中的一些错误。这是因为 Vite 创建的应用是为 Node 设计的,因此使用了 Deno 不使用的约定(例如“草率导入” - 导入模块时没有文件扩展名)。禁用此项目的 Deno 扩展以避免这些错误,或者尝试使用 deno.json 文件构建 React 应用的教程。
添加后端 跳转到标题
下一步是添加后端 API。我们将创建一个非常简单的 API,返回有关恐龙的信息。
在您的新项目根目录中,创建一个 api 文件夹。在该文件夹中,创建一个 main.ts 文件(用于运行服务器)和一个 data.json 文件(用于包含硬编码的恐龙数据)。
复制并粘贴 此 json 文件 到 api/data.json 文件中。
我们将构建一个简单的 API 服务器,其中包含返回恐龙信息的路由。我们将使用 oak
中间件框架 和 cors
中间件 来启用 CORS。
使用 deno add
命令将所需的依赖项添加到您的项目中
deno add jsr:@oak/oak jsr:@tajpouria/cors
接下来,更新 api/main.ts 以导入所需的模块并创建一个新的 Router
实例来定义一些路由
import { Application, Router } from "@oak/oak";
import { oakCors } from "@tajpouria/cors";
import data from "./data.json" with { type: "json" };
const router = new Router();
之后,在同一文件中,我们将定义两个路由。一个在 /api/dinosaurs
返回所有恐龙,另一个在 /api/dinosaurs/:dinosaur
基于 URL 中的名称返回特定的恐龙
router.get("/api/dinosaurs", (context) => {
context.response.body = data;
});
router.get("/api/dinosaurs/:dinosaur", (context) => {
if (!context?.params?.dinosaur) {
context.response.body = "No dinosaur name provided.";
}
const dinosaur = data.find((item) =>
item.name.toLowerCase() === context.params.dinosaur.toLowerCase()
);
context.response.body = dinosaur ?? "No dinosaur found.";
});
最后,在同一文件的底部,创建一个新的 Application
实例,并使用 app.use(router.routes())
将我们刚刚定义的路由附加到应用程序,并启动服务器监听 8000 端口
const app = new Application();
app.use(oakCors());
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 8000 });
您可以使用 deno run --allow-env --allow-net api/main.ts
运行 API 服务器。我们将创建一个任务在后台运行此命令,并更新 dev 任务以同时运行 React 应用和 API 服务器。
在您的 package.json
文件中,更新 scripts
字段以包含以下内容:
{
"scripts": {
"dev": "deno task dev:api & deno task dev:vite",
"dev:api": "deno run --allow-env --allow-net api/main.ts",
"dev:vite": "deno run -A npm:vite",
// ...
}
如果您现在运行 deno task dev
并访问 localhost:8000/api/dinosaurs
,您应该在浏览器中看到所有恐龙的 JSON 响应。
更新入口点 跳转到标题
React 应用的入口点在 src/main.tsx 文件中。我们的入口点将非常基础。
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<App />,
);
添加路由器 跳转到标题
该应用将有两个路由:/
和 /:dinosaur
。
我们将使用 react-router-dom
构建一些路由逻辑,因此我们需要将 react-router-dom
依赖项添加到您的项目中。在项目根目录中运行:
deno add npm:react-router-dom
更新 /src/App.tsx 文件以导入和使用 react-router-dom 的 BrowserRouter
组件并定义两个路由
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Index from "./pages/index";
import Dinosaur from "./pages/Dinosaur";
import "./App.css";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Index />} />
<Route path="/:selectedDinosaur" element={<Dinosaur />} />
</Routes>
</BrowserRouter>
);
}
export default App;
代理转发 API 请求 跳转到标题
Vite 将在 5173 端口上提供应用程序服务,而我们的 API 在 8000 端口上运行。因此,我们需要设置一个代理,以允许路由器访问 api/-paths。用以下内容覆盖 vite.config.ts 以配置代理:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
proxy: {
"/api": {
target: "http://localhost:8000",
changeOrigin: true,
},
},
},
});
创建页面 跳转到标题
我们将创建两个页面:Index 和 Dinosaur。Index 页面将列出所有恐龙,而 Dinosaur 页面将显示特定恐龙的详细信息。
在 src 目录中创建一个 pages 文件夹,并在其中创建两个文件:index.tsx 和 Dinosaur.tsx。
类型 跳转到标题
两个页面都将使用 Dino 类型来描述它们期望从 API 获取的数据形状,因此让我们在 src 目录中创建一个 types.ts 文件。
export type Dino = { name: string; description: string };
index.tsx 跳转到标题
此页面将从 API 获取恐龙列表并将它们渲染为链接。
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { Dino } from "../types.ts";
export default function Index() {
const [dinosaurs, setDinosaurs] = useState<Dino[]>([]);
useEffect(() => {
(async () => {
const response = await fetch(`/api/dinosaurs/`);
const allDinosaurs = await response.json() as Dino[];
setDinosaurs(allDinosaurs);
})();
}, []);
return (
<main>
<h1>Welcome to the Dinosaur app</h1>
<p>Click on a dinosaur below to learn more.</p>
{dinosaurs.map((dinosaur: Dino) => {
return (
<Link
to={`/${dinosaur.name.toLowerCase()}`}
key={dinosaur.name}
className="dinosaur"
>
{dinosaur.name}
</Link>
);
})}
</main>
);
}
Dinosaur.tsx 跳转到标题
此页面将从 API 获取特定恐龙的详细信息并在段落中渲染它。
import { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { Dino } from "../types";
export default function Dinosaur() {
const { selectedDinosaur } = useParams();
const [dinosaur, setDino] = useState<Dino>({ name: "", description: "" });
useEffect(() => {
(async () => {
const resp = await fetch(`/api/dinosaurs/${selectedDinosaur}`);
const dino = await resp.json() as Dino;
setDino(dino);
})();
}, [selectedDinosaur]);
return (
<div>
<h1>{dinosaur.name}</h1>
<p>{dinosaur.description}</p>
<Link to="/">🠠 Back to all dinosaurs</Link>
</div>
);
}
样式化恐龙列表 跳转到标题
由于我们在主页上显示恐龙列表,让我们进行一些基本格式化。将以下内容添加到 src/App.css 的底部,以有序地显示我们的恐龙列表。
.dinosaur {
display: block;
}
运行应用 跳转到标题
要运行应用,请使用您之前设置的任务。
deno task dev
在浏览器中导航到本地 Vite 服务器 (localhost:5173
),您应该看到显示的恐龙列表,您可以点击浏览以了解每个恐龙的信息。
构建和部署 跳转到标题
此时,该应用正在由 Vite 开发服务器提供服务。要在生产环境中提供该应用,您可以使用 Vite 构建该应用,然后使用 Deno 提供构建的文件。为此,我们需要更新 API 服务器以提供构建的文件。我们将编写一些中间件来执行此操作。在您的 api 目录中,创建一个新的文件夹 util 和一个名为 routeStaticFilesFrom.ts 的新文件,并添加以下代码:
import { Next } from "jsr:@oak/oak/middleware";
import { Context } from "jsr:@oak/oak/context";
// Configure static site routes so that we can serve
// the Vite build output and the public folder
export default function routeStaticFilesFrom(staticPaths: string[]) {
return async (context: Context<Record<string, object>>, next: Next) => {
for (const path of staticPaths) {
try {
await context.send({ root: path, index: "index.html" });
return;
} catch {
continue;
}
}
await next();
};
}
此中间件将尝试从 staticPaths 数组中提供的路径提供静态文件。如果未找到该文件,它将调用链中的下一个中间件。我们现在可以更新 api/main.ts 文件以使用此中间件。
import { Application, Router } from "@oak/oak";
import { oakCors } from "@tajpouria/cors";
import data from "./data.json" with { type: "json" };
import routeStaticFilesFrom from "./util/routeStaticFilesFrom.ts";
const router = new Router();
router.get("/api/dinosaurs", (context) => {
context.response.body = data;
});
router.get("/api/dinosaurs/:dinosaur", (context) => {
if (!context?.params?.dinosaur) {
context.response.body = "No dinosaur name provided.";
}
const dinosaur = data.find((item) =>
item.name.toLowerCase() === context.params.dinosaur.toLowerCase()
);
context.response.body = dinosaur ? dinosaur : "No dinosaur found.";
});
const app = new Application();
app.use(oakCors());
app.use(router.routes());
app.use(router.allowedMethods());
app.use(routeStaticFilesFrom([
`${Deno.cwd()}/dist`,
`${Deno.cwd()}/public`,
]));
await app.listen({ port: 8000 });
在您的 package.json 文件中添加一个 serve 脚本,以使用 Vite 构建应用,然后运行 API 服务器。
{
"scripts": {
// ...
"serve": "deno task build && deno task dev:api",
}
现在您可以通过运行以下命令使用 Deno 提供构建的应用:
deno task serve
如果您在浏览器中访问 localhost:8000
,您应该看到应用正在运行!
🦕 现在您可以使用 Vite 和 Deno 搭建和开发 React 应用了!您已准备好构建极速 Web 应用程序。我们希望您喜欢探索这些前沿工具,我们迫不及待想看看您会创造什么!