express res.locals app.locals app.set render.opts原始碼分析

杜嘉偉發表於2019-05-07

express中為模板傳入變數的時候可以通過:

1.res.render的options:

 通過向res.render函式傳入optinos:res.render(目標模板名,{user:'djw'});

在EJS模版中通過<%=user%>使用變數

2.res.locals:

在res.locas物件上直接賦值:res.locals.user = 'djw'

3.app.locals

在res.locas物件上直接賦值:res.locals.user = 'djw'

4.app.set

通過向app.set函式傳入變數名稱和值:app.set('user','djw');這種方式會向模板中傳入一個程式級變數Settings,在EJS模板中通過<%=settings.user%>使用變數


res.render的opts 、res.locals、app.locals的優先順序:

由於通過app.set設定的變數其實只掛在app.locals.settings上面的,所以不用考慮優先順序設定。

express res.locals app.locals app.set render.opts原始碼分析

為什麼是這種優先順序呢?

在呼叫res.render中會先把res.locals掛到res.render的options上①,然後呼叫app.render②:

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);                                            ②

};

在app.render建立renderOptions然後依次把app.locals,res.locals,res.render的optionsmerge到renderOptions上。

先看下merge的實現:

exports = module.exports = function(a, b){
if (a && b) {
for (var key in b) {
a[key] = b[key];
}
}
return a;
};

merge把b上的屬性賦值到a上,所以後merge的會覆蓋之前已存在的屬性,例如:app.locals.user=‘djw’會先merge到renderOptions上,之後res.locals.user=‘kkp’會後覆蓋掉已存在的user。這樣就形成了res.render的options>res.locals>app.locals的優先順序。

原始碼:

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 options._locals就是掛在的render的options上的res.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);
};

之後tryRender通過app.set('view',fn)設定的模板引擎進行渲染。

引用:

1.《node.js實戰》圖片

​ ​2.express.原始碼


相關文章