Node.js-Common.js-Koa2

Amos Xu

Amos Xu

Front End Engineer|前端开发

Node.js-Common.js-Koa2

安装 全局

- npm install -g koa-generator

创建

koa2 -e #name#
#name#为项目名称

安装后进入目录 cd #name#

npm安装

npm install

开启

npm start// 启动
npm run dev// 可监听

简单的node.js的服务

//引入http模块
var http = require('http');
// 创建一个server服务
// (request表示获取页面url的信息,response表示给浏览器相应信息)
http.createServer(function (request, response) {
//设置响应头
response.writeHead(200, {'Content-Type': 'text/plain'});
//表示给我们页面上面输出一句话并结束响应
response.end('Hello World');
}).listen(8081);//端口号
console.log('Server running at http://127.0.0.1:8081/');

也可以重新写一个

const http = require('http');
http.createServer((req,res)=>{
console.log(req.url);
//设置响应头
// 状态码200,文件类型html,字符集 utf-8
res.writeHead(200,{"Content-type":"text/html;charset='utf-8"});
res.write('first nodejs');
res.end();//必须写这句话 表示结束相应
}).listen(3001);

同样运行在127.0.0.1只不过端口号改为3001

如果中文乱码 设置html编码utf-8

es.write("<head><meta charset='UTF-8'></head>");

nodejs的url模块

const url = require('url');
var api = 'http://www.xxx.com?name=abc&sex=male';
console.log(url.parse(api,true));

输出结果:

Url {
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.xxx.com',
port: null,
hostname: 'www.xxx.com',
hash: null,
search: '?name=abc&sex=male',
query: [Object: null prototype] { name: 'abc', sex: 'male' },
pathname: '/',
path: '/?name=abc&sex=male',
href: 'http://www.xxx.com/?name=abc&sex=male' }

如果想单纯打印出后面接收到的json数据格式

url.parse(api,true).query;

module.exports

使用module.exports或者exports可以暴露方法和属性 这样使得在其他页面可以使用require获得当前的的方法和属性,类似java的import

var obj={
get:function(){
console.log('get');
},
post:function(){
console.log('post');
}
}
module.exports = obj;

包目录

一般包含如下这些文件

  • package.json//包描述文件
  • bin //用于存放可执行二进制文件的目录
  • lib //用于存放JavaScript代码的目录
  • doc //用于存放文档的目录

fs模块(文件操作)

  1. fs.stat 检测是文件还是目录
  2. fs.mkdir 创建目录
  3. fs.writeFile 创建写入文件
  4. fs.appendFile 追加文件
  5. fs.readFile 读取文件
  6. fs.readdir 读取目录
  7. fs.rename 重命名
  8. fs.unlink 删除文件

使用:

const fs = require('fs');

例1: 查看是否有upload目录 没有则创建

const fs = require('fs');
var path = './upload'
fs.stat(path,(err,data)=>{
if(err){
//执行创建目录
mkdir(path);
return;
}
if(data.isDirectory()){
console.log('upload 存在');
}else{
//首先删除
//执行创建
fs.unlink(path,()=>{
if(!err){
mkdir(path);
}else{
console.log('请检测目录是否正确');
}
})
mkdir(path);
return;
}
})
//创建方法
function mkdir(dir){
fs.mkdir(dir,(err)=>{
console.log(err);
return;
})
}

也可以使用mkdirp模块。

例2:找出文件夹下的所有目录放入数组中

const fs = require('fs');
let path = './root';
let dirArr=[];
fs.readdir(path,(err,data)=>{
if(err){
console.log(err);
return;
}
(function getDir(i){//IIFE函数 立即调用
if(i==data.length){
console.log(dirArr);
return;
}
fs.stat(path+'/'+data[i],(err,status)=>{
if(status.isDirectory()){
dirArr.push(data[i]);
}
getDir(++i);
})
})(0)
})

以流的方式处理稳健

fs.createReadStream fs.createWriteStream

