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 相关文章推荐
JQuery live函数
Dec 24 Javascript
浅谈Jquery为元素绑定事件
Apr 27 Javascript
javascript中递归函数用法注意点
Jul 30 Javascript
JavaScript数组合并的多种方法
May 22 Javascript
移动端点击图片放大特效PhotoSwipe.js插件实现
Aug 25 Javascript
谈谈Vue.js——vue-resource全攻略
Jan 16 Javascript
在Vue组件中获取全局的点击事件方法
Sep 06 Javascript
用vue-cli开发vue时的代理设置方法
Sep 20 Javascript
layDate插件设置开始和结束时间
Nov 15 Javascript
微信JSSDK实现打开摄像头拍照再将相片保存到服务器
Nov 15 Javascript
TypeScript高级用法的知识点汇总
Dec 17 Javascript
Vue-axios-post数据后端接不到问题解决
Jan 09 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 文章调用类代码
2011/08/11 PHP
基于php验证码函数的使用示例
2013/05/03 PHP
PDO防注入原理分析以及使用PDO的注意事项总结
2014/10/23 PHP
PHPCMS忘记后台密码的解决办法
2016/10/30 PHP
php 如何设置一个严格控制过期时间的session
2017/05/05 PHP
php mysql PDO 查询操作的实例详解
2017/09/23 PHP
php利用云片网实现短信验证码功能的示例代码
2017/11/18 PHP
php和vue配合使用技巧和方法
2019/05/09 PHP
Laravel项目中timeAgo字段语言转换的改善方法示例
2019/09/16 PHP
PHP Beanstalkd消息队列的安装与使用方法实例详解
2020/02/21 PHP
用JavaScript实现仿Windows关机效果
2007/03/10 Javascript
javascript下利用arguments实现string.format函数
2010/08/24 Javascript
jquery zTree异步加载简单实例分享
2013/02/05 Javascript
从对象列表中获取一个对象的方法,依据关键字和值
2017/09/20 Javascript
使用cookie绕过验证码登录的实现代码
2017/10/12 Javascript
使用Vue自定义指令实现Select组件
2018/05/24 Javascript
Webpack之tree-starking 解析
2018/09/11 Javascript
JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例
2018/12/14 Javascript
详解微信图片防盗链“此图片来自微信公众平台 未经允许不得引用”的解决方案
2019/04/04 Javascript
php结合js实现多条件组合查询
2019/05/28 Javascript
vue 路由懒加载中给 Webpack Chunks 命名的方法
2020/04/24 Javascript
vue绑定数字类型 value为数字的实例
2020/08/31 Javascript
python获取当前计算机cpu数量的方法
2015/04/18 Python
序列化Python对象的方法
2020/08/01 Python
用html5的canvas和JavaScript创建一个绘图程序的简单实例
2016/07/06 HTML / CSS
Nike意大利官网:Nike.com IT
2020/01/19 全球购物
KOHLER科勒美国官网:国际著名卫浴橱柜领先品牌
2020/06/27 全球购物
4s客服专员岗位职责
2013/12/01 职场文书
单位门卫岗位职责
2013/12/20 职场文书
信息学院毕业生自荐信范文
2014/03/04 职场文书
师范生自荐信模板
2014/05/28 职场文书
学校群众路线专项整治方案
2014/10/31 职场文书
2014年机关后勤工作总结
2014/12/16 职场文书
个人先进材料范文
2014/12/30 职场文书
档案接收函格式
2015/01/30 职场文书
陶瓷类经典广告语集锦
2019/10/25 职场文书