精华 Koa 2、bigpipe,io密集、vue ssr等问题回复
发布于 8 年前 作者 i5ting 9493 次浏览 来自 分享

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

xiaoweiquan.jpeg

10 回复

https://www.zhihu.com/lives/802472054787043328 发一个广告,我要讲一场live,有兴趣的可以来聊聊

本次 Live 是受斯达克学院「 StuQ 公开课」的邀请,并与「 StuQ 公开课」共同策划,我将围绕大前端和 Node.js 进行深度解读。随着大前端的演进与火爆, h5 、 hybrid 、组简化、小程序,越来越多的公司和程序员不得不去重视前端,那么多框架 react 、 vue 、 ng2 到底该怎么选?没有很多经验的程序员该如何高效的解决问题?对于不得不用的 Node.js 欲拒还迎,到底该如何抉择?作为资深全栈工程师、架构师、实践者,狼叔带你回顾 2016 ,展望 2017 ,以 Node.js 视角来帮助大家更好处理前端问题。

本次 Live 主要包括以下内容:

  • * Node.js 2016 回顾
  • * Node.js 的应用场景(前端工具、 proxy2 种、后端、微服务)
  • * Node.js 如何高效配合服务器端渲染(以 react 、 vue 为例)
  • * 如何结合 Node.js 完成更多(以最近写的 bigview 模块为例)
  • * 如何在真实项目里使用、推广 Node.js ?
  • * Node.js 2017 展望

https://www.zhihu.com/lives/802472054787043328

Screen Shot 2017-01-20 at 9.43.48 AM.png

厉害。支持

支持狼叔,live结束正好上飞机

个人不建议在koa中直接使用res,关于vue的ssr,用transform流转一下就可以,比用ctx.res优雅些,前几天撸了一个简单vue ssr的框架就是用transform流对vue渲染返回的流添加的首尾,vue-easy-renderer: https://github.com/leaves4j/vue-easy-renderer

@leaves4j 变得简单了么?没有吧

@i5ting 不是变简单了,是优雅一些。

  1. 我理解的是在koa中直接调用ctx.res是不优雅的,应该仅仅把需要返回值(对象、字符串、流之类的)赋值到ctx.body,由koa来处理怎么返回给client,所以不应该直接修改ctx.res这个流,而是组合出一个需要的流,赋值给ctx.body,koa判断ctx.body是流后会pipe到res里,本质上跟你的方式一样,只是我觉得这样更优雅下。
  1. 本身koa最后会对res.body有一些判断处理,直接修改ctx.res会绕过这层处理。

Koa 2 有像论坛这样的一个比较完整的例子参考吗,我觉得这个很重要,当时我学node很多都是参考论坛源码来处理的。涉及路由,数据库,cookie,登录状态等多个网站开发的重要技术处理。

回到顶部