如何在Koa/Express所有路由中共享全局侧边栏/页脚并复用博客查询?
解决Koa/Express中全局侧边栏重复查询的问题
当然可以搞定!你现在每个路由都重复查侧边栏博客的问题,本质是没利用好框架的中间件机制或者缓存策略,我给你分享几个实用的方案,分框架给你举例子:
方案一:全局中间件(最推荐)
不管是Express还是Koa,中间件都是处理这类“所有请求都需要的公共数据”的最佳方式——它能在所有路由处理之前统一执行查询,然后把结果挂载到请求/上下文对象上,后续所有路由直接用就行,完全不用重复写查询代码。
Express 实现示例
// 先定义一个获取最新博客的全局中间件 app.use(async (req, res, next) => { try { // 这里替换成你的实际查询逻辑,比如从MongoDB取最新5篇 req.latestBlogs = await BlogModel.find().sort({ createdAt: -1 }).limit(5); next(); // 放行,继续执行后面的路由 } catch (err) { // 万一查询出错,做个降级处理,别让页面崩了 console.error('侧边栏博客查询失败:', err); req.latestBlogs = []; next(); } }); // 现在你的路由可以直接用req.latestBlogs了 app.get('/home', (req, res) => { res.render('home', { latestBlogs: req.latestBlogs }); }); app.get('/about', (req, res) => { res.render('about', { latestBlogs: req.latestBlogs }); });
Koa 实现示例
Koa里推荐把公共数据挂到ctx.state上(这是Koa官方建议的上下文数据传递方式):
// 全局中间件 app.use(async (ctx, next) => { try { ctx.state.latestBlogs = await BlogModel.find().sort({ createdAt: -1 }).limit(5); await next(); // 执行后续的路由/中间件 } catch (err) { console.error('侧边栏博客查询失败:', err); ctx.state.latestBlogs = []; await next(); } }); // 路由里直接用ctx.state.latestBlogs router.get('/home', async (ctx) => { await ctx.render('home', { latestBlogs: ctx.state.latestBlogs }); }); router.get('/about', async (ctx) => { await ctx.render('about', { latestBlogs: ctx.state.latestBlogs }); });
方案二:加缓存优化(减少数据库压力)
如果你的博客不是每秒都更新,完全没必要每次请求都查数据库——可以把查询结果缓存一段时间,比如5分钟,这样能大幅降低数据库的查询次数,提升性能。
结合中间件的缓存示例(Express为例)
// 定义缓存变量和过期时间 let cachedLatestBlogs = null; let cacheExpire = 0; const CACHE_TIME = 5 * 60 * 1000; // 5分钟有效期 app.use(async (req, res, next) => { const now = Date.now(); // 缓存不存在或者过期了,重新查询 if (!cachedLatestBlogs || now > cacheExpire) { try { cachedLatestBlogs = await BlogModel.find().sort({ createdAt: -1 }).limit(5); cacheExpire = now + CACHE_TIME; } catch (err) { console.error('侧边栏博客查询失败:', err); cachedLatestBlogs = []; } } req.latestBlogs = cachedLatestBlogs; next(); });
⚠️ 这里要提醒你:别直接用全局变量存请求级的实时数据!你说的“全局变量”如果是模块级的变量(比如let latestBlogs = []),如果不加缓存过期逻辑,数据会一直是第一次查询的结果,除非重启服务——而用中间件挂到请求对象上,每个请求都能拿到最新的(或缓存的)数据,才是正确的做法。
方案三:模板引擎全局变量(适合低更新频率场景)
如果你用的是EJS、Pug这类模板引擎,还可以把侧边栏数据设为模板的全局变量,这样所有模板都能直接用,连路由里传参的步骤都省了。
Express + EJS 示例
// 先给模板引擎设置全局变量,初始为空数组 app.locals.latestBlogs = []; // 定时更新这个全局变量,比如每5分钟更一次 setInterval(async () => { try { app.locals.latestBlogs = await BlogModel.find().sort({ createdAt: -1 }).limit(5); } catch (err) { console.error('更新侧边栏缓存失败:', err); } }, 5 * 60 * 1000); // 路由里渲染模板时,不用再传latestBlogs了 app.get('/home', (req, res) => { res.render('home'); // 模板里直接用<% latestBlogs.forEach(blog => { ... }) %> });
这种方式适合博客更新不频繁的场景,因为是定时更新,不是每次请求都查数据库。
总结一下,最实用的就是中间件+可选缓存的组合,既保证每个请求能拿到需要的数据,又能灵活优化性能,还彻底解决了重复代码的问题。
内容的提问来源于stack exchange,提问作者andreaslacza
相关产品推荐
相关产品推荐

