Koa 2、bigpipe,io密集、vue ssr等问题回复
为啥说Node.js适合io密集操作
理解http,基本的请求响应模型
var http = require("http");
http.createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.write("Hello World");
res.end();
}).listen(8888);
理解事件驱动,凡是简单on("data",function(chunk){}
的,是listener监听器,都是继承自EventEmit才有的功能,那么事件emit发射在哪里?自己试着想想
var http = require('http');
http.createServer(function(request, response) {
var body = [];
request.on('data', function(chunk) {
body.push(chunk);
}).on('end', function() {
body = Buffer.concat(body).toString();
response.end(body);
});
}).listen(8080);
理解Stream,各种语言、框架里pipe管道(比如shell里,比如gulp里的pipe),本身http就是io操作比较多,使用Stream流式处理可以减少切换成本,无中间文件等,
Stream学习资料https://cnodejs.org/topic/570b1fa494b38dcb3c09a7f8
var http = require('http');
http.createServer(function(request, response) {
if (request.method === 'GET' && request.url === '/echo') {
request.pipe(response);
} else {
response.statusCode = 404;
response.end();
}
}).listen(8080);
如果一个请求要多次处理呢?
request.pipe(xx).pipe(yy).pipe(response);
总结,说Node.js适合io密集操作,确实是,libuv就是为了Node.js异步io而生的。这部分是有优化的,所以它比较强,如果在cpu密集方面也有类似的库,是不是它也适合cup密集工作呢?凡事皆有二面性,非不能解,看想不想解。
koa好是好,不过第三方生态圈不如express,express好多比较有特色的模块,并不直接支持koa
koa好是好,不过第三方生态圈不如express,express好多比较有特色的模块,并不直接支持koa。虽然也可以转,但是用起来还是不如原生用的方便。
如果说要比生态,用java和php等老牌语言会更加成熟。其实选型取舍是需求和追求,express已经很长时间了,是非常稳定的web框架。从代码提交、issues、测试就可以看出来。明显比Koa要完善很多。本质上,它最多是Node.js http模块的强力补充。
我们又要回到那个很久远的问题,都喜欢温水,都不喜欢冷水,换一个环境多少会有不适应的感觉。最好是一只不变下去。但实际呢?技术的驱动和演进往往是那些打破你舒适区的东西。我也是个express老司机,我也很喜欢拿express去理解koa,被苏大fengmk2喷过好几次,如果要完全一样,革新干什么呢?
举个例子:webpack-dev-middleware算比较火热的模块了,它只支持express
如果想改写成Koa版本,你需要掌握2点
- Koa中间件写法(基础)
- webpack-dev-middleware模块的用法
代码如下
'use strict'
const devMiddleware = require('webpack-dev-middleware')
module.exports = (compiler, opts) => {
const expressMiddleware = devMiddleware(compiler, opts)
return (ctx, next) => {
return expressMiddleware(ctx.req, {
end: (content) => {
ctx.body = content
},
setHeader: (name, value) => {
ctx.headers[name] = value
}
}, next);
}
}
- 用法,传参都一样
- return (ctx, next) => {} 是Koa 2.x经典的中间件写法
- 配置项end和setHeader等都是webpack-dev-middleware里需要的
就这点代码,难度很大么?折腾了一番,相信大家对webpack-dev-middleware这个模块会有更深入的理解。
爷爷都是从孙子走过来的,哪个大牛没写过hello world!
Koa-bigpipe
bigpipe当年给facebook立了很大功劳。给出百科简介
BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。虽然BigPipe是对现有的服务网络基础过程的重新设计,但它却不需要改变现有的网络浏览器或服务器,它完全使用PHP和JavaScript来实现。
它只是优化技术而已,很明显这段简介里是有问题的。早起fb是php,微博也是php,所以才有这个无解。但技术问题是通用的,所有语言都可以实现,只是看哪个简单哪个高效而已。用Node.js实现bigpipe是非常好的选择。
这里简单说一下大家对bigpipe技术的感觉,在express使用res.write,多次写html片段到浏览器,而已。
原理上确实是这样的,所以说也对也不对,原因是你真的在项目里使用它了么?
Koa里没有提供对bigpipe的支持,ctx.body赋值做了很多约定。可以说是不太容易控制。
- http模块是基于stream的,所以方案1是通过require(‘stream’).Readable来处理,这种是非常容易理解,但要求大家对stream有一个比较好的理解。
- 方案2,反正Koa和express都是基于http模块的,那么为什么不用http模块的方法呢?
在Koa里有2个概念非常容易混,req和request,res和response,我们经常在express里用req和res,但在Koa里它们指的是http启动server时传入的req和res参数
var http = require("http");
http.createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.write("Hello World");
res.end();
}).listen(8888);
那么是不是可以不用Koa的东西,改用http的接口呢?res.write和express里的res.write是一样的。
所以代码如下
'use strict'
/**
* Module exports.
* @public
*/
module.exports = (ctx, next) => {
ctx.type = 'html'
ctx.status= 200
ctx.chunks = []
let req = ctx.req
let res = ctx.res
// write chunk to browser
ctx.write = (chunk) => {
if (!chunk) {
return ctx.end()
}
ctx.chunks.push(chunk)
res.write(chunk)
}
// end response
ctx.end = (chunk) => {
if (chunk) {
ctx.write(chunk)
}
res.end(null)
}
return next()
}
总结一下,ctx.write和ctx.end方法是为了保持跟koa风格一致,ctx本身是上下文的意思,通过提供这2个方法来完成bigpipe,其实如果从简,你直接ctx.res.write也是可以的。
vue ssr
https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer
抓出一段代码
// example usage with express
app.get('/', (req, res) => {
const vm = new App({ url: req.url })
const stream = renderer.renderToStream(vm)
res.write(`<!DOCTYPE html><html><head><title>...</title></head><body>`)
stream.on('data', chunk => {
res.write(chunk)
})
stream.on('end', () => {
res.end('</body></html>')
})
})
这很明显就是express里bigpipe的写法。如果你想把它改成Koa版本的,那么你首先要搞定Koa版本的bigpipe如何实现。然后再考虑vue的ssr。
如果盲点太多,又怕坑,那么还是早早的转行吧,这个时刻需要学习的行业不适合你~
退一步讲,如果连express这段代码都不是特别懂,那还是乖乖的用express好了。
给出Koa vue ssr的代码
app.use((ctx, next) => {
let res = ctx.res
let req = ctx.req
if (!renderer) {
return res.end('waiting for compilation... refresh in a moment.')
}
var s = Date.now()
const context = { url: req.url }
const renderStream = renderer.renderToStream(context)
let firstChunk = true
ctx.write(html.head)
renderStream.on('data', chunk => {
if (firstChunk) {
// embed initial store state
if (context.initialState) {
ctx.write(
`<script>window.__INITIAL_STATE__=${
serialize(context.initialState, { isJSON: true })
}</script>`
)
}
firstChunk = false
}
ctx.write(chunk)
})
renderStream.on('end', () => {
ctx.end(html.tail)
console.log(`whole request: ${Date.now() - s}ms`)
})
renderStream.on('error', err => {
throw err
})
})
使用了ES6和module,如何做测试和覆盖率
测试和覆盖问题
这个问题有点蒙,测试框架是独立的,通用的,对Koa还是express没有啥区别。
引入es6对测试的影响,我觉得只是多了一步实例化的过程,整体并无太多。另外其他特性,新版的测试框架都已经集成的差不多了。
如果激进点,选择ava,非常爽,非常快,尤其是新特性,如果想保守点,可以用mocha,另外jest和jasmine也不错。
测试覆盖率还是要在根上看,如果严格遵守tdd或bdd,做这个比较容易。如果是为了做而做就失去意义的。
google出来的文章分不清Koa 1和Koa 2
不能一概而论,下面的方法可以快速辨别
方法1
直接用的是Koa1
var koa = require('koa')
var app = koa()
实例化的是Koa2
const Koa = require('koa')
const app = new Koa()
方法2
中间件,完全是generator的,一定是koa1
function* hello() {
yield 'hello';
yield function () {
return 'generator';
};
return 'done';
}
如果出现convert和co.wrap的就是Koa2
另外async/await和function(ctx, next)也是koa2
方法3
在处理过程中,大量使用this的,是koa1
而使用ctx显示声明的,是koa2
我使用了koa static管理public资源,当资源不存在时,依然走了后面的N个中间件,怎么才能在资源没有时,直接404
这其实是中间件的原理问题。
static放到最前面,如果找不到资源,自然会将请求转交给后面的路由或中间件的的。
可以使用https://github.com/koa-modules/serve-static这个,效率高,逻辑等同express。
至于中间件原理,限于篇幅,参见https://github.com/i5ting/stuq-koa