Expess View 从指定烘托引擎开端
以 mustache 烘托引擎为例,需求初始化一些代码
const app = express()
app.set("view engine", "mustache");
app.engine("mustache", mustacheExpress());
app.set("views", toAbsolutePath("./views"));
- 指定视图引擎
- 指定引擎东西
- 指定视图方位
装置依赖
pnpm install mustache mustache-express
从 res.render 函数开端
render 函数接收两个参数,第一个 view 的途径,第二个烘托数据。
res.render = function render(view, options, callback) {
// 调用 app.render
app.render(view, opts, done);
};
下面是 app.render 代码完结
app.render = function render(name, options, callback) {
// view
if (!view) {
var View = this.get('view');
view = new View(name, {
defaultEngine: this.get('view engine'),
root: this.get('views'),
engines: engines
});
if (!view.path) {
var dirs = Array.isArray(view.root) && view.root.length > 1
? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
: 'directory "' + view.root + '"'
var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
err.view = view;
return done(err);
}
// prime the cache
if (renderOptions.cache) {
cache[name] = view;
}
}
// render
tryRender(view, renderOptions, done);
};
在 view 不在的情况下,会用 View 实例化 view, 最终调用 tryRender 函数,tryRender 函数会调用 view.render 办法:
View 的完结
module.exports = View;
function View(name, options) {
// store loaded engine
this.engine = opts.engines[this.ext];
// lookup path
this.path = this.lookup(fileName);
}
跟一般的构造函数一样,初始化一些属性,用传入的 opts 兼并一些属性。
- View 扩展办法
View.prototype.lookup = function lookup(name) {}
View.prototype.render = function render(options, callback) {
this.engine(this.path, options, callback);
}
View.prototype.resolve = function resolve(dir, file) {}
render 的详细完结就交给第三方引擎来完结了。
mustache 的render 办法的完结
Writer.prototype.render = function render (template, view, partials, config) {
var tags = this.getConfigTags(config);
var tokens = this.parse(template, tags);
var context = (view instanceof Context) ? view : new Context(view, undefined);
return this.renderTokens(tokens, context, partials, template, config);
};
render 函数调用 renderTokens 办法来解析,详细 renderTokens 办法的完结,就不做深化的分析了。
一个事例切图事例
需求是这样的,后端运用费 Node.js 开发,没有 JS 运转时,为了能够快速的完结项目,页面的切头由前端完结。此时页面多,任务重,React/Vue 这种现代结构,需求服务端烘托,后端不想用 Node.js,增加复杂度。由于前端 Node.js 能够运用模板烘托,并且模板种类许多,模板能够处理复用的问题,所以前端功能化能够处理,现代前端能结局的问题。
运用 exprss 服务 + mustache 模板引擎为基础完结一个简略的切图服务
- Express 创建服务和路由
- Nodemon 监听文件变化,重新启动路由
- esno + TypeScript + es Module 编写服务端代码
- prettier 格式化文件
在 express 中运用 mustache
import express from "express";
import mustacheExpress from "mustache-express";
app.engine("mustache", mustacheExpress());
app.set("view engine", "mustache");
app.set("views", toAbsolutePath("./views")); // 指定视图途径
- 烘托一个视图
app.get(url, async (_, res) => {
res.render(url, data);
});
mustache 拆分模板的基本能用法
- 界说模板文件
- 引用模板文件,以及引入文件下的模板的办法
- 在模板中运用变量
- 条件判断
- 列表烘托
mustache 官方 Github 仓库,需求研讨的能够自己访问学习,更多详细的用法。
示例
形成一个约定:由于只做简略的切图工作,view + data 文件运用 render 函数烘托的时分一一对应,这样就减少了许多的样板代码。
- main.server.ts
读取 views/
文件夹下的一切视图文件,布局文件不包含(简化),将 /static
目录设置为静态文件夹目录。路由不在单独的写了,此处一致处理为与视图相同命名用于简略的烘托。
// express
import express from "express";
import mustacheExpress from "mustache-express";
// config
import cutConfig from "./cut.config";
import defineVars from "iesmo";
// node
import { resolve } from "path";
import fs from "node:fs";
const { __dirname } = defineVars(import.meta);
export const toAbsolutePath = (p) => resolve(__dirname, p);
const routes = fs
.readdirSync(toAbsolutePath("./views/"))
.map((file) => {
if (!/\.mustache$/.test(file)) return null;
return file.replace(/\.mustache$/, "").toLowerCase();
})
.filter((i) => i !== null);
const app = express();
app.engine("mustache", mustacheExpress());
app.set("view engine", "mustache");
app.set("views", toAbsolutePath("./views"));
app.use("/static", express.static("static"));
routes.forEach((route) => {
let url = route === "index" ? "/" : `/${route}`;
app.get(url, async (_, res) => {
let data = (await import(`./data/${route}.ts`)).default;
res.render(route as string, data);
});
});
app.listen(cutConfig.port, () => {
console.log("server listening on port: ", cutConfig.port);
});
以 index.mustache 模板为示例:
数据存在 /data
文件夹下,默许输出一个 JS 目标
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{{title}}}</title>
{{#links}}
<link href="{{{href}}}" rel="stylesheet" />
{{/links}}
{{#scriptsHead}}
<script src="{{{src}}}"></script>
{{/scriptsHead}}
</head>
<body>
{{>tpls/list }}
{{>layout/footer}}
{{#scriptBody}}
<script src="{{{src}}}"></script>
{{/scriptBody}}
</body>
</html>
-
{{{title}}}
刺进数据 - 依据 html 烘托出数据
{{#links}}
<link href="{{{href}}}" rel="stylesheet" />
{{/links}}
- 运用文件夹中的模板
<body>
{{>tpls/list }}
{{>layout/footer}}
</body>
以上行为表明 tpls 目录下的 list 模板文件和 layout 目录下的 footer 模板文件
下面是一个详细的比如,运用到了 jQuery, Bootstrap 等等技术。能够自己测验一下,假如觉得还便利,能够给一个星星:
- static-cutouts-express
小结
- 运用 express + muchaste 模板示例。
- 在 express 运转过程中的 muchaste 调用流程,以及触及的 view 和 view 目标和烘托相关函数。
- 一个简略的示例 muchaste 语法在详细的示例中运用。