第二阶段说明
第二阶段,我们将使用oak_nest来搭建一个博客。本阶段仍不使用数据库。
目标:
- 用户注册、登陆、登出接口与页面交互
- 学会session
- 了解安全守卫
- 博客接口开发
- 留言接口开发
- 博客、留言页面交互
ejs简介
模板引擎(Template Engine)是一个将页面模板和数据结合起来生成 HTML 的工具。
ejs只是其中一种。语法可以参考其官网ejs.bootcss.com/。我们可以将模板拆分成一些组件,然后使用 ejs 的 include 方法将组件组合起来进行渲染。
现在流行的三大框架(vue、react、angular)无不包含自己的模板引擎。之所以没选择他们是因为它们的优势不在服务端渲染,也不是一个量级的产品。
在Deno可以直接使用esm.sh网站转换的地址(相当于复用了Node.js的包)——esm.sh/ejs@3.1.8?p…。
版本号一定要固定,而后面添加pin,表示固定该构建版本,因为esm服务器经常会更新,更新后就会重建所有模块,会影响Deno计算出来的hash值,进而影响deno lock文件的校验(前文说过,Deno lock现在不是很稳定,酌情使用,生产中自行验证)。
以下是根据字符串生成:
import { render } from "https://esm.sh/ejs@3.1.8?pin=v86";
const users = ["geddy", "neil", "alex"];
let res = render('<p>[?= users.join(" | "); ?]</p>', { users: users }, {
delimiter: "?",
openDelimiter: "[",
closeDelimiter: "]",
});
console.log(res); // => '<p>geddy | neil | alex</p>'
或者读取文件渲染:
import { renderFile } from "https://esm.sh/ejs@3.1.8?pin=v86";
const res2 = await renderFile(
"./template.ejs",
{
title: "from test2",
name: "world",
age: 18,
},
{
cache: true,
filename: "template",
},
);
console.log(res2);
vscode插件
在vscode安装一个插件EJS Beautify:
安装成功后,在.vscode/settings.json增加配置:
{
"emmet.includeLanguages": {
"ejs": "html",
},
"[html]": {
"editor.defaultFormatter": "j69.ejs-beautify"
},
}
这样编辑器就能使用它来进行格式化ejs文件了。不然缩进会有问题。
hello world
我们先不考虑博客的事情,先看我们第一个由ejs模板渲染出来的页面。
import_map.json
先在import_map.json中引入我们上面的ejs库:
"ejs": "esm.sh/ejs@3.1.8?p…",
其实现在也可以直接使用npm仓库,比如:
"ejs": "npm:ejs@3.1.8"
只不过,如果你在国内下载缓慢的话,可以考虑使用环境变量NPM_CONFIG_REGISTRY=https://registry.npmmirror.com/,将它添加到deno.jsonc的deno run命令前。本文就暂时先用之前的方案了。
global.ts
修改src/globals.ts,在Config中加一个meta属性:
export interface Config {
...
meta: {
title: string;
description: string;
};
}
config.yaml
修改config.yaml,新增一条:
meta:
title: Deno Blog
description: Deno Blog is a simple blog written in Deno.
ejs.ts
新建src/tools/ejs.ts,写个render函数,里面写死了渲染的ejs文件所在的目录(views),数据合并了配置文件和本地参数,以后者为准。
// deno-lint-ignore-file no-explicit-any
import { renderFile } from "ejs";
import globals from "../globals.ts";
export function render(
path: string,
data: Record<string, any>,
): Promise<string> {
return renderFile("views/" + path + ".ejs", {
...globals.meta,
...data,
}, {
cache: true,
filename: path,
});
}
这里第三个参数设置cache,其实是为了缓存ejs编译后的函数,当一个模板被反复利用时,适合开启它。
index.ejs
新建views/index.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" version="<%= version %>">
<title>
<%= title %>
</title>
</head>
<body>
<h1>
<%= title %>
</h1>
<p>
<%= description %>
</p>
</body>
</html>
app.controller.ts
修改src/app.controller.ts,把版本号返回给页面:
import { Controller, Get } from "oak_nest";
import { render } from "./tools/ejs.ts";
import { Logger } from "./tools/log.ts";
import globals from "./globals.ts";
@Controller("")
export class AppController {
constructor(private readonly logger: Logger) {}
@Get("/")
async version() {
const version = globals.version;
this.logger.info(`version: ${version}`);
return render("index", {
version,
});
}
}
验证
打开http://localhost:8000/,看下页面:
作业
思考下一个我们的博客有哪些功能设计。