2013年12月10日

【翻译】Node.js+Express站点中的简易MVC架构

作者 非鱼

原文:http://timstermatic.github.io/blog/2013/08/17/a-simple-mvc-framework-with-node-and-express/

我喜欢框架。一旦我放弃了程序员的自我,学会了拥抱严格的约定,立即就在开发和部署的时间上感受到了好处。另一方面,我也喜欢了解引擎盖下是如何运转的,如果你看不到框架下面的东西,可能就会遇到危险。

这就是我为什么喜欢node.js和express。它们提供了一套框架样板,可以让我快速建立我自己的约定。

当然,使用express建立一个网站非常简单,而且,让express设置的更接近MVC也很简单。

MVC的通俗解释

当我第一次使用我的MVC框架(cakephp)的时候,我很受伤。不难看出作为一个模式,它为什么能够经受住时间的考验。你只需要知道这些:

  • M是指model,用来定义数据结构和与数据打交道的方法的地方。
  • V是指view,用来管理所有用户最终在屏幕上看到的东西的地方。
  • C是指controller,用来接收用户的请求,从model里取出数据,并传递给view的地方。

嗯,知道这些就够了。下面是我如何组织我的express项目,把它变成一个最基本的MVC结构的。

首先,在根目录下,我需要三个子目录:

  • models
  • views
  • controllers

来看看app.js:

var express = require('express');
      ,http = require('http');
      ,path = require('path');
      ,app = express();
      ,fs = require('fs');

// database connection
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/mydb');

// some environment variables
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// dynamically include routes (Controller)
fs.readdirSync('./controllers').forEach(function (file) {
  if(file.substr(-3) == '.js') {
      route = require('./controllers/' + file);
      route.controller(app);
  }
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

这里的内容大部分是express自动为你生成的,不过有几行内容比较重要。第5行我引入了fs模块,这样我们可以动态读取我们的controllers,然后在25到30行,我们读进controllers目录下所有的js文件。第13行也很重要,它告诉引擎我们的views模板保存在哪里。

现在如果我们往controllers目录里增加一个js文件,我们可以按照这个格式,让它包含一系列的routes,比如controllers/users.js

var mongoose = require('mongoose')
var Video = require('../models/user');
module.exports.controller = function(app) {
  app.get('/signup', function(req, res) {
      // any logic goes here
      res.render('users/signup')
  });
  app.get('/login', function(req, res) {
      // any logic goes here
      res.render('users/login')
  });
}

我让它加载了所有的models,因为有缓存机制,每个model实际只加载一次并缓存起来,所以这样没有什么负作用。 下面export了一个controller函数,其实包含我们所有的routes。注意我们使用view的方式:res.render(‘users/signup’),这意味着我们要把文件放在views/users/signup.jade。

最后,models/user.js的内容是这样的:

var mongoose = require('mongoose')
      ,Schema = mongoose.Schema
      userSchema = new Schema( {
          username: String,
          password: String
      }),
User = mongoose.model('user', userSchema);

module.exports = User;

我喜欢用这种方式,用controller来融合model和view,这才是MVC的灵魂所在。这样组织文件还有个巨大的好处,如果我要修改users/signup页面,我立即知道我的model在models/user.js,route在controllers/users.js,页面在views/users/signup.jade。

这就是为什么MVC是这么棒的一个模式。