koa怎么设置全局变量

在 Koa 中设置全局变量并让模板引擎可以使用,通常需要以下几个步骤:

一、使用中间件设置全局变量

Koa 本身没有内置的模板渲染功能,通常我们会使用如 koa-views、koa-ejs、koa-nunjucks等中间件来渲染模板。这些中间件通常允许你在渲染时传入局部变量,但如果你想设置​​全局变量​​(即每个模板都可以访问的变量,比如网站标题、当前用户信息等),可以通过中间件的方式在上下文(ctx)上挂载变量,然后在渲染时将这些变量传递给模板引擎。

二、具体实现步骤

1. 安装模板引擎中间件(以 koa-views+ ejs为例)

npm install koa-views ejs

2. 配置模板引擎和全局变量

const Koa = require('koa');
const views = require('koa-views');
const path = require('path');

const app = new Koa();

// 设置模板目录和引擎(这里以 ejs 为例)
app.use(views(path.join(__dirname, '/views'), {
  extension: 'ejs'
}));

// 设置全局变量的中间件
app.use(async (ctx, next) => {
  // 设置全局变量,所有模板都可以访问
  ctx.state.siteName = '我的网站';
  ctx.state.currentYear = new Date().getFullYear();

  // 如果有用户登录信息,也可以挂载
  // ctx.state.user = { name: '张三' };

  await next();
});

// 示例路由
app.use(async (ctx) => {
  if (ctx.path === '/') {
    // 渲染模板时,除了可以传入局部变量,还可以使用 ctx.state 中的全局变量
    await ctx.render('index', {
      title: '首页',
      // 这里的变量是局部的,和 ctx.state 的全局变量可以共存
    });
  }
});

app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

3. 在模板中使用全局变量(以 EJS 为例)

假设你的模板文件位于 /views/index.ejs:

<!DOCTYPE html>
<html>
<head>
  <title><%= title %> - <%= siteName %></title>
</head>
<body>
  <h1>欢迎来到 <%= title %></h1>
  <p>网站名称:<%= siteName %></p>
  <p>当前年份:<%= currentYear %></p>
</body>
</html>

✅ 注意:在 koa-views中,所有通过 ctx.state设置的变量,都会​​自动注入到每个渲染的模板中​​,你不需要手动传递它们!也就是说,你只需要在中间件里设置 ctx.state.xxx,然后在模板里直接用 <%= xxx %>就可以访问到。

三、其他模板引擎(如 nunjucks、handlebars 等)

如果你使用的是别的模板引擎,比如 koa-nunjucks-2,原理是一样的:

npm install koa-nunjucks-2
配置示例:

const Koa = require('koa');
const nunjucks = require('koa-nunjucks-2');
const path = require('path');

const app = new Koa();

// 配置 nunjucks 模板引擎
app.use(nunjucks({
  ext: 'html',
  path: path.join(__dirname, 'views'), // 模板目录
  nunjucksConfig: {
    trimBlocks: true,
  },
}));

// 设置全局变量
app.use(async (ctx, next) => {
  ctx.state.siteName = '我的网站';
  await next();
});

app.use(async ctx => {
  if (ctx.path === '/') {
    await ctx.render('index', { title: '首页' });
  }
});

app.listen(3000);

在 views/index.html模板中:

<h1>{{ title }}</h1>
<p>网站名称:{{ siteName }}</p>

四、总结

✅ 推荐做法: 使用 ctx.state来设置​​全局变量​​,然后在模板中直接使用。

通过一个​​中间件​​来设置这些全局变量(如网站名、当前年份、菜单、用户信息等)。

大多数 Koa 的模板渲染中间件(如 koa-views, koa-nunjucks-2, koa-ejs等)都会自动将 ctx.state注入到模板上下文中,无需手动传递。

📌 常用全局变量可能包括: 网站名称(siteName)

当前年份(currentYear)

菜单数据(menus)

当前登录用户(user)

网站配置信息等

五、附加提示

如果你真的想设置 Koa 应用级别的全局变量(不是请求级别的),你也可以挂载到 app.context上,例如:

app.context.siteName = '我的网站'; 但这种方式是​​应用级别​​的常量,所有请求共享同一个值,无法动态变化(比如不能根据用户不同显示不同内容)。所以推荐还是使用 ctx.state在中间件中设置。

如你使用的是其他模板引擎,欢迎告诉我,我可以给出对应示例!

