Node.js的Koa框架上手及MySQL操作指南


Posted in Javascript onJune 13, 2016

由 Express 原班人马打造的 koa,致力于成为一个更小、更健壮、更富有表现力的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升常用错误处理效率。Koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。

安装koa
koa 依赖支持 generator 的 Node 环境,也就是说,node的版本要在 0.11.9 或者更高,否则将无法执行。

用npm:

$ npm install koa

或者,选择安装在全局:

$ npm install -g koa

Example
这是一个koa的简单例子:

var koa = require('koa');
var app = koa();

// logger

app.use(function *(next){
 var start = new Date;
 yield next;
 var ms = new Date - start;
 console.log('%s %s - %s', this.method, this.url, ms);
});

// response

app.use(function *(){
 this.body = 'Hello World';
});

app.listen(3000);

与普通的 function 不同,generator functions 以 function* 声明。以这种关键词声明的函数支持 yield。在后面会讲到 yield 的用法和意义。

执行koa
执行koa时需要在 —-harmony 模式下运行,为了方便可以将 node 设置为默认启动 harmony 模式的别名:

alias node='node --harmony'

这样在执行相关js的时候就可以直接使用了。

Cascading
这是一个比较抽象的概念。Koa 中间件以一种非常传统的方式级联起来,也就是这里所谓的Cascading。

在以往的 Node 开发中,频繁使用回调不太便于展示复杂的代码逻辑,在 Koa 中,我们可以写出真正具有表现力的中间件。与 Connect 实现中间件的方法相对比,Koa 的做法不是简单的将控制权依次移交给一个又一个的中间件直到程序结束,Koa 执行代码的方式有点像回形针,用户请求通过中间件,遇到 yield next 关键字时,会被传递到下一个符合请求的路由(downstream),在 yield next 捕获不到下一个中间件时,逆序返回继续执行代码(upstream)。

下边这个例子展现了使用这一特殊方法书写的 Hello World 范例:一开始,用户的请求通过 x-response-time 中间件和 logging 中间件,这两个中间件记录了一些请求细节,然后「穿过」 response 中间件一次,最终结束请求,返回 「Hello World」。

当程序运行到 yield next 时,代码流会暂停执行这个中间件的剩余代码,转而切换到下一个被定义的中间件执行代码,这样切换控制权的方式,被称为 downstream,当没有下一个中间件执行 downstream 的时候,代码将会逆序执行。

var koa = require('koa');
var app = koa();

// x-response-time
app.use(function *(next){
 // (1) 进入路由
 var start = new Date;
 yield next;
 // (5) 再次进入 x-response-time 中间件,记录2次通过此中间件「穿越」的时间
 var ms = new Date - start;
 this.set('X-Response-Time', ms + 'ms');
 // (6) 返回 this.body
});

// logger
app.use(function *(next){
 // (2) 进入 logger 中间件
 var start = new Date;
 yield next;
 // (4) 再次进入 logger 中间件,记录2次通过此中间件「穿越」的时间
 var ms = new Date - start;
 console.log('%s %s - %s', this.method, this.url, ms);
});

// response
app.use(function *(){
 // (3) 进入 response 中间件,没有捕获到下一个符合条件的中间件,传递到 upstream
 this.body = 'Hello World';
});

app.listen(3000);

在上方的范例代码中,中间件以此被执行的顺序已经在注释中标记出来。你也可以自己尝试运行一下这个范例,并打印记录下各个环节的输出与耗时。

.middleware1 {
 // (1) do some stuff
 .middleware2 {
  // (2) do some other stuff
  .middleware3 {
   // (3) NO next yield !
   // this.body = 'hello world'
  }
  // (4) do some other stuff later
 }
 // (5) do some stuff lastest and return
}

上方的伪代码中标注了中间件的执行顺序,看起来是不是有点像 ruby 执行代码块(block)时 yield 的表现了?也许这能帮助你更好的理解 koa 运作的方式。

koa访问mysql数据库操作
实现方法一(co-mysql)
mysql库是以回调形式实现的,而koa中间件要求Promise形式,经过搜索,发现了co-mysql和mysql-co,这两个库的思路差不多,mysql-co封装度更高,并使用速度更快的mysql2,而co-mysql更简单,只是将mysql.query封装成Promise形式。下面是基于co-mysql的写法

var wrapper = require('co-mysql'),
 mysql = require('mysql');
var options = {
  host : 'localhost',
  port : 3306 ,
  database : 'test',
  user: 'root',
  password : 'rootroot'
};

var pool = mysql.createPool(options),
 p = wrapper(pool);

...
 var rows = yield p.query('SELECT 1');
 yield this.render('index', {
    title: rows[0].fieldName
  });
...
})();

实现方法二(promisify-node)
找到promisify-node库,可以将库整体转化为Promise形式,示例代码如下:

var promisify = require("promisify-node");
var db = promisify("myDbHelper");
...
var rows = yield db.getById('tableName', {id:1});
  yield this.render('index', {
    title: rows[0].fieldName
  });
...

实现方法三(thunkify、thunkify-wrap)

使用thunkify也能够完成封装,thunkify-wrap是一个增强版的thunkify,不过看说明,这种方法在未来的发展中可能会被淘汰,大概的使用如下:

var genify = require('thunkify-wrap').genify;
var db = genify("myDbHelper");
...
var rows = yield db.getById('tableName', {id:1});
  yield this.render('index', {
    title: rows[0].fieldName
  });
...

实现方法四(直接方法)
直接改造原来express下的代码为Promise形式,参考了co-mysql,并仔细学习了Promise相关知识,完成了已有代码的改造,代码及说明如下:
dbHelper.js

var config = require('./dbconfig');

