基于express的复杂应用——代码结构分层

2017-05-16 15:15:25

项目之初,我一般选择用 express 的脚手架工具 express-generator 生成目录结构,比较快捷,生成的文件结构也比较直观。

npm install express-generator -g // 全局安装  
express -e myapp // 创建工程,生成目录,使用ejs作为模板语言  

生成的目录结构如下:

├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
├── routes
│   ├── index.js
│   └── users.js
└── views
    ├── error.ejs
    ├── index.ejs
    └── layout.ejs
  • app.js 作为入口文件
  • public 存放静态资源
  • routes 存放路由文件
  • views 模板

对于功能比较 单一/复杂度较低 的应用,我们只需要增加一个 models 文件夹,将逻辑与数据操作封装后放置其中,然后在路由文件中引入即可,如: routes/index.js:

const getIndexlists = require('../modals/getIndexlists');

app.get('/list', function(req, res) {  
    getIndexlists(req, function(err, result){
        // 向models中模块传递参数,利用回调函数得到结果,然后操作res
        if(!err) res.render('list', result)
        ....
    })
});

models/getIndexlists.js

const mysql = require('mysql');  
const conf = require('../conf/db.js');  
// 连接数据库
const pool = mysql.createPool( conf.mysql );

module.exports = function(req, callback){  
    let id = req.params.id;
    pool.getConnection(function(err, connection){
        if(err){
            callback(err);
            return;
        }
        connection.query('select * from list where id='+"id",function(err, result){
            // 得到结果后回调
            callback(err, result)
        });
        //释放连接 
         connection.release();
    })
}

这时的项目结构长这样:

├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
├── routes
│   └── index.js
├── models
│   └── getIndexlists.js
└── views
    └── list

对于路由较为简单,功能较少的应用,这样分离路由与数据操作已经够用了。但是我们会发现,上述的示例中,路由直接传递参数给 model,model 与进项数据操作后直接返回结果给 route,然后 route 直接拿数据渲染 view 模板。然而通常我们拿到数据后需要对数据进行一系列的处理,或者是进行多个表的数据操作,将结果整合,从而得到我们期待的数据结构。那么这些操作我们应该放在那里呢,放在 model 中明显不合理,因为这明显不与数据库操作挂钩,那么放在 route 文件中呢?看着好像还可以。 这时我们的 route 文件大概是这样:

routes/index.js

const async = require('async');  
const getIndexlists = require('../modals/getIndexlists'),  
      getUserInfo = require('../modals/getIndexlists'),
      ...;

app.get('/list', function(req, res) {  
        // 多任务
        async.parallel({
           list: function(callback){
                getIndexlists(req, function(err, result){
                    // 拿到数据,进行格式处理后返回
                    result.data = result.data.map(function(item){
                        return item+1;
                    })
                    .....
                    callback(err, result)
                })
           },
           user: function(){
                getUserInfo(req, function(err, result){
                    ....数据处理
                    ....数据处理
                    callback(err, result)
                })
           }
           ...其余操作 
        },
        function(err, result){
            //最终返回结果
            if(!err){
                res.render('list', result);
            }
        })
    });

这时候我们的路由文件实际上已经变的臃肿了,不仅处理路由信息,还要处理多任务数据操作及数据格式问题,这样的形式写的再多一点,route 文件就很混乱了。这时候我们就会想把(任务处理、数据处理)逻辑处理给抽出来,放到 services 文件夹中,形成 route-service-model 的结构,route 负责单一的路由处理,service 负责逻辑处理,model 负责数据访问。这样每个文件各司其职,代码结构就会比较清晰。将 service 抽离出来的另一个好处是——可复用性,实际上很多数据处理的格式相同,我们可以把这些相同的操作封装到一个 service 文件中,在需要的地方直接调用即可,同理,对于 model 中的数据操作,我们也可以将常用的增删改查操作封装起来,提高代码的复用性。

这时我们的代码结构长这样:

