用法
在看koa-view源码之前先看下koa-view用法:
在使用koa-view时,必须安装您想要使用的引擎如ejs
var views = require('@ladjs/koa-views');
const render = views(__dirname + '/views', {
map: {
html: 'ejs'
}
})
// 注册中间件
app.use(render)
// 或者app.context.render = render()
app.use(async function (ctx) {
ctx.state = {
session: this.session,
title: 'app'
};
await ctx.render('user', {
user: 'John'
});
});
实现原理是,views函数调用会产生一个中间件函数render,在render中间件中会向ctx中添加render方法,这个render方法的调用就会解析对应的模板得到html
API
views(root, opts)函数有两个参数
- root: 必须是一个绝对路径,指定视图文件所在的目录。所有渲染的视图都是相对于这个路径的。
- opts (可选): 一个包含配置选项的对象。
配置选项 (opts):
- autoRender: 布尔值,决定是否使用
ctx.body来接收渲染后的模板字符串。默认值为true。
// 设置成false 后需要return出去
const render = views(__dirname, { autoRender: false, extension: 'pug' });
app.use(render)
app.use(async function (ctx) {
return await ctx.render('user.pug')
})
- extension: 视图文件的默认扩展名。使用此选项,您可以省略文件扩展名。
const render = views(__dirname, { extension: 'pug' })
app.use(render)
app.use(async function (ctx) {
await ctx.render('user') // 这里不需要写拓展名
})
- map: 将文件扩展名映射到特定的模板引擎。例如,将
.html文件映射到nunjucks引擎。
const render = views(__dirname, { map: { html: 'nunjucks' }});
app.use(render);
app.use(async function (ctx) {
await ctx.render('user.html');
});
- engineSource: 替换默认的
@ladjs/consolidate引擎源。一般不用改,关于@ladjs/consolidate下一节会介绍 - options: 这些选项将传递给视图引擎。模板引擎需要的options
koa-view的依赖包@ladjs/consolidate
@ladjs/consolidate 是一个 Node.js 的模板引擎集成库,它允许开发者使用多种模板引擎来渲染模板。@ladjs/consolidate 支持多种模板引擎,如 EJS。使用方法
const cons = require('@ladjs/consolidate');
// 使用ejs引擎,使用前请安装ejs
cons.ejs('views/page.html', { user: 'tobi' }, function(err, html) {
if (err) throw err;
console.log(html);
});
koa-view的依赖包koa-send
koa-send 它用于发送静态文件。这个库特别适用于当你需要在 Koa 应用中提供文件下载或服务静态文件时。
根路径 (root)
root 选项是必需的,它应该指定一个目录,从该目录提供文件服务。koa-send 会自动解析该路径,并去除前导的 /,以确保路径是相对的,并且不允许路径中包含 "..",以防止用户输入被串联。
示例用法
以下是一个简单的示例,展示了如何在 Koa 应用中使用 koa-send 来服务静态文件:
const send = require('koa-send');
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
if ('/' == ctx.path) return ctx.body = 'Try GET /package.json';
await send(ctx, ctx.path, { root: __dirname + '/public' });
});
app.listen(3000);
console.log('listening on port 3000');
在这个示例中,如果请求根路径 /,应用将返回一条消息提示尝试获取 package.json 文件。对于其他路径,koa-send 将尝试发送请求的文件。
源码注释
// 引入所需的npm包
const debug = require('debug')('koa-views'); // 调试模块,用于输出调试信息
const consolidate = require('@ladjs/consolidate'); // 模板引擎整合库
const send = require('koa-send'); // Koa文件发送中间件
const getPaths = require('get-paths'); // 用于获取模板文件路径的模块
const pretty = require('pretty'); // 美化HTML内容的模块
const resolve = require('resolve-path'); // 用于解析文件路径的模块
module.exports = viewsMiddleware; // 导出中间件
// 用于处理BigInt类型在JSON.stringify中的序列化问题
const bigIntReplacer = () => {
const seen = new WeakSet(); // 使用WeakSet来记录已经遍历过的对象
return (key, value) => {
if (typeof value === 'bigint') return value.toString(); // 将BigInt转换为字符串
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return; // 如果对象已经被遍历过,则跳过
}
seen.add(value); // 将对象添加到seen集合中
}
return value; // 返回原始值
}
};
// 定义viewsMiddleware中间件函数,它接受模板路径和其他配置选项
function viewsMiddleware(
path,
{
autoRender = true, // 是否自动渲染视图
engineSource = consolidate, // 模板引擎的来源,默认为consolidate
extension = 'html', // 默认的文件扩展名
options = {}, // 传递给模板引擎的额外选项
map // 用于指定不同文件扩展名对应的模板引擎
} = {}
) {
// 返回实际的Koa中间件函数
return function views(ctx, next) {
let extendsContext = false; // 标记是否扩展了应用上下文
// 定义render函数,用于渲染模板
function render(relPath, locals = {}) {
// 根据上下文确定ctx变量
if (extendsContext) {
if (this.ctx && this.ctx.req === this.req) ctx = this.ctx;
else ctx = this;
}
// 使用getPaths获取模板文件的完整路径
return getPaths(path, relPath, extension).then(paths => {
const suffix = paths.ext; // 获取文件扩展名
const state = Object.assign({}, options, ctx.state || {}, locals); // 创建state对象,包含所有选项和上下文状态
// 深度复制partials对象
state.partials = Object.assign(Object.create(null), options.partials || {});
// 如果debug开启,则输出调试信息
if (debug.enabled) debug('render `%s` with %s', paths.rel, JSON.stringify(state, bigIntReplacer()));
ctx.type = 'text/html'; // 设置响应类型为HTML
// 检查文件扩展名是否为html,如果是且没有map指定引擎,则直接发送文件
if (isHtml(suffix) && !map) {
return send(ctx, paths.rel, {
root: path
});
} else {
// 根据文件扩展名或map配置获取对应的模板引擎名称
const engineName = map && map[suffix] ? map[suffix] : suffix;
const render = engineSource[engineName]; // 获取对应的渲染函数
// 如果没有找到对应的引擎,则抛出错误
if (!engineName || !render)
return Promise.reject(
new Error(`Engine not found for the ".${suffix}" file extension`)
);
// 使用模板引擎渲染模板
return render(resolve(path, paths.rel), state).then(html => {
// 如果设置了pretty选项,则使用pretty包美化HTML
if (locals.pretty) {
debug('using `pretty` package to beautify HTML');
html = pretty(html);
}
// 如果设置了autoRender,则将渲染后的HTML设置为响应体
if (autoRender) {
ctx.body = html;
return html;
}
// 如果没有设置autoRender,则返回Promise解析后的HTML
return Promise.resolve(html);
});
}
});
}
// 如果ctx未定义,则设置extendsContext为true,并返回render函数
if (!ctx) {
extendsContext = true;
return render;
}
// 如果ctx已经定义了render函数,则直接执行下一个中间件
if (ctx.render) return next();
// 向ctx添加render方法,用于渲染模板
ctx.response.render = ctx.render = render;
// 执行下一个中间件
return next();
}
}
// 辅助函数,用于检查文件扩展名是否为html
function isHtml(ext) {
return ext === 'html';
}
概括一下:
koa-view返回一个函数,函数调用传入path和配置对象,返回值是view中间件函数,通过app.use注册中间件,此时在ctx中写入render函数:ctx.response.render = ctx.render = render;,当处理请求时,可以调用ctx.render处理模板,将处理后的模板返回会复制给body