Express (node.js) 中的多个视图路径

50
我正在使用Node.js和Express框架编写一个CMS。在我的CMS中,我有几个模块用于处理用户、页面等等。
我希望每个模块都有自己的文件夹,包括视图文件。
有人知道我该如何实现这个目标吗?
我目前使用swig作为我的模板引擎,但如果有其他更好的选择,我也可以考虑替换它。

1
像这样的东西对你有用吗? https://dev59.com/nGox5IYBdhLWcg3wDgEz - Timothy Strimple
http://stackoverflow.com/questions/36374424/mean-app-with-admin-panel-and-client-panel?answertab=active#tab-top - Muhammad Shahzad
6个回答

57

最后更新

自Express 4.10版起,该框架支持多视图文件夹功能。

只需将一个文件夹路径数组传递给views属性即可。

app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);

Express 2.0

据我所知,目前express不支持多个视图路径或命名空间(例如静态中间件)。

但你可以修改查找逻辑,使其按照你想要的方式工作,例如:

function enableMultipleViewFolders(express) {
    // proxy function to the default view lookup
    var lookupProxy = express.view.lookup;

    express.view.lookup = function (view, options) {
        if (options.root instanceof Array) {
            // clones the options object
            var opts = {};
            for (var key in options) opts[key] = options[key];

            // loops through the paths and tries to match the view
            var matchedView = null,
                roots = opts.root;
            for (var i=0; i<roots.length; i++) {
                opts.root = roots[i];
                matchedView = lookupProxy.call(this, view, opts);
                if (matchedView.exists) break;
            }
            return matchedView;
        }

        return lookupProxy.call(express.view, view, options)
    };
}

通过调用上述函数并将express作为参数传递,您将启用新逻辑,然后您将能够指定视图数组到配置中:

var express = require('express');
enableMultipleViewFolders(express);
app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);

或者,如果你愿意,你可以直接修改框架(更新其中的view.js文件)

这应该在Express 2.x中可行,不确定是否适用于新版本(3.x)

更新

不幸的是,上面的解决方案在Express 3.x中将不起作用,因为express.view将会是undefined

另一种可能的解决方案是代理response.render函数,并设置视图文件夹配置,直到它找到匹配项:

var renderProxy = express.response.render;
express.render = function(){
    app.set('views', 'path/to/custom/views');
    try {
        return renderProxy.apply(this, arguments);
    }
    catch (e) {}
    app.set('views', 'path/to/default/views');       
    return renderProxy.apply(this, arguments);
};

我没有测试过,对我来说感觉很hacky,不幸的是这个特性已经被推迟了: https://github.com/visionmedia/express/pull/1186

更新2

这个特性已经在Express 4.10中添加,因为以下pull request已合并: https://github.com/strongloop/express/pull/2320


1
@Feng - 从文档中似乎并不明显。它只说views被设置为单个目录。你能否指出一个地方、文档或代码,说明它支持多个目录? - Vagmi Mudumbai
1
@VagmiMudumbai 刚刚注意到这是几天前添加的,已经在4.10中上线了,而不是4.x https://github.com/strongloop/express/pull/2320 - Feng
@Feng - 太好了。谢谢你告诉我。 - Vagmi Mudumbai
我遇到了这个错误 TypeError: Cannot read property 'lookup' of undefined,我该如何解决? - Muhammad Shahzad
大家好,我有一个问题。我有两个视图目录。那么我可以在每个目录中创建layout.ejs文件吗?一个目录一个layout.ejs。我可以这样做吗? - AquariusPotter
显示剩余7条评论

11

除了@user85461的答案外,require view部分对我没有起作用。 我的做法是:删除路径内容并将其全部移动到一个我可以要求的模块中, patch.ViewEnableMultiFolders.js(适用于当前的express):

function ViewEnableMultiFolders(app) {
    // Monkey-patch express to accept multiple paths for looking up views.
    // this path may change depending on your setup.
    var lookup_proxy = app.get('view').prototype.lookup;

    app.get('view').prototype.lookup = function(viewName) {
        var context, match;
        if (this.root instanceof Array) {
            for (var i = 0; i < this.root.length; i++) {
                context = {root: this.root[i]};
                match = lookup_proxy.call(context, viewName);
                if (match) {
                    return match;
                }
            }
            return null;
        }
        return lookup_proxy.call(this, viewName);
    };
}

module.exports.ViewEnableMultiFolders = ViewEnableMultiFolders;

并使用:

var Patch = require('patch.ViewEnableMultiFolders.js');
Patch.ViewEnableMultiFolders(app);
app.set('views', ['./htdocs/views', '/htdocs/tpls']);

5

您可以将所有视图文件放入“view”文件夹中,但是需要将每个模块的视图分别放在“view”文件夹的各自文件夹中。因此,结构大致如下:

views  
--moduleA    
--moduleB  
----submoduleB1  
----submoduleB2  
--moduleC  

像平常一样设置视图文件:

app.set('views', './views');

每个模块渲染时,要包含模块的名称:
res.render('moduleA/index', ...);

甚至子模块的名称:
res.render('moduleB/submoduleB1/index', ...);

这个解决方案也适用于 Express 4.x 之前的版本。


5
这里是Express 3.x的解决方案。它通过猴子补丁的方式修改了Express 3.x的“View”对象,以执行与@ShadowCloud上面解决方案相同的查找技巧。不幸的是,View对象的路径查找不够简洁,因为3.x没有将其公开给express - 因此您必须深入挖掘node_modules的细节。
function enable_multiple_view_folders() {
    // Monkey-patch express to accept multiple paths for looking up views.
    // this path may change depending on your setup.
    var View = require("./node_modules/express/lib/view"),
        lookup_proxy = View.prototype.lookup;

    View.prototype.lookup = function(viewName) {
        var context, match;
        if (this.root instanceof Array) {
            for (var i = 0; i < this.root.length; i++) {
                context = {root: this.root[i]};
                match = lookup_proxy.call(context, viewName);
                if (match) {
                    return match;
                }
            }
            return null;
        }
        return lookup_proxy.call(this, viewName);
    };
}

enable_multiple_view_folders();

1

安装glob npm install glob

如果您有一个类似以下的views目录:

views
├── 404.ejs
├── home.ejs
├── includes
│   ├── header.ejs
│   └── footer.ejs
├── post
│   ├── create.ejs
│   └── edit.ejs
└── profile.ejs


你可以使用这个 glob 函数来返回 views 目录下的子目录数组(添加 path.substring 来移除末尾的 /)。
let viewPaths = glob.sync('views/**/').map(path => {
    return path.substring(0, path.length - 1)
})


console.log(viewPaths)
>> ['views', 'views/post', 'views/includes']

现在你可以设置

app.set('views', viewPaths)

现在你可以使用

res.render('404')
res.render('home')
res.render('post/edit')
res.render('post/create')

0
对于所有仍在寻找答案的人。在一个数组中提及路径。 例如:
app.set('views', [
path.join(path.resolve(), 'src', 'features', 'user', 'views'),
path.join(path.resolve(), 'src', 'features', 'home', 'view')]);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接