Egg View 插件開發(fā)

2020-02-06 14:12 更新

絕大多數(shù)情況,我們都需要讀取數(shù)據(jù)后渲染模板,然后呈現(xiàn)給用戶,而框架并不強(qiáng)制使用某種模板引擎,由開發(fā)者來自行選型,具體參見模板渲染。

本文將闡述框架對 View 插件的規(guī)范約束, 我們可以依此來封裝對應(yīng)的模板引擎插件。以下以 egg-view-ejs 為例。

插件目錄結(jié)構(gòu)

egg-view-ejs
├── config
│ ├── config.default.js
│ └── config.local.js
├── lib
│ └── view.js
├── app.js
├── test
├── History.md
├── README.md
└── package.json

插件命名規(guī)范

  • 遵循插件開發(fā)規(guī)范
  • 插件命名約定以 egg-view- 開頭
  • package.json 配置如下,插件名以模板引擎命名,比如 ejs{ "name": "egg-view-ejs", "eggPlugin": { "name": "ejs" }, "keywords": [ "egg", "egg-plugin", "egg-view", "ejs" ],}
  • 配置項(xiàng)也以模板引擎命名// config/config.default.jsmodule.exports = { ejs: {},};

View 基類

接下來需提供一個(gè) View 基類,這個(gè)類會在每次請求實(shí)例化。

View 基類需提供 render 和 renderString 兩個(gè)方法,支持 generator function 和 async function(也可以是函數(shù)返回一個(gè) Promise)。render 方法用于渲染文件,而 renderString 方法用于渲染模板字符串。

以下為簡化代碼,可直接查看源碼

const ejs = require('ejs');

module.exports = class EjsView {

render(filename, locals) {
return new Promise((resolve, reject) => {
// 異步調(diào)用 API
ejs.renderFile(filename, locals, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}

renderString(tpl, locals) {
try {
// 同步調(diào)用 API
return Promise.resolve(ejs.render(tpl, locals));
} catch (err) {
return Promise.reject(err);
}
}
};

參數(shù)

render 方法的三個(gè)參數(shù)

  • filename: 是完整的文件的路徑,框架查找文件時(shí)已確認(rèn)文件是否存在,這里不需要處理
  • locals: 渲染所需的數(shù)據(jù),數(shù)據(jù)來自 app.locals,ctx.locals 和調(diào)用 render 方法傳入的。框架還內(nèi)置了 ctx,request, ctx.helper 這幾個(gè)對象。
  • viewOptions: 用戶傳入的配置,可覆蓋模板引擎的默認(rèn)配置,這個(gè)可根據(jù)模板引擎的特征考慮是否支持。比如默認(rèn)開啟了緩存,而某個(gè)頁面不需要緩存。

renderString 方法的三個(gè)參數(shù)

  • tpl: 模板字符串,沒有文件路徑。
  • locals: 同 render。
  • viewOptions: 同 render。

插件配置

根據(jù)上面的命名約定,配置名一般為模板引擎的名字,比如 ejs。

插件的配置主要來自模板引擎的配置,可根據(jù)具體情況定義配置項(xiàng),如 ejs 的配置。

// config/config.default.js
module.exports = {
ejs: {
cache: true,
}
};

helper

框架本身提供了 ctx.helper 供開發(fā)者使用,但有些情況下,我們希望對 helper 方法進(jìn)行覆蓋,僅在模板渲染時(shí)生效。

在模板渲染中,我們經(jīng)常會需要輸出用戶提供的 html 片段,通常需要使用 egg-security 插件提供的 helper.shtml 清洗下

<div>{{ helper.shtml(data.content) | safe }}</div>

但如上代碼所示,我們需要加上 | safe 來告知模板引擎,該 html 是安全的,無需再次 escape,直接渲染。

而這樣用起來比較麻煩,而且容易遺忘,所以我們可以封裝下:

  • 先提供一個(gè) helper 子類:
// {plugin_root}/lib/helper.js
module.exports = app => {
return class ViewHelper extends app.Helper {
// safe 由 [egg-view-nunjucks] 注入,在渲染時(shí)不會轉(zhuǎn)義,
// 否則在模板調(diào)用 shtml 會被轉(zhuǎn)義
shtml(str) {
return this.safe(super.shtml(str));
}
}
};
  • 在渲染時(shí)使用自定義的 helper
// {plugin_root}/lib/view.js
const ViewHelper = require('./helper');

module.exports = class MyCustomView {
render(filename, locals) {
locals.helper = new ViewHelper(this.ctx);

// 調(diào)用 Nunjucks render
}
}

具體代碼可查看

安全相關(guān)

模板和安全息息相關(guān),egg-security 也給模板提供了一些方法,模板引擎可以根據(jù)需求使用。

首先聲明對 egg-security 的依賴:

{
"name": "egg-view-nunjucks",
"eggPlugin": {
"name": "nunjucks",
"dep": [
"security"
]
}
}

此外,框架提供了 app.injectCsrf 和 app.injectNonce,更多可查看安全章節(jié)。

單元測試

作為一個(gè)高質(zhì)量的插件,完善的單元測試是必不可少的,我們也提供了很多輔助工具使插件開發(fā)者可以無痛的編寫測試,具體參見單元測試插件中的相關(guān)內(nèi)容。


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號