var options = {
  'host': config.db_host,
  'port': config.db_port,
  'database': config.db_name,
  'user': config.db_user,
  'password': config.db_passwd
}

var mysql = require('mysql');
var pool = mysql.createPool(options);

//内部对mysql的封装,执行sql语句
function execQuery(sql, values, callback) {
  var errinfo;
  pool.getConnection(function(err, connection) {
    if (err) {
      errinfo = 'DB-获取数据库连接异常!';
      throw errinfo;
    } else {
      var querys = connection.query(sql, values, function(err, rows) {
        release(connection);
        if (err) {
          errinfo = 'DB-SQL语句执行错误:' + err;
          callback(err);
        } else {
          callback(null,rows);    //注意:第一个参数必须为null
        }
      });
    }
  });
}

function release(connection) {
  try {
    connection.release(function(error) {
      if (error) {
        console.log('DB-关闭数据库连接异常!');
      }
    });
  } catch (err) {}
}
//对外接口返回Promise函数形式
exports.getById = function(tablename, id){
  return new Promise(function(resolve, reject){
    var values = {id:id};
    var sql = 'select * from ?? where ?';
    execQuery(sql,[tablename, values], function(err, rows){
      if(err){
        reject(err);
      }else{
        resolve(rows);
      }
    })
  });
}
routes/index.js

var db = require("../dbHelper");
...
var rows = yield db.getById('tableName', {id:1});
  yield this.render('index', {
    title: rows[0].fieldName
  });
...

代码
请参考这个项目中的数据库操作部分,项目处于持续开发中,数据库示例部分取自该项目。
https://github.com/zhoutk/koadmin.git

Javascript 相关文章推荐
javascript 读取图片文件的大小
Jun 25 Javascript
js replace正则表达式应用案例讲解
Jan 17 Javascript
解决jquery版本冲突的有效方法
Sep 02 Javascript
Node.js连接postgreSQL并进行数据操作
Dec 18 Javascript
codeMirror插件使用讲解
Jan 16 Javascript
Bootstrap的popover(弹出框)在append后弹不出(失效)
Feb 27 Javascript
input输入框内容实时监测(附代码)
Aug 15 Javascript
详解Vue组件实现tips的总结
Nov 01 Javascript
使用vue-router设置每个页面的title方法
Feb 11 Javascript
angular4强制刷新视图的方法
Oct 09 Javascript
详解小程序中h5页面onShow实现及跨页面通信方案
May 30 Javascript
vue3.0 加载json的方法(非ajax)
Oct 26 Javascript
jQuery中的一些常见方法小结(推荐)
Jun 13 #Javascript
jQuery实现手机自定义弹出输入框
Jun 13 #Javascript
实例讲解JavaScript中的this指向错误解决方法
Jun 13 #Javascript
BootStrap智能表单实战系列(十一)级联下拉的支持
Jun 13 #Javascript
BootStrap 智能表单实战系列(十)自动完成组件的支持
Jun 13 #Javascript
BootStrap智能表单实战系列(九)表单图片上传的支持
Jun 13 #Javascript
浅谈JavaScript对象的创建方式
Jun 13 #Javascript
You might like
PHP.MVC的模板标签系统(一)
2006/09/05 PHP
几个有用的php字符串过滤,转换函数代码
2012/05/01 PHP
ThinkPHP使用UTFWry地址库进行IP定位实例
2014/04/01 PHP
PHP 7.0新增加的特性介绍
2017/06/08 PHP
怎么用javascript进行拖拽
2006/07/20 Javascript
JavaScript函数、方法、对象代码
2008/10/29 Javascript
Javascript 面向对象编程(一) 封装
2011/08/28 Javascript
自己封装的javascript事件队列函数版
2014/06/12 Javascript
基于jquery实现的鼠标悬停提示案例
2016/12/11 Javascript
AngularJS2中一种button切换效果的实现方法(二)
2017/03/27 Javascript
angularJS实现动态添加,删除div方法
2018/02/27 Javascript
解决vue一个页面中复用同一个echarts组件的问题
2020/07/19 Javascript
jquery实现简单自动轮播图效果
2020/07/29 jQuery
[01:06]DOTA2亚洲邀请赛专属珍藏-荧煌之礼
2017/03/24 DOTA
[01:13:51]TNC vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python是编译运行的验证方法
2015/01/30 Python
Python中map,reduce,filter和sorted函数的使用方法
2015/08/17 Python
python遍历文件夹下所有excel文件
2018/01/03 Python
python验证码识别教程之利用投影法、连通域法分割图片
2018/06/04 Python
Python从使用线程到使用async/await的深入讲解
2018/09/16 Python
python算法与数据结构之冒泡排序实例详解
2019/06/22 Python
安装python及pycharm的教程图解
2019/10/10 Python
Python turtle库绘制菱形的3种方式小结
2019/11/23 Python
升级keras解决load_weights()中的未定义skip_mismatch关键字问题
2020/06/12 Python
python实现测试工具(一)——命令行发送get请求
2020/10/19 Python
HTML5实现一个能够移动的小坦克示例代码
2013/09/02 HTML / CSS
阿玛尼美妆加拿大官方商城:Giorgio Armani Beauty加拿大
2017/10/24 全球购物
Speedo速比涛德国官方网站:世界领先的泳装品牌
2019/08/26 全球购物
函授生自我鉴定
2014/03/25 职场文书
党员演讲稿
2014/09/04 职场文书
中学生的1000字检讨书
2014/10/11 职场文书
2014年财务经理工作总结
2014/12/08 职场文书
15个值得收藏的JavaScript函数
2021/09/15 Javascript
使用RedisTemplat实现简单的分布式锁
2021/11/20 Redis
Python中的pprint模块
2021/11/27 Python
vue打包时去掉所有的console.log
2022/04/10 Vue.js