目錄約定規範
egg-project
├── package.json
├── app.js (可選)
├── agent.js (可選)
├── app
| ├── router.js
│ ├── controller
│ | └── home.js
│ ├── service (可選)
│ | └── user.js
│ ├── middleware (可選)
│ | └── response_time.js
│ ├── schedule (可選)
│ | └── my_task.js
│ ├── public (可選)
│ | └── reset.css
│ ├── view (可選)
│ | └── home.tpl
│ └── extend (可選)
│ ├── helper.js (可選)
│ ├── request.js (可選)
│ ├── response.js (可選)
│ ├── context.js (可選)
│ ├── application.js (可選)
│ └── agent.js (可選)
├── config
| ├── plugin.js
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可選)
| ├── config.local.js (可選)
| └── config.unittest.js (可選)
└── test
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
如上,由框架約定的目錄:
- app/router.js 用於配置 URL 路由規則,具體參見 Router。
- app/controller/** 用於解析用戶的輸入,處理後返回相應的結果,具體參見 Controller。
- app/service/** 用於編寫業務邏輯層,可選,建議使用,具體參見 Service。
- app/middleware/** 用於編寫中間件,可選,具體參見 Middleware。
- app/public/** 用於放置靜態資源,可選,具體參見內置插件 egg-static。
- app/extend/** 用於框架的擴展,可選,具體參見框架擴展。
- config/config.{env}.js 用於編寫配置文件,具體參見配置。
- config/plugin.js 用於配置需要加載的插件,具體參見插件。
- test/** 用於單元測試,具體參見單元測試。
- app.js 和 agent.js 用於自定義啓動時的初始化工作,可選,具體參見啓動自定義。關於agent.js的作用參見Agent機制。
框架內置 egg-view 作爲模板解決方案,並支持多模板渲染,每個模板引擎都以插件的方式引入,但保持渲染的 API 一致。如果想更深入的瞭解,可以查看模板插件開發。
以下以官方支持的 View 插件 egg-view-nunjucks 爲例
1. 引入 view 插件
$ npm i egg-view-nunjucks --save
2. 啓用插件
// config/plugin.js
exports.nunjucks = {
enable: true,
package: 'egg-view-nunjucks',
};
3. 配置插件
egg-view 提供了 config.view 通用配置
- root {String}
模板文件的根目錄,爲絕對路徑,默認爲 ${baseDir}/app/view。支持配置多個目錄,以 , 分割,會從多個目錄查找文件。
app/view下存放我們要渲染的html文件
如下示例演示瞭如何配置多個 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;
};
- mapping 和 defaultViewEngine
每個模板在註冊時都會指定一個模板名(viewEngineName),在使用時需要根據後綴來匹配模板名,比如指定 .nj 後綴的文件使用 Nunjucks 進行渲染。
module.exports = {
view: {
mapping: {
'.nj': 'nunjucks',
},
},
};
調用 render 渲染文件時,會根據上述配置的後綴名去尋找對應的模板引擎。
await ctx.render('home.nj');
必須配置文件後綴和模板引擎的映射,否則無法找到對應的模板引擎,但是可以使用 defaultViewEngine 做全局配置。
// config/config.default.js
module.exports = {
view: {
defaultViewEngine: 'nunjucks',
},
};
如果根據文件後綴沒有找到對應的模板引擎,會使用默認的模板引擎進行渲染。對於只使用一種模板引擎的應用,建議配置此選項。
- defaultExtension
一般在調用 render 時的第一個參數需要包含文件後綴,如果配置了 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',
});
}
}
- Locals
在渲染頁面的過程中,我們通常需要一個變量來收集需要傳遞給模板的變量,在框架裏面,我們提供了 app.locals 和 ctx.locals。
- app.locals 爲全局的,一般在 app.js 裏面配置全局變量。
- ctx.locals 爲單次請求的,會合並 app.locals。
- 可以直接賦值對象,框架在對應的 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` 合並進去。
ctx.app.locals = { a: 2 };
console.log(ctx.locals); // 上面已經合併過一次,故輸出還是 { a: 1, b: 2 }
// 也可以直接賦值整個對象,不用擔心會覆蓋前面的值,我們通過 setter 做了自動合併。
ctx.locals.c = 3;
ctx.locals = { d: 4 };
console.log(ctx.locals); // { a: 1, b: 2, c: 3, d: 4 }
但在實際業務開發中,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);