关注【索引目录】服务号,更多精彩内容等你来探索!
“最小”框架正在欺骗你
废话少说:Hono 和 Elysia 并不是可扩展的 Web 框架。
它们非常适合用于玩具项目、快速演示和 Hacker News 要点——但如果你曾经尝试在生产级应用中扩展它们,你就会知道其中的痛苦:
-
隐藏在插件中的中间件逻辑 -
全球国家混乱 -
装饰者打破沉默 -
无法追踪的隐式路由行为
第一天它们看起来很干净。到了第三十天,就变成了无法维护的抽象汤。
时间:功能性、明确性、按比例建造
Tirne是一个 Bun 原生的 Web 框架,由被魔法驱动的工具链所困扰的工程师设计。
我们不想构建一个框架。
我们想在凌晨 2 点停止调试装饰器。
Tirne 从 Go 中汲取灵感net/http— — Go 的功能是一流的,结构是可见的,并且控制权始终掌握在你手中。
// index.ts
import { createRouter, compose, json } from "tirne";
const logger = async (ctx, next) => {
console.log(`[${ctx.method}] ${ctx.url.pathname}`);
return await next();
};
const routes = [
{
method: "GET",
path: "/",
handler: compose([logger], () => json({ hello: "Tirne" })),
},
];
Bun.serve({ fetch: createRouter(routes) });
就是这样。没有宏。没有命令行界面。没有隐藏行为。它易于扩展,因为它是显式的。 ⭐
Tirne 存在的原因(以及为什么您可能需要它)
-
因为您不需要阅读框架内部内容来理解处理程序。 -
因为 Go 风格的错误处理比 try/catch体操更安全。 -
因为路由和中间件应该是函数,而不是链式魔法书。 -
因为“零样板”不应该意味着“零控制”。
Tirne 是 Go 和 Bun 生下的孩子——由一位讨厌装饰者的后端工程师抚养长大。
真正的比较:Tirne 与 Toys
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
想要规模化?那就先让你的架构变得简单——但又清晰可见。
哲学:没有魔法,只有代码
Tirne 遵循五条不容置疑的规则:
- 如果它不能用五行写出来,它可能就不应该存在。
- 函数 > 框架。始终如此。
- 返回错误。不要抛出错误。
- 像大人一样平行奔跑。
- 一个文件就足够了。始终如此。
你不需要框架。你需要的是结构。Tirne 就是那个结构。
安装它。使用它。判断它。
包子
bun init
bun add tirne
touch index.ts
//index.ts
import { createRouter, json } from "tirne";
const routes = [
{ method: "GET", path: "/", handler: () => json({ msg: "Hello" }) },
];
Bun.serve({ fetch: createRouter(routes) });
bun run index.ts
德诺
touch index.ts deno.json
//index.ts
import { createRouter, json } from "https://deno.land/x/tirne@v1.0.2/mod.ts";
const routes = [
{
method: "GET",
path: "/",
handler: () => json({ message: "Hello from Deno + Tirne!" }),
},
];
const router = createRouter(routes);
console.log("Server is running on http://localhost:8000");
Deno.serve({ handler: router });
//deno.json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["deno.ns", "deno.dom","dom"],
"strict": true
}
}
deno run --allow-net index.ts
Cloudflare Workers
npm install -g wrangler
wrangler init tirne-worker
cd tirne-worker
npm install tirne
// src/index.ts
import { createRouter, json } from "tirne";
const routes = [
{
method: "GET",
path: "/",
handler: () => json({ message: "Hello from Tirne on Cloudflare Workers!" }),
},
];
const router = createRouter(routes);
export default {
fetch: router,
};
wrangler dev
节点
npm init -y
npm install --save-dev ts-node typescript tirne
touch index.ts
//index.ts
import { createServer, IncomingMessage } from "http";
import { createRouter, json } from "tirne";
const routes = [
{
method: "GET",
path: "/",
handler: () => json({ message: "Hello from Node.js + Tirne!" }),
},
];
const router = createRouter(routes);
const server = createServer(async (req, res) => {
const url = `http://localhost:3000${req.url}`;
const headers = new Headers(
Object.entries(req.headers || {})
.filter(([_, value]) => typeof value === "string")
.map(([key, value]) => [key, value as string])
);
const request = new Request(url, {
method: req.method,
headers,
body: req.method !== "GET" && req.method !== "HEAD" ? await streamToBuffer(req) : undefined,
});
async function streamToBuffer(stream: IncomingMessage): Promise<Buffer> {
const chunks: Buffer[] = [];
for await (const chunk of stream) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
}
return Buffer.concat(chunks);
}
const response = await router(request);
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
const body = await response.text(); //
res.end(body);
});
server.listen(3000, () => {
console.log("Server is running on http://localhost:3000");
});
npx ts-node index.ts

