當我們使用 Express 做服務端框架的時候,如果選擇一種類似於 EJS這種模板引擎渲染前端頁面的時候,經常服務端在響應 http 請求的時候呼叫 res.render({options})
去向模板中渲染資料。
我們還會經常看到 res.locals 和 app.locals 這物件,那麼他們到底是幹什麼的呢? 檢視原始碼:
//express/lib/response.js
res.render = function render(view, options, callback) {
var app = this.req.app;
var done = callback;
var opts = options || {};
var req = this.req;
var self = this;
// support callback function as second arg
if (typeof options === `function`) {
done = options;
opts = {};
}
// merge res.locals
opts._locals = self.locals;
// default callback to respond
done = done || function (err, str) {
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, opts, done);
};
變數 opts 初始時獲得開發人員程式碼中傳入的 options 物件,這段程式碼中的opts._locals = self.locals;
self 指向 res 物件自身,即又在最終渲染到模板中的變數裡面增添了一個物件屬性,該屬性的值為 res.locals
緊接著呼叫到了 app.render
函式,並傳入 opts 物件。
//express/lib/application.js
app.render = function render(name, options, callback) {
var cache = this.cache;
var done = callback;
var engines = this.engines;
var opts = options;
var renderOptions = {};
var view;
// support callback function as second arg
if (typeof options === `function`) {
done = options;
opts = {};
}
// merge app.locals
merge(renderOptions, this.locals);
// merge options._locals
if (opts._locals) {
merge(renderOptions, opts._locals);
}
// merge options
merge(renderOptions, opts);
// set .cache unless explicitly provided
if (renderOptions.cache == null) {
renderOptions.cache = this.enabled(`view cache`);
}
// primed cache
if (renderOptions.cache) {
view = cache[name];
}
// 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);
merge是一個很方便合併兩個物件的第三方模組。
不難看出 這函式在得出最終的渲染變數物件的時候又再一次 merge了app.locals物件,將開發人員 res.render 傳入的物件,res.locals 物件,app.locals 物件三者合併作為最終渲染模板的素材。
知道這個過程之後,很容易就能理解為什麼經常在專案入口檔案 app.js 中經常看到如下程式碼:
// 設定站點全域性常量
app.locals.webGlobal = {
title: "",
description: "",
keywords: ""
}
// 設定可變但是又是每個模板總是需要的一些模板變數
app.use(function(req, res, next) {
res.locals._host = req.headers.host;
res.locals._user = req.user;
res.locals._url = req.url || req.originalUrl;
res.locals._moment = moment;
res.locals._enums = enums;
next();
});
res.render 傳入的物件作為特定場景特定頁面的個性化變數資訊資料,app.locals
上通常掛載常量資訊,res.locals
上通常掛載變數資訊,三者相輔相成的共同構成渲染模板的整個變數資訊。