项目之初,我一般选择用 express 的脚手架工具 express-generator 生成目录结构,比较快捷,生成的文件结构也比较直观。
1 2 npm install express-generator -g // 全局安装 express -e myapp // 创建工程,生成目录,使用ejs作为模板语言
生成的目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ├── 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 文件夹,将逻辑与数据操作封装后放置其中,然后在路由文件中引入即可,如:
1 2 3 4 5 6 7 8 9 10 11 routes/index.js: const getIndexlists = require ('../modals/getIndexlists' );app.get('/list' , function (req, res ) { getIndexlists(req, function (err, result ) { if (!err) res.render('list' , result) .... }) });
models/getIndexlists.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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(); }) }
这时的项目结构长这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ├── 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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 中的数据操作,我们也可以将常用的增删改查操作封装起来,提高代码的复用性。
这时我们的代码结构长这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ├── 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 应用,也会分解的条理清晰。当然在实际项目中我们还需要日志显示,那么我们最终的项目结构因该是长这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 . ├── 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
内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 var express = require ('express' ); var router = express.Router();router.get('/' , function (req, res ) { res.send('Birds home page' ); }); router.get('/about' , function (req, res ) { res.send('About birds' ); }); module .exports = router;
主路由routes/index.js
内容:
1 2 3 var birds = require ('./birds' ); ... app.use('/birds' , birds);
应用即可处理发自/birds
和/birds/about
的请求。