.3-浅析express源码之applicaiton模块(2)-app.render
这个模块还漏了一个稍微复杂点的API,就是app.render,首先看官网的定义:
app.render(view, [locals], callback)
view为对应的文件名,locals为一个配置对象,callback为解析完成的回调函数。
涉及到的全局属性有
view:默认为一个内置模块,负责解析文件路径与获取对应文件后缀的parser
views:默认为process() + '/views',一个字符串或数组,搜索对应文件路径时的目录
view engine:默认解析引擎,需要自定义
另外,locals配置对象既可以提供render相关的设置,也可以提供渲染模板所需要的参数。
app.render
首先看一眼render主函数:
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;
// 参数修正
if (typeof options === 'function') {
done = options;
opts = {};
}
// 参数合并
merge(renderOptions, this.locals);
if (opts._locals) {
merge(renderOptions, opts._locals);
}
merge(renderOptions, opts);
// 设置缓存
if (renderOptions.cache == null) {
renderOptions.cache = this.enabled('view cache');
}
// 尝试获取缓存
if (renderOptions.cache) {
view = cache[name];
}
// 尝试获取文件绝对路径
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);
}
// 设置缓存
if (renderOptions.cache) {
cache[name] = view;
}
}
// 渲染
tryRender(view, renderOptions, done);
};
结构直接清晰,稍微说一下。
1、第二个配置对象参数是可选的
2、若未定义cache属性,是否缓存保持与全局缓存属性一致
3、view属性基本上不需要自己定义,因为看起来挺麻烦的
4、最后的tryRender方法来源于view属性的原型方法,可能为了拓展才分割出来
function tryRender(view, options, callback) {
try {
view.render(options, callback);
} catch (err) {
callback(err);
}
}
这里以express-generator的demo来说明一下,在生成的目录中,app.js涉及的相关代码如下:
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
设置了默认解析引擎为jade,默认文件目录为views文件夹。
然后假设调用代码如下:
app.render('index', { title: 'Express' }, callback);
先不管callback是什么,进入内置view模块。
function View(name, options) {
var opts = options || {};
// 获取参数
this.defaultEngine = opts.defaultEngine;
this.ext = extname(name);
this.name = name;
this.root = opts.root;
/**
* 有两种方式指定文件后缀
* 1.name参数提供完整的文件名+后缀
* 2.提前设置默认解析引擎view engine参数
*/
if (!this.ext && !this.defaultEngine) {
throw new Error('No default engine was specified and no extension was provided.');
}
var fileName = name;
/**
* 文件无后缀时会拼接默认解析引擎与文件名
*/
if (!this.ext) {
// jade + . => .jade
this.ext = this.defaultEngine[0] !== '.' ?
'.' + this.defaultEngine :
this.defaultEngine;
// index + .jade => index.jade
fileName += this.ext;
}
// 无对应引擎模块时
if (!opts.engines[this.ext]) {
// 获取后缀
var mod = this.ext.substr(1)
debug('require "%s"', mod)
/**
* 引进对应模块
* 比如jade => fn = requore('jade').__express
*/
var fn = require(mod).__express
if (typeof fn !== 'function') {
throw new Error('Module "' + mod + '" does not provide a view engine.')
}
opts.engines[this.ext] = fn
}
// 将引擎解析模块存入本地属性
this.engine = opts.engines[this.ext];
// 搜索路径
this.path = this.lookup(fileName);
}
基本上信息都写在注释里了,稍微提一下,最佳的实践就是在文件名直接给出对应的后缀,并且提前在全局属性设置并引入解析引擎,这样在生成对应的view时会省去很多的时间。
完成文件名拼接与解析模块引入后,会进行文件的路径搜素,由于设置了指定目录,所以这一步也就很简单了,源码如下:
View.prototype.lookup = function lookup(name) {
var path;
// 还特地跑去查了一下 这个方法接受字符串 老了……
var roots = [].concat(this.root);
debug('lookup "%s"', name);
// 遍历所有的本地目录
for (var i = 0; i < roots.length && !path; i++) {
var root = roots[i];
// 拼接文件名与目录
var loc = resolve(root, name);
var dir = dirname(loc);
var file = basename(loc);
path = this.resolve(dir, file);
}
return path;
};
这个就太简单了。
以jade为例,可以稍微看一眼解析入口函数:
exports.render = function(str, options, fn) {
// 参数修正
if ('function' == typeof options) {
fn = options, options = undefined;
}
if (typeof fn === 'function') {
var res
try {
// 解析文件
res = exports.render(str, options);
} catch (ex) {
return fn(ex);
}
// 调用callback 第二参数为解析后的字符串
return fn(null, res);
}
options = options || {};
// cache requires .filename
if (options.cache && !options.filename) {
throw new Error('the "filename" option is required for caching');
}
// parse...
return handleTemplateCache(options, str)(options);
};
总的来说,就是根据文件的绝对路径、参数对象返回一个解析后的html字符串,作为callback的第二个参数返回。
至此,render函数的过程解析完毕。
.3-浅析express源码之applicaiton模块(2)-app.render的更多相关文章
- .2-浅析express源码之applicaiton模块(1)-咸鱼方法
上一节讲了express的入口文件,当执行主函数,会调用app.init方法,这个方法就来源于application模块. 这个模块有很多方法,目前仅仅过一下初始化方法: app.init = fun ...
- .4-浅析express源码之applicaiton模块(3)-compile函数
基本上application模块的api都看的差不多了,但是在app.set中还有一个遗漏点,如下: app.set = function set(setting, val) { // ...设值 / ...
- .7-浅析express源码之Router模块(3)-app[METHODS]
之前的讨论都局限于use方法,所有方式的请求会被通过,这一节讨论express内部如何处理特殊请求方法. 给个流程图咯~ 分别给出app.METHODS与router.METHODS: // app. ...
- .5-浅析express源码之Router模块(1)-默认中间件
模块application已经完结,开始讲Router路由部分. 切入口仍然在application模块中,方法就是那个随处可见的lazyrouter. 基本上除了初始化init方法,其余的app.u ...
- .6-浅析express源码之Router模块(2)-router.use
这一节继续深入Router模块,首先从最常用的use开始. router.use 方法源码如下: proto.use = function use(fn) { var offset = 0; var ...
- express源码剖析--Router模块
1.加载模块执行代码: methods.forEach(function(method){ //method是http协议的各种请求方法,如:get,put,headee,post Route.pro ...
- express源码分析之Router
express作为nodejs平台下非常流行的web框架,相信大家都对其已经很熟悉了,对于express的使用这里不再多说,如有需要可以移步到www.expressjs.com自行查看express的 ...
- nginx源码分析之模块初始化
在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...
- 读Zepto源码之Event模块
Event 模块是 Zepto 必备的模块之一,由于对 Event Api 不太熟,Event 对象也比较复杂,所以乍一看 Event 模块的源码,有点懵,细看下去,其实也不太复杂. 读Zepto源码 ...
随机推荐
- java解决共享资源竞争
由于多线程的实现,在运行一个程序的时候可能会有很多的线程在同时运行,但是线程的调度并不是可见的,所以不会知道一个线程什么时候在运行,比如说 你坐在桌子前手拿着叉子,正要去叉盘中的最后一片食物,当你的叉 ...
- 《Python黑帽子:黑客与渗透测试编程之道》 Windows系统提权
环境准备: pywin32的安装在第八章的键盘记录中有,这里还需要安装wmi: 在本人的32位win7上本来是没有easy_install这个命令的,这需要安装setuptools-0.6c11.wi ...
- 将Python项目打包成EXE可执行文件(单文件,多文件,包含图片)
解决 将Python项目打包成EXE可执行文件(单文件,多文件,包含图片) 1.当我们写了一个Python的项目时,特别是一个GUI项目,我们特备希望它能成为一个在Windows系统可执行的EXE文件 ...
- ProxySQL 部署 Single Writer Failover 读写分离 (PXC)
主机信息: Proxysql: 如果你忽略了ProxySQL会报告主机组的变化,我建议把它设置为0,除非你试图调试"某些东西",否则你的日志将很快变得巨大.UPDATE globa ...
- Cesium Vue开发环境搭建
最近被问到如何在 vuejs 中集成 cesium,首先想到的官网应该有教程.官网有专门讲 Cesium and Webpack(有坑),按照官网的说明,动手建了一个Demo,在这记录下踩坑过程. 一 ...
- 线程中的同步辅助类CountDownLatch
四个类可协助实现常见的专用同步语句.Semaphore 是一个经典的并发工具.CountDownLatch 是一个极其简单但又极其常用的实用工具,用于在保持给定数目的信号.事件或条件前阻塞执行.Cyc ...
- (转)Cognos的下载地址分享
原文:https://blog.csdn.net/Wikey_Zhang/article/details/76138965 刚开始接触Cognos,发现Cognos真是一款挺不错的报表工具,先分享一下 ...
- C#:使用ListView动态添加数据一直闪烁的解决办法
首先,自定义一个类ListViewNF,继承自 System.Windows.Forms.ListView using System; using System.Collections.Generic ...
- freerdp服务器共享屏幕,skype lync终端显示黑屏的原因分析
问题描述:freerdp支持远程桌面共享协议rdp,使用freerdp与skype终端进行远程桌面共享时.发送1080p 视频数据时 skype终端显示黑屏 经过分析,发现rdp协商参数大于一定值时, ...
- System.Threading.Tasks.Task 引起的 IIS 应用池崩溃
接口服务运行一段时间后,IIS应用池就会突然挂掉,事件查看日志,会有事件日志Event ID为5011的错误 为应用程序池“PokeIn”提供服务的进程在与 Windows Process Activ ...