在 Koa 中,app.context是 Koa 应用的​​应用级上下文对象​​,它是所有请求的 ctx(即 ctx的原型)的基对象。你可以在 app.context上定义一些​​全局共享的属性或方法​​,这些属性会被每个请求的 ctx自动继承。

但是!

❗ ​​app.context上的属性默认情况下不会自动传递给模板引擎!​​

也就是说,即使你在 app.context上定义了变量,比如 app.context.siteName = '我的网站',在模板中​​不能直接使用 siteName​​,除非你手动将它传给模板渲染函数,或者通过中间件将其挂载到 ctx.state中。

一、app.context是什么?

app.context是 Koa 提供的一个对象,它是每个请求上下文 ctx的原型。你可以在它上面添加属性或方法,这样所有的 ctx都可以访问到它们,类似全局共享。

示例:

const Koa = require('koa');
const app = new Koa();

// 在 app.context 上定义全局属性
app.context.siteName = '我的网站';
app.context.currentYear = new Date().getFullYear();

app.use(async ctx => {
  ctx.body = `
    <h1>网站名称:${ctx.siteName}</h1>
    <p>当前年份:${ctx.currentYear}</p>
  `;
});

app.listen(3000);

✅ 在上面的普通 HTTP 响应中(ctx.body直接输出 HTML),你可以直接通过 ctx.siteName和 ctx.currentYear访问 app.context上的属性,因为 ctx是基于 app.context的。

二、但在模板中,​​不能直接使用 app.context上的变量!​​

如果你使用了模板引擎(如 koa-views, ejs, nunjucks等),并调用 ctx.render('index', { ... })来渲染页面,那么:

模板引擎只能访问你​​显式传递给模板的变量​​,以及某些中间件自动注入的变量(比如 ctx.state)。

​​app.context上的变量默认不会自动传给模板!​​

也就是说,你如果在 app.context上定义了:

app.context.siteName = '我的网站';

然后在模板中直接写:

<h1><%= siteName %></h1>   <!-- 不会生效! -->

这是 ​​无效的​​,因为模板引擎并不知道 siteName是从哪里来的,除非你手动传递它。

三、如何在模板中使用 app.context的变量?

方法 1:​​手动传递到模板中(推荐灵活控制)​​

你可以在渲染时,手动将 app.context上的变量传进去:

app.use(async ctx => {
  await ctx.render('index', {
    title: '首页',
    siteName: ctx.siteName,      // 手动从 app.context (也就是 ctx) 中传递
    currentYear: ctx.currentYear
  });
});

模板中就可以用了:

<h1><%= title %></h1>
<p>网站名称:<%= siteName %></p>
<p>当前年份:<%= currentYear %></p>

方法 2:​​通过中间件将 app.context的变量挂载到 ctx.state(推荐更优雅的全局方案)​​

由于大多数模板引擎(比如 koa-views)会自动将 ctx.state上的变量注入到模板中,你可以写一个​​中间件​​,在每个请求中将 app.context的某些属性复制到 ctx.state中:

// 中间件:将 app.context 的某些全局变量赋值到 ctx.state
app.use(async (ctx, next) => {
  ctx.state.siteName = ctx.siteName;         // 来自 app.context
  ctx.state.currentYear = ctx.currentYear;
  await next();
});

然后你就可以在模板中直接使用 ctx.state中的变量,而不用每次渲染都手动传:

<h1><%= title %></h1>
<p>网站名称:<%= siteName %></p>        <!-- 来自 ctx.state -->
<p>当前年份:<%= currentYear %></p>

并且你的路由代码可以更干净:

app.use(async ctx => {
  await ctx.render('index', {
    title: '首页'
    // 不需要再传 siteName 和 currentYear,因为它们已经在 ctx.state 中了
  });
});

方法 3:​​直接挂载到 ctx.state全局中间件(更推荐)​​

更常见的做法是:​​不直接使用 app.context存全局变量,而是使用中间件将全局变量统一设置到 ctx.state中​​,这样更灵活,也更容易维护。

比如:

// 全局中间件设置 ctx.state
app.use(async (ctx, next) => {
  ctx.state.siteName = '我的网站';         // 直接定义,不依赖 app.context
  ctx.state.currentYear = new Date().getFullYear();
  await next();
});

这样你完全不用碰 app.context,而且在所有模板中都可以通过 <%= siteName %>直接访问(因为模板引擎自动注入 ctx.state)。