hono.js 使用mysql 查询博客文章上下文,并判断是否存在上下文,在jsx 模板中 显示博文,底部显示,上文,和下文链接。
以下是在 Hono.js 中结合 MySQL 查询和 JSX 模板实现博客文章上下文展示的完整示例:
// app.tsx
import { Hono } from 'hono'
import { jsx } from 'hono/jsx'
import { createPool } from 'mysql2/promise'
// 创建 MySQL 连接池
const pool = createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'blog_db',
waitForConnections: true,
connectionLimit: 10,
})
const app = new Hono()
// 文章详情页路由
app.get('/post/:id', async (c) => {
const id = c.req.param('id')
try {
// 获取当前文章
const [currentPost] = await pool.query(
'SELECT id, title, content FROM posts WHERE id = ?',
[id]
)
if (!currentPost[0]) return c.notFound()
// 获取上一篇(更早的文章)
const [previousResult] = await pool.query(
`SELECT id, title
FROM posts
WHERE publish_time < (SELECT publish_time FROM posts WHERE id = ?)
ORDER BY publish_time DESC
LIMIT 1`,
[id]
)
// 获取下一篇(更新的文章)
const [nextResult] = await pool.query(
`SELECT id, title
FROM posts
WHERE publish_time > (SELECT publish_time FROM posts WHERE id = ?)
ORDER BY publish_time ASC
LIMIT 1`,
[id]
)
const previousPost = previousResult[0] || null
const nextPost = nextResult[0] || null
return c.html(
<PostPage
post={currentPost[0]}
previous={previousPost}
next={nextPost}
/>
)
} catch (error) {
console.error(error)
return c.text('Internal Server Error', 500)
}
})
// JSX 模板组件
const PostPage = ({ post, previous, next }) => (
<html>
<head>
<title>{post.title}</title>
</head>
<body>
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
<nav class="post-navigation">
{/* 上一篇链接 */}
{previous ? (
<a href={`/post/${previous.id}`}>« {previous.title}</a>
) : (
<span class="disabled">« 没有上一篇</span>
)}
{/* 下一篇链接 */}
{next ? (
<a href={`/post/${next.id}`} class="next">
{next.title} »
</a>
) : (
<span class="disabled">没有下一篇 »</span>
)}
</nav>
</body>
</html>
)
export default app
关键实现说明:
- 数据库查询优化
// 使用子查询提高性能
const [previousResult] = await pool.query(
`SELECT id, title
FROM posts
WHERE publish_time < (SELECT publish_time FROM current_post)
ORDER BY publish_time DESC
LIMIT 1`
)
- 导航链接条件渲染
{/* 更安全的可选链判断 */}
{previous?.id && (
<a href={`/post/${encodeURIComponent(previous.id)}`}>
{previous.title}
</a>
)}
- 样式增强方案
/* 添加 CSS 类名处理样式 */
.post-navigation {
display: flex;
justify-content: space-between;
margin-top: 2rem;
}
.post-navigation a {
padding: 0.5rem 1rem;
background: #f0f0f0;
}
.post-navigation .disabled {
color: #999;
cursor: not-allowed;
}
- 边界情况处理
// 处理删除的文章
if (!currentPost[0]) {
return c.html(<ErrorPage message="文章不存在" />, 404)
}
性能优化建议
- 数据库索引
-- 为 publish_time 和 id 创建复合索引
CREATE INDEX idx_posts_ordering ON posts (publish_time, id);
- 合并查询
// 使用 UNION 合并查询减少数据库请求
const [results] = await pool.query(`
(SELECT 'previous' AS type, id, title
FROM posts
WHERE publish_time < ?
ORDER BY publish_time DESC LIMIT 1)
UNION ALL
(SELECT 'next' AS type, id, title
FROM posts
WHERE publish_time > ?
ORDER BY publish_time ASC LIMIT 1)
`, [postTime, postTime]);
- 缓存策略
// 使用内存缓存(示例使用 lru-cache)
import LRU from 'lru-cache'
const postCache = new LRU({
max: 100,
ttl: 1000 * 60 * 5 // 5分钟
})
app.get('/post/:id', async (c) => {
const id = c.req.param('id')
const cached = postCache.get(id)
if (cached) return c.html(cached)
// ...查询数据库...
postCache.set(id, renderedHtml)
})
完整工作流程
- 用户访问
/post/123
- 服务端查询 ID 为 123 的文章内容
- 并行查询该文章的前后相邻文章
- 组合数据传递给 JSX 模板
- 根据上下文是否存在渲染不同的导航链接
- 最终生成包含完整导航的 HTML 页面
这个实现方案具有以下特点:
- 清晰的上下文导航展示
- 安全的数据库查询防注入
- 响应式的 HTML 结构
- 友好的错误处理机制
- 性能优化扩展点