Egg View 模板渲染

2020-02-06 14:11 更新

絕大多數(shù)情況,我們都需要讀取數(shù)據(jù)后渲染模板,然后呈現(xiàn)給用戶。故我們需要引入對應(yīng)的模板引擎。

框架內(nèi)置 egg-view 作為模板解決方案,并支持多模板渲染,每個模板引擎都以插件的方式引入,但保持渲染的 API 一致。如果想更深入的了解,可以查看模板插件開發(fā)。

以下以官方支持的 View 插件 egg-view-nunjucks 為例

引入 view 插件

$ npm i egg-view-nunjucks --save

啟用插件

// config/plugin.js
exports.nunjucks = {
enable: true,
package: 'egg-view-nunjucks',
};

配置插件

egg-view 提供了 config.view 通用配置

root {String}

模板文件的根目錄,為絕對路徑,默認(rèn)為 ${baseDir}/app/view。支持配置多個目錄,以 , 分割,會從多個目錄查找文件。

如下示例演示了如何配置多個 view 目錄:

// config/config.default.js
const path = require('path');
module.exports = appInfo => {
const config = {};
config.view = {
root: [
path.join(appInfo.baseDir, 'app/view'),
path.join(appInfo.baseDir, 'path/to/another'),
].join(',')
};
return config;
};

cache {Boolean}

模板路徑緩存,默認(rèn)開啟??蚣軙鶕?jù) root 配置的目錄依次查找,如果匹配則會緩存文件路徑,下次渲染相同路徑時不會重新查找。

mapping 和 defaultViewEngine

每個模板在注冊時都會指定一個模板名(viewEngineName),在使用時需要根據(jù)后綴來匹配模板名,比如指定 .nj 后綴的文件使用 Nunjucks 進(jìn)行渲染。

module.exports = {
view: {
mapping: {
'.nj': 'nunjucks',
},
},
};

調(diào)用 render 渲染文件時,會根據(jù)上述配置的后綴名去尋找對應(yīng)的模板引擎。

await ctx.render('home.nj');

必須配置文件后綴和模板引擎的映射,否則無法找到對應(yīng)的模板引擎,但是可以使用 defaultViewEngine 做全局配置。

// config/config.default.js
module.exports = {
view: {
defaultViewEngine: 'nunjucks',
},
};

如果根據(jù)文件后綴沒有找到對應(yīng)的模板引擎,會使用默認(rèn)的模板引擎進(jìn)行渲染。對于只使用一種模板引擎的應(yīng)用,建議配置此選項。

defaultExtension

一般在調(diào)用 render 時的第一個參數(shù)需要包含文件后綴,如果配置了 defaultExtension 可以省略后綴。

// config/config.default.js
module.exports = {
view: {
defaultExtension: '.nj',
},
};

// render app/view/home.nj
await ctx.render('home');

渲染頁面

框架在 Context 上提供了 3 個接口,返回值均為 Promise:

  • render(name, locals) 渲染模板文件, 并賦值給 ctx.body
  • renderView(name, locals) 渲染模板文件, 僅返回不賦值
  • renderString(tpl, locals) 渲染模板字符串, 僅返回不賦值
// {app_root}/app/controller/home.js
class HomeController extends Controller {
async index() {
const data = { name: 'egg' };

// render a template, path relate to `app/view`
await ctx.render('home/index.tpl', data);

// or manually set render result to ctx.body
ctx.body = await ctx.renderView('path/to/file.tpl', data);

// or render string directly
ctx.body = await ctx.renderString('hi, {{ name }}', data, {
viewEngine: 'nunjucks',
});
}
}

當(dāng)使用 renderString 時需要指定模板引擎,如果已經(jīng)定義 defaultViewEngine 這里可以省略。

Locals

在渲染頁面的過程中,我們通常需要一個變量來收集需要傳遞給模板的變量,在框架里面,我們提供了 app.locals 和 ctx.locals。

  • app.locals 為全局的,一般在 app.js 里面配置全局變量。
  • ctx.locals 為單次請求的,會合并 app.locals。
  • 可以直接賦值對象,框架在對應(yīng)的 setter 里面會自動 merge。
// `app.locals` 會合并到 `ctx.locals
ctx.app.locals = { a: 1 };
ctx.locals.b = 2;
console.log(ctx.locals); // { a: 1, b: 2 }

// 一次請求過程中,僅會在第一次使用 `ctx.locals` 時把 `app.locals` 合并進(jìn)去。
ctx.app.locals = { a: 2 };
console.log(ctx.locals); // 上面已經(jīng)合并過一次,故輸出還是 { a: 1, b: 2 }

// 也可以直接賦值整個對象,不用擔(dān)心會覆蓋前面的值,我們通過 setter 做了自動合并。
ctx.locals.c = 3;
ctx.locals = { d: 4 };
console.log(ctx.locals); // { a: 1, b: 2, c: 3, d: 4 }

但在實際業(yè)務(wù)開發(fā)中,controller 中一般不會直接使用這 2 個對象,直接使用 ctx.render(name, data) 即可:

  • 框架會自動把 data 合并到 ctx.locals。
  • 框架會自動注入 ctx, request, helper 方便使用。
ctx.app.locals = { appName: 'showcase' };
const data = { name: 'egg' };

// will auto merge `data` to `ctx.locals`, output: egg - showcase
await ctx.renderString('{{ name }} - {{ appName }}', data);

// helper, ctx, request will auto inject
await ctx.renderString('{{ name }} - {{ helper.lowercaseFirst(ctx.app.config.baseDir) }}', data);

注意:

  • ctx.locals 有緩存,只在第一次訪問 ctx.locals 時合并 app.locals。
  • 原 Koa 中的 ctx.state,由于容易產(chǎn)生歧義,在框架中被覆蓋為 locals,即 ctx.state 和 ctx.locals 等價,我們建議使用后者。

Helper

在模板中可以直接使用 helper 上注冊的方法,具體可以參見擴(kuò)展

// app/extend/helper.js
exports.lowercaseFirst = str => str[0].toLowerCase() + str.substring(1);

// app/controller/home.js
await ctx.renderString('{{ helper.lowercaseFirst(name) }}', data);

Security

框架內(nèi)置的 egg-security 插件,為我們提供了常見的安全輔助函數(shù),包括 helper.shtml / surl / sjs 等等等,強(qiáng)烈建議閱讀下安全


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號