在本章中,我们将首先了解什么是 Bun JavaScript 运行时,以及为什么它正在成为开发者中流行的 JavaScript 运行时。然后,我们将介绍一个用于开发后端的库——Hono,它提供了一种简单而强大的方式来构建 Web 应用程序。你将设置你的项目,包括安装 Bun 和 Hono,了解一些有用的代码结构,并配置诸如中间件、环境文件、Prettier 和 ESLint 等基本工具。
到本章结束时,你将对 Bun 的环境有深入的了解,并能够设置自己的后端项目。所有这些都将为你开发功能完备的后端应用程序以及构建我们的聊天应用程序的基础提供必要的支持。你还将学习如何提高代码的可读性和可配置性,使其更易于开发。
在本章中,我们将涵盖以下主要主题:
• 介绍 Bun
• 介绍 Hono
• 设置你的项目
• 添加代码检查和格式化
• 添加中间件
• 处理环境变量
• 讨论项目的结构
技术要求
首先,我们需要安装 Bun。
对于 Linux 和 MacOS,使用以下命令:
$ curl -fsSL https://bun.sh/install | bashBun 对 Windows 的支持还处于有限的实验阶段,因此建议使用 Windows 子系统 for Linux(WSL),但您仍然可以使用以下命令进行安装:
$ powershell -c "irm bun.sh/install.ps1|iex"如果出现命令未找到的错误,您需要将 C:/Users/当前用户/. Bun 文件夹和 C:/Users/当前用户/. Bun/bin 添加到 Windows PATH 系统变量中。
这些步骤足以开始,我们将在后续过程中安装更多库。我们在本章中讨论的所有代码示例都可以在以下 GitHub 仓库中找到: [代码库]( https://github.com/PacktPublishing/Full-Stack-Web-Development-with-TypeScript-5/tree/main/Chapter03 GitHub. Com 上面的第三章代码库)
介绍 Bun
Bun 是一个 JavaScript 运行时,是 Node. Js 和 Deno 的替代品。由于其卓越的性能和易用性,它正在迅速获得关注。Bun 原生支持 TypeScript,并且还有许多其他好处,包括以下几点:
• 高性能运行时:Bun 基于 Safari 中使用的 JavaScriptCore 引擎构建,以其速度而闻名。这些以及其他优化使得 Bun 成为一个极其快速的运行时,提供性能提升,有时能够处理比 Node. Js 多 5 到 10 倍的负载。
• 内置打包和转译:与需要外部工具来完成这些任务的传统运行时不同,Bun 具备内置的打包和转译能力。因此,你只需要 Bun 来解释和转译你的代码,我们不需要配置像 webpack、esbuild 或 rollup 这样的工具。
• TypeScript 和 JSX 支持:Bun 原生支持 TypeScript 和 JSX。
• 高效的包管理:Bun 的包管理器设计得比传统的包管理器(如 npm 或 yarn)更快、更高效,在不同场景下速度提高了 2 到 10 倍。
• Node. Js 兼容:Bun 被设计为 Node. Js 的直接替代品。Bun 原生实现了大多数基本的 Node. Js 和 Web API。因此,你可以在 Bun 中使用为 Node. Js 开发的包,甚至可以在现有项目中用 Bun 替换 Node. Js。虽然它不能在 100%的情况下工作,但兼容性相当高,大多数项目可以原样运行,无需进行太多更改。
• 有用的内置库和工具:Bun 提供了一些编写大多数实际项目所需的重要工具,如测试运行器、环境变量处理器和密码生成器。这将为我们节省相当多的时间,因为否则我们需要引入外部库来实现这些功能。
如果你想了解更多关于 Node. Js、Deno 和 Bun 之间的比较,可以访问查看比较它们的图表。
由于其内置的基础设施,包括 HTTP 服务器,Bun 将对开发我们的后端聊天应用程序非常有帮助。然而,对于我们的后端需求,我们需要一个在功能上更强大的东西。这就是 Hono 库发挥作用的地方,它提供了一种极简主义和灵活的 Web 应用程序开发方法。
介绍 Hono
Hono 是一个专为 JavaScript 和 TypeScript 开发者设计的现代、轻量级 Web 框架。它旨在为你提供 Web 开发所需的一切,而不会提供不必要的功能,也不会限制你的选择。
Hono 负责处理诸如路由、数据验证和中间件创建等基本任务,但不会强制规定其他内容,例如数据库交互、架构模式或日志库。这种灵活性让你可以根据项目需求选择最适合的工具和实践,同时也提供了足够多的工具来处理那些繁琐的任务。
Hono 的主要特点如下:
• TypeScript 集成:Hono 与 TypeScript 无缝集成,在你的端点周围提供额外的类型安全性。
• 路由:它提供简单的路由处理,帮助我们根据 URL 确定要调用的端点。
• 中间件:Hono 支持中间件,允许你在请求处理器执行前后添加功能。
• 错误处理:该库包含内置的错误处理功能,以便于调试并向调用者提供有意义的错误信息。
• JSON 支持:Hono 原生支持 JSON 解析和响应格式化,因此我们可以轻松地接收和返回最流行的 API 格式。
• 查询和参数解析:它自动解析 URL 参数和查询字符串。
• 静态文件服务:Hono 可以提供静态文件,如图片、CSS 和 JavaScript。
• 可定制的上下文:你可以通过自定义属性或方法扩展请求上下文。
• Cookie 处理:该框架提供用于设置、获取和删除 Cookie 的函数,这些 Cookie 通常用于身份验证目的。
• 头部操作:Hono 简化了 HTTP 请求和响应头部的操作,这对于安全性很重要。
• 额外的库:Hono 通过外部库(主要是中间件)补充其核心功能,以实现诸如 CORS 设置、请求日志记录以及跟踪端点性能指标等功能。
随着我们开发应用程序,我们将更加熟悉 Hono 和 Bun 及其功能,因此让我们设置我们的项目并看看它们的实际效果。
设置项目
让我们从设置项目的主体部分开始。我们将通过使用模板来简化这一过程,模板可以帮助我们快速创建基本结构:
• 首先,运行以下命令,使用 Bun 从模板创建一个 Hono 项目:
$ bun create hono chat_backend• 当提示时,选择 bun 模板并按回车键。几秒钟后,你将拥有一个最小化的初始设置。接下来,导航到项目文件夹,安装必要的依赖项,并使用以下命令启动本地后端服务器:
$ cd chat_backend
$ bun install
$ bun run dev• 在此之后,你可以在终端中期待以下输出:
$ bun run --hot src/index.ts
Started server http://localhost:3000现在,如果你在浏览器中访问 http://localhost:3000,你应该会看到显示的消息“Hello Hono!”。恭喜你,你刚刚创建了你的第一个 Hono 应用程序!它现在还很基础,但我们会很快添加更多内容。
让我们查看 package.json 文件以了解其内容:
{
"scripts": {
"dev": "bun run --hot src/index.ts"
},
"dependencies": {
"hono": "^3.12.2"
},
"devDependencies": {
"@types/bun": "^1.0.0"
}
}在这个文件中,我们可以看到一个 scripts 键,它定义了我们可以运行的命令。dev 脚本是 Bun 运行的脚本。它很简单——它使用入口点 src/index.ts 启动我们的项目。这里的一个显著特点是 --hot 标志,它启用了热重载功能。这意味着 Bun 会自动重新加载已更改的文件,而无需重启整个操作系统进程。这使得开发体验更快、更愉快。
关于依赖项,我们目前有一个生产依赖项 hono 和一个开发依赖项 @types/bun。@types/bun 包是一个你经常会在 TypeScript 中看到的模式。它提供了特定于 Bun 环境的类型定义,例如 Bun 函数的返回类型。
现在,让我们查看 tsconfig.json 中的 TypeScript 配置。我们将移除与 JSX 相关的配置(因为我们不在后端使用 JSX),并添加 Bun 类型,得到以下内容:
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"types": ["bun-types"]
}
}让我们分解一下 compilerOptions 中每一部分的作用:
• "esModuleInterop": true:此设置允许在 TypeScript 中兼容使用 CommonJS 模块,类似于 ES 6 模块。它简化了 CommonJS 模块的导入,使得可以使用 import fs from 'fs' 这样的语法,而不是 import * as fs from 'fs'。
• "strict": true:在 TypeScript 中激活所有严格的类型检查选项。这将导致最彻底的类型检查,包括严格的空值检查和不允许隐式 any 等。这是一种在代码编译期间确保更全面错误检查的方法。
• "types": ["bun-types"]:特别为编译器包含 Bun 类型库。通常,你可能不需要这样指定类型,但 Bun 作为一个独特的环境,需要在这里明确声明其类型。
接下来,让我们讨论一下 bun.lockb 文件。这个文件是 Bun 中使用的锁定文件。它的主要作用是通过锁定每个包及其传递依赖项的具体版本,来维护项目依赖项的一致性。
例如,如果你的 package.json 中将 hono 标记为 ^3.12.2(表示从 3.12.2 到但不包括 4.0.0 的任何版本),bun.lockb 可能会将其具体锁定为 3.13.0。这确保了参与项目的每个开发人员都使用完全相同的版本,防止了常见的“在我的电脑上可以运行”的问题。
值得注意的是,bun.lockb 文件是二进制格式的,Bun 使用它来减小文件大小并优化其包管理系统的性能。
现在,让我们查看最后一个重要的文件 src/index.ts,它包含了我们服务器的代码:
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default app现在,让我们分解一下这段代码中发生了什么:
• 导入 Hono 和服务器初始化:我们首先导入 Hono 库。然后,我们使用 const app = new Hono() 创建了一个 Hono 服务器的新实例。
• 路由定义:app.get('/', 这一行是我们定义路由的地方。它指示 Hono 应用程序处理根 URL 路径 '/' 的 HTTP GET 请求。当访问此 URL 时,将执行回调函数 (c) => { return c.text('Hello Hono!') }。此函数利用 Hono 提供的上下文对象 c 将文本响应“Hello Hono!”发送回客户端。
• 导出应用程序:export default app 允许将应用程序实例导入并在其他文件中使用,或者用于启动服务器。然而,只处理纯文本响应并不常见,因此我们将更改我们的端点以返回 JSON 响应。因此,我们将 return c.text('Hello Hono!') 替换为 return c.json({'message':'Hello Hono!'})。
在进行此更改并重新加载页面后,你的浏览器应该显示以下内容:
{
"message": "Hello Hono!"
}至此,我们有了第一个返回 JSON 的端点!在继续开发我们的应用程序之前,最好集成一些工具,以简化我们的工作流程并提高代码质量。我们将设置代码检查、格式化和实用中间件,我们还将讨论处理环境变量。让我们从代码检查和格式化开始。
添加代码检查和格式化
代码检查(Linting)是一个扫描代码以查找错误或不一致的过程,而无需实际运行代码。它对于捕获未使用的变量、防止在生产环境中出现 console.log 语句,甚至识别逻辑错误(例如无限循环)非常有用。
和静态类型一样,这都是为了让我们的代码更好,并尽早发现问题。对于这个项目,我们将使用 ESLint,这是最常用的 JavaScript 代码检查工具。我们将保持简单,并遵循 Hono 项目推荐的规则。
首先,让我们全局安装 ESLint,以便我们可以在命令行中使用它:
$ bun install -g eslint现在让我们安装 Hono 推荐的规则:
$ bun install --dev @hono/eslint-config现在,我们可以在项目的根目录下创建一个带有 ESLint 配置的文件,并将以下配置放入其中,以包含 Hono 推荐的代码检查规则: .eslintrc.cjs
module.exports = {
extends: ["@hono/eslint-config"]
};这为你设置了基本的 ESLint 配置。我们可以使用以下命令运行我们的代码检查工具:
$ eslint --fix将代码检查工具与你的集成开发环境(IDE)集成是非常有益的。这允许它在你保存文件时自动运行,从而使开发体验更加顺畅。
在 WebStorm 中,你可以这样操作:
• 打开设置。
• 转到“语言和框架”|“JavaScript”|“代码质量工具”|“ESLint”。
• 如果自动 ESLint 配置已关闭,请将其打开。
• 勾选“保存时运行 eslint --fix”。
在 Visual Studio Code 中,你可以这样操作:
• 打开设置。
• 在顶部的搜索栏中输入 editor.codeActionsOnSave。
• 选择“在 settings. Json 中编辑”,以在 VSCode 中打开 settings.json 文件。
• 在你的 settings.json 文件中插入以下内容:
{
"editor.codeActionsOnSave": { "source.fixAll.eslint": true },
"eslint.alwaysShowStatus": true
}为了看看它是如何工作的,你可以在 src/index.ts 中创建一个未使用的变量。现在,保存文件,你将在 IDE 中看到一个警告,提示该变量未被使用。此外,你现在可以在终端中运行 eslint,你将得到类似以下的消息:
8:7 warning 'a' is assigned a value but never used @typescript-eslint/no-unused-vars
✖ 1 problem (0 errors, 1 warning)
添加中间件
现在,让我们专注于格式化。格式化确保我们的代码具有一致的风格,当多人协作处理一个项目时,这非常有价值。有趣的是,有许多有效的代码风格。关键不在于你选择哪种风格,而在于每个人都始终如一地遵循相同的风格。一个自动强制执行风格的格式化工具是一个救星,它在代码审查中节省了无数小时,并防止了关于代码风格的争论。对于我们的项目,我们将集成 Prettier,这是一个流行的工具,它自动将代码格式化为预定义的风格。
将 Prettier 与 ESLint 集成,使它们同时运行并同时修复格式化和代码检查错误,也非常方便。这使得两个工具可以同时运行,同时修复格式化和代码检查错误。首先,我们需要安装额外的库,以将 Prettier 作为 ESLint 插件处理:
$ bun install --dev eslint-config-prettier eslint-plugin-prettier现在,你可以扩展我们的 ESLint 配置,使其看起来像这样: .eslintrc. Cjs
module.exports = {
extends: ["@hono/eslint-config", "plugin:prettier/recommended", "prettier"],
plugins: ["prettier"],
rules: {
"prettier/prettier": "error",
}
};这一添加将 Prettier 规则包含在 ESLint 配置中。我们将 Prettier 错误设置为被视为错误,而不是警告。这种严格的方法有助于保持项目的整体质量。
现在,尝试在代码中的变量中添加额外的制表符或空格。当你保存文件时,你会注意到它会自动格式化为正确的风格,这都归功于 Prettier。很方便,不是吗?
设置好代码检查和格式化后,让我们继续讨论可以进一步增强开发体验的额外中间件。
添加 中间件 在 Hono 中,中间件充当拦截函数,在 HTTP 请求到达最终路由处理器之前对其进行处理。中间件也可以在将响应发送回客户端之前处理响应。它们可以修改请求(例如,解析正文、添加头部)和响应(例如,设置 Cookie、修改头部)。
中间件按照在代码中定义的顺序执行。每个中间件函数可以决定是将请求传递给下一块中间件还是结束响应周期。它通常用于日志记录、身份验证、错误处理和数据解析。
我们将实现自己的中间件来处理身份验证。然而,目前,让我们整合 Hono 提供的一些现成的中间件。我们将添加一个用于基本请求日志记录的中间件和另一个用于向响应添加性能指标的中间件。
以下是我们在 index.ts 文件的开头进行修改以包含这些中间件的方式:
Src/index. Ts
import { Hono } from 'hono'
import { logger } from "hono/logger";
import { timing } from "hono/timing";
const app = new Hono()
app.use("*", timing());
app.use("*", logger());在这段代码中,我们导入了 logger 和 timing 库,然后使用 app.use 函数将它们添加到我们的应用程序中,路径为 '*'。这个路径规范确保中间件应用于所有传入的请求,尽管它可以调整为针对特定端点。
现在,当你访问端点时,日志将包括如下额外的行:
<-- GET /
--> GET / 200 4ms
这些日志条目提供了诸如访问的端点、响应状态码以及响应所花费的时间等详细信息。此外,如果你在浏览器的开发者控制台中检查响应头部,你会注意到附加了一个新的 'Server-Timing' 头部。它携带了类似的信息,例如:
total;dur=0.1;desc="Total Response Time"
这一添加为我们提供了有关请求性能的宝贵见解。
现在,让我们讨论如何有效管理环境变量。
处理环境变量
环境变量是一种标准方式,用于将秘密或特定于平台的详细信息保留在代码库之外。它们也便于配置应用程序的各个方面。Bun 有一个内置的机制来处理环境变量,可以通过 Bun.env.VARIABLE_NAME 在代码中访问。它甚至会自动从 .env 文件中读取,所以我们只需要提供这个文件。
以下是设置方法:
• 首先,创建一个名为 .env 的文件。
• 在 .env 文件中,定义一个变量,如 TEST=test value。
• 在我们的 src/index.ts 中,添加 console.log(Bun.env.TEST) 这一行。记住,环境变量不会热重载,因此你需要重启服务器才能在控制台中看到更改。重启后,你应该会在终端中看到打印出的 test value。
有时,你可能需要为不同的情景使用不同的环境文件,例如本地开发与生产环境。或者,你可能为不同的用例有不同的环境变量集合,因此你最终会有多个 .env 文件,而 Bun 只会为你处理 .env 文件。在这种情况下,你可以使用像 dotenv 这样的工具从多个文件中加载额外的环境变量。
让我们通过从 .env.dev 记录另一个环境变量来说明这一点。在 src/index.ts 中添加 console.log(Bun.env.AI),并在我们的 .env.dev 文件中添加 AI=chat。你会看到该值是 undefined。让我们通过重启服务器并为其提供额外的环境变量文件来解决这个问题:
$ bunx dotenv -e .env -e .env.dev -- bun run dev使用这个命令重启项目后,你应该会在控制台中看到 test value 和 chat。
设置好环境变量后,我们现在就可以深入讨论我们应用程序的项目结构了。
重要提示
不要将环境文件添加到你的 Git 仓库中,因为这是一个安全风险。你的环境文件必须放入 .gitignore 文件中,并且不应该被提交。
讨论项目结构
在为项目构建代码时,拥有清晰的关注点分离至关重要。这意味着以一种方式组织文件和文件夹,使每个组件都有明确的责任,从而有助于提高整个项目的清晰度和可维护性。对于我们的相对较小的项目,我们将选择一个简单的结构。
以下是我们将使用的结构及其背后的理由:
• src/controllers:此文件夹将包含特定的 REST 端点处理程序。每个控制器处理传入的请求并生成适当的响应。通过将端点逻辑隔离在控制器中,我们更容易更新或扩展 API 功能。
• src/middlewares:在这里,我们将存储 Hono 的额外中间件函数。中间件对于处理请求和响应至关重要,提供诸如身份验证、日志记录或数据解析等功能。将它们放在一个专门的文件夹中可以方便重用和管理。
• src/models:此目录指定用于我们代码中使用的对象的类型定义。它确保了数据结构定义的集中位置,增强了代码的一致性,并减少了类型相关错误的可能性。
• src/storage:一个用于管理与各种存储解决方案交互的代码的文件夹,例如内存数据库、SQL 数据库或 ORM。这种分离确保了存储逻辑的变化不会影响应用程序的其他部分。
• src/constants. Ts:这是一个用于存储项目范围常量的文件。通过集中常量,我们确保了一致性,并防止了由于在多个位置硬编码值而可能出现的差异。
• src/index. Ts:这是我们应用程序的入口点。在这里,我们将应用程序的各个组件联系在一起,设置服务器、中间件、路由以及任何初始配置。
• tests:这是一个专门用于存储测试文件的文件夹。
这种结构是为我们聊天后端应用程序量身定制的,确保每个模块都有明确的角色和责任。它促进了有序的开发环境,使我们能够根据需要更轻松地导航、维护和扩展我们的应用程序。
总结
在本章中,我们探索了 Bun 和 Hono 的核心要素。Bun 是 JavaScript 运行时领域中的一颗冉冉升起的新星,而 Hono 则是一个以简单性和在 Web 应用程序开发中的高效性而闻名的框架。我们逐步介绍了为 Bun 和 Hono 定制开发环境的实际步骤,包括安装过程、建立连贯的代码结构以及集成日志记录和 ESLint 等基本工具。本章还涵盖了项目设置、代码检查和格式化、中间件集成、管理环境变量以及讨论有效的项目结构策略等关键主题。
在下一章中,我们将为我们的聊天应用程序实现后端功能。我们的后端将使用内存存储,并且我们将从头到尾实现功能。
$\ce{Zn^2+ <=>[+ 2OH-][+ 2H+] $\underset{\text{amphoteres Hydroxid}}{\ce{Zn(OH)2 v}}$ <=>[+ 2OH-][+ 2H+] $\underset{\text{Hydroxozikat}}{\ce{[Zn(OH)4]^2-}}$}$
graph LR emperor((朱八八))-.子.->朱五四-.子.->朱四九-.子.->朱百六 朱雄英--长子-->朱标--长子-->emperor emperor2((朱允炆))--次子-->朱标 朱樉--次子-->emperor 朱棡--三子-->emperor emperor3((朱棣))--四子-->emperor emperor4((朱高炽))--长子-->emperor3