├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
├── routes
│   └── index.js
├── models
│   └── getIndexlists.js
├── services
│   └── dealListData.js
└── views
    └── list

到此为止,我们的代码结构已经很清晰了,基本实现了高复用,低耦合的理念。 那么当我们的应用进一步复杂,我们的系统可能有好多功能模块,几十个路由,这时候出现的问题就是路由文件泰国庞大,所有的路由都集中在一个路由文件中,阅读调试起来相当不便。这是我们会想到将路由进行二级分化,主路由进行大方向的路由控制,将同一功能模块(路由相似)的路由集中到一个路由文件中。我们把自路由控制文件放置于 controller 文件夹中,这样路由的控制实际是 route-controller 这样的二次处理,减轻了主路由的负担,提高代码的可维护性与可读性。 到此为止,我们的express应用的运行流程是:route-controller-service-model

这样,即便是复杂的 express 应用,也会分解的条理清晰。当然在实际项目中我们还需要日志显示,那么我们最终的项目结构因该是长这样:

.
├── app.js
├── bin
│   └── www
├── conf
│   ├── db.js
│   └── log.js
├── controllers
│   ├── indexController.js
│   ├── homeController.js
│   ├── userController.js
│   ├── pageController.js
│   └── resultController.js
├── logs
├── models
│   ├── analyze.js
│   ├── create.js
│   ├── list.js
│   └── result.js
├── package.json
├── public
│   ├── fonts
│   ├── images
│   ├── javascripts
│   ├── stylesheets
│   └── uploads
├── routes
│   └── index.js
├── service
│   ├── delResult.js
│   ├── delUser.js
│   ├── getAnalyze.js
│   ├── getIndexLists.js
│   ├── mysql.js
│   ├── publishArticle.js
│   ├── ssoLogin.js
│   └── upload.js
└── views
    ├── index
    ├── home
    ├── user
    ├── page
    └── common

经验之谈,说的不对的地方,望大家指正。

附:创建二级路由
express.Router

可使用 express.Router 类创建模块化、可挂载的路由句柄。Router实例是一个完整的中间件和路由系统,因此常称其为一个 “mini-app”。

下面的实例程序创建了一个二级路由模块,定义了一些路由,并且将它们挂载至应用的主路由上。

二级路由 controllers/birds.js 内容如下:

var express = require('express');  
var router = express.Router();

// 定义网站主页的路由
router.get('/', function(req, res) {  
  res.send('Birds home page');
});
// 定义 about 页面的路由
router.get('/about', function(req, res) {  
  res.send('About birds');
});

module.exports = router;  

主路由 routes/index.js 内容:

var birds = require('./birds');  
...
app.use('/birds', birds);  

应用即可处理发自 /birds 和 /birds/about 的请求。

SASS

sass 安装 install ruby sudo gem install sass 申明变量 $value = 100px; $color = #fafafa; 编译 sass style.scss style.css 编译风格 * nested:嵌套缩进的css代码,它是默认值。 * expanded:没有缩进的、扩展的css代码。 * compact:简洁格式的css代码。 * compressed:压缩后的css代码。 sass --style compressed style.scss build.css 监听文件 // watch a file sass --watch input.scss:output.css // watch a directory

vue2+express前后端分离跨域session等问题

基于 vue2 + express 的 RBAC 角色权限验证前后端分离项目。前端vue2 负责路由控制,数据渲染,后端 express 负责数据库操作,RBAC权限控制。 前端: 脚手架:vue-cli 选型:vue2 + vuex + vue-router + axios + iview + ES6 后端 脚手架:express-generator 选型:express4 + mongodb 结构整合 使用 vue-cli 生成的项目,默认安装了简易的 express,该 express 的启动文件为build/dev-server.js 。执行 cnpm start 启动该 express,实际主要是启一个服务来运行 Vue。所以我们后端部分不以该文件为入口。 后端部分的目录使用express-generator来生成,由于我们做了前后端分离,