.2-淺析express源碼之applicaiton模組-app.render(2)

來源:互聯網
上載者:User

標籤:name   需要   base   amp   ==   try   分割   define   擷取檔案   

  這個模組還漏了一個稍微複雜點的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函數的過程解析完畢。

.2-淺析express源碼之applicaiton模組-app.render(2)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.