下例是写入到ouput文件

const fs = require('fs');
var writeStream = fs.createWriteStream('./root/output.txt');
var str = ''
for (let i = 0; i < 500; i++) {
str+='你好\n';
}
writeStream.write(str);
//如果需要监听写入完成
writeStream.end();//标记写入完成
writeStream.on('finish',()=>{
console.log('finish');
})

利用管道流复制文件

const fs = require('fs');
var readStream = fs.createReadStream('./root/1.txt');
var writeStream = fs.createWriteStream('./root/2.txt');
readStream.pipe(writeStream);

1复制到2

Nodejs的非阻塞IO 异步 以及事件驱动

nodejs中是非阻塞的io,也就是说在一段程序段内代码的运行不会因为一个异步操作而停止等待,而是会继续执行下去。

当方法内为异步函数而取不到值时的处理方法

  • 利用回调函数来解决
function getXX(callback){
fs.readFile('xxx.json',function(err,data){
callback(data);
})
}
getXX(function(result){
console.log(result.toString());
})
  • 利用nodejs中的 events模块处理异步
//引入events模块
var events = require('events');
//实例化EventEmitter
var EventEmitter = new events.EventEmitter();
//广播和接收广播
//监听到了to_do的广播
EventEmitter.on('to_do',function(data){
console.log('监听到了广播'+data);
})
setTimeout(function(){
consolo.log('begin')
//广播to_do事件
EventEmitter.emit('to_do','发送的数据')
},2000);
  • 直接使用async以及await(简单方便)

async以及await

//普通方法log
function print(){
return 'hello';
}
console.log(print());

输出结果为

hello
//async
async function print(){
return 'hello';
}
console.log(print());

输出结果为

Promise{'hello'}

可见其输出对象为Promise对象

async 可以让一个方法变成Promise的异步方法

获取async方法内的数据: -方法一使用Promise的.then回调函数 -方法二使用await(await只能写在async方法内部) 类似把异步操作转换成同步操作。

//async
async function print(){
return 'hello';
}
async function getData(){
var data = await print();
console.log(data);
}
getData();

koa2

koa router 文档

https://www.npmjs.com/package/koa-router

中间件

  1. 应用级中间件
//任何一个路由的匹配都要先过中间件
app.use(async (ctx,next)=>{
console.log(new Date());
await next()//当前路由匹配完成之后继续向下匹配,如果不写await next(),就会终止路由
})
  1. 路由级中间件
//路由级中间件是放在路由当中的
router.get('/home', async (ctx, next) => {
console.log("hello world");
await next()//不写的话就不会继续向下匹配
})
  1. 错误处理中间件
app.use(async (ctx,next)=>{
if(ctx.status==404){
ctx.status=404;
ctx.body="404页面"
}
})
  1. 中间件的执行顺序
app.use(async (ctx,next)=>{
console.log("1");
await next();
console.log("5")
})
app.use(async (ctx,next)=>{
console.log("2");
await next()
console.log("4")
})
router.get('/home', async (ctx, next) => {
console.log("3");
})

以上的程序段会输出12345

中间件的执行顺序有点类似递归、栈这样的一个过程。

==其实中间件执行逻辑没有什么特别的不同,都是依赖函数调用栈的执行顺序,抬杠一点讲都可以叫做洋葱模型。Koa 依靠 async/await(generator + co)让异步操作可以变成同步写法,更好理解。最关键的不是这些中间的执行顺序,而是响应的时机,Express 使用 res.end() 是立即返回,这样想要做出些响应前的操作变得比较麻烦;而 Koa 是在所有中间件中使用 ctx.body 设置响应数据,但是并不立即响应,而是在所有中间件执行结束后,再调用 res.end(ctx.body) 进行响应,这样就为响应前的操作预留了空间,所以是请求与响应都在最外层,中间件处理是一层层进行,所以被理解成洋葱模型,个人拙见。(一段网友的见解描述express和koa2的中间件模型)==