我经常在网上看到类似于KOA VS express的文章,大家都在讨论哪一个好,哪一个更好。作为小白,我真心看不出他两who更胜一筹。我只知道,我只会跟着官方文档的start做一个DEMO,然后我就会宣称我会用KOA或者express框架了。但是几个礼拜后,我就全忘了。web框架就相当于一个工具,要使用起来,那是分分钟的事。毕竟人家写这个框架就是为了方便大家上手使用。但是这种生硬的照搬模式,不适合我这种理解能力极差的使用者。因此我决定扒一扒源码,通过官方API,自己写一个web框架,其实就相当于“抄”一遍源码,加上自己的理解,从而加深影响。不仅需要知其然,还要需要知其所以然。

我这里选择KOA作为参考范本,只有一个原因!他非常的精简!核心只有4个js文件!基本上就是对createServer的一个封装。

在开始解刨KOA之前,createServer的用法还是需要回顾下的:

const http = require('http');
let app=http.createServer((req, res) => {
//此处省略其他操作
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.body="我是createServer";
res.end('okay');
});
app.listen(3000)

回顾了createServer,接下来就是解刨KOA的那4个文件了:

  • application.js

    • 这个js主要就是对createServer的封装,其中一个最主要的目的就是将他的callback分离出来,让我们可以通过app.use(callback);来调用,其中callback大概就是令大家闻风丧胆的中间件(middleWare)了。
  • request.js
    • 封装createServer中返回的req,主要用于读写属性。
  • response.js
    • 封装createServer中返回的res,主要用于读写属性。
  • context.js
    • 这个文件就很重要了,它主要是封装了request和response,用于框架和中间件的沟通。所以他叫上下文,也是有道理的。

好了~开始写框架咯~

仅分析大概思路,分析KOA的原理,所以并不是100%重现KOA。

本文github地址:点我

step1 封装http.createServer

先写一个初始版的application,让程序先跑起来。这里我们仅仅实现:

  • 封装http.createServer到myhttp的类
  • 将回调独立出来
  • listen方法可以直接用

step1/application.js

let http=require("http")
class myhttp{
handleRequest(req,res){
console.log(req,res)
}
listen(...args){
// 起一个服务
let server = http.createServer(this.handleRequest.bind(this));
server.listen(...args)
}
}

这边的listen完全和server.listen的用法一摸一样,就是传递了下参数

友情链接

server.listen的API

ES6解构赋值...

step1/testhttp.js

let myhttp=require("./application")
let app= new myhttp()
app.listen(3000)

运行testhttp.js,结果打印出了reqres就成功了~

step2 封装原生req和res

这里我们需要做的封装,所需只有两步:

  • 读取(get)req和res的内容
  • 修改(set)res的内容

step2/request.js

let request={
get url(){
return this.req.url
}
}
module.exports=request

step2/response.js

let response={
get body(){
return this.res.body
},
set body(value){
this.res.body=value
}
}
module.exports=response

如果po上代码,就是这么简单,需要的属性可以自己加上去。那么问题来这个this指向哪里??代码是很简单,但是这个指向,并不简单。

回到我们的application.js,让这个this指向我们的myhttp的实例。

step2/application.js

class myhttp{
constructor(){
this.request=Object.create(request)
this.response=Object.create(response)
}
handleRequest(req,res){
let request=Object.create(this.request)
let response=Object.create(this.response)
request.req=req
request.request=request
response.req=req
response.response=response
console.log(request.headers.host,request.req.headers.host,req.headers.host)
}
...
}

此处,我们用Object.create拷贝了一个副本,然后把request和response分别挂上,我们可以通过最后的一个测试看到,我们可以直接通过request.headers.host访问我们需要的信息,而可以不用通过request.req.headers.host这么长的一个指令。这为我们下一步,将requestresponse挂到context打了基础。

step3 context闪亮登场

context的功能,我对他没有其他要求,就可以直接context.headers.host,而不用context.request.headers.host,但是我不可能每次新增需要的属性,都去写一个get/set吧?于是Object.defineProperty这个神操作来了。

step3/content.js

let context = {
}
//可读可写
function access(target,property){
Object.defineProperty(context,property,{
get(){
return this[target][property]
},
set(value){
this[target][property]=value
}
})
}
//只可读
function getter(target,property){
Object.defineProperty(context,property,{
get(){
return this[target][property]
}
})
}
getter('request','headers')
access('response','body')
...

这样我们就可以方便地进行定义数据了,不过需要注意地是,Object.defineProperty地对象只能定义一次,不能多次定义,会报错滴。

step3/application.js

接下来就是连接contextrequestresponse了,新建一个createContext,将responserequest颠来倒去地挂到context就可了。

class myhttp{
constructor(){
this.context=Object.create(context)
...
}
createContext(req,res){
let ctx=Object.create(this.context)
let request=Object.create(this.request)
let response=Object.create(this.response)
ctx.request=request
ctx.response=response
ctx.request.req=ctx.req=req
ctx.response.res=ctx.res=res
return ctx
}
handleRequest(req,res){
let ctx=this.createContext(req,res)
console.log(ctx.headers)
ctx.body="text"
console.log(ctx.body,res.body)
res.end(ctx.body);
}
...
}

以上3步终于把准备工作做好了,接下来进入正题。

参考KOA,5步手写一款粗糙的web框架的更多相关文章

  1. 如何手写一款KOA的中间件来实现断点续传

    本文实现的断点续传只是我对断点续传的一个理解.其中有很多不完善的地方,仅仅是记录了一个我对断点续传一个实现过程.大家应该也会发现我用的都是一些H5的api,老得浏览器不会支持,以及我并未将跨域考虑入内 ...

  2. 一步一步手写GIS开源项目-(2)地图平移缩放实现

    系列文章目录 一步一步手写GIS开源项目-(1)500行代码实现基础GIS展示功能 一步一步手写GIS开源项目-(2)地图平移缩放实现 项目github地址:https://github.com/Hu ...

  3. 手写一款符合Promise/A+规范的Promise

    手写一款符合Promise/A+规范的Promise 长篇预警!有点长,可以选择性观看.如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的.主要 ...

  4. 手写数字识别 卷积神经网络 Pytorch框架实现

    MNIST 手写数字识别 卷积神经网络 Pytorch框架 谨此纪念刚入门的我在卷积神经网络上面的摸爬滚打 说明 下面代码是使用pytorch来实现的LeNet,可以正常运行测试,自己添加了一些注释, ...

  5. koa : Express出品的下一代基于Node.js的web框架

    https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434501579966a ...

  6. 如何手写一款SQL injection tool?

    0×01 前言 我想在FreeBuf上出没的人一般都是安全行业的,或者说是安全方面的爱好者,所以大家对sql注入应该都比较了解,反正我刚入门的时候就是学的这些:sql注入.xss之类的.sql注入从出 ...

  7. 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server

    本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...

  8. 【spring】-- 手写一个最简单的IOC框架

    1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...

  9. Zookeeper——基本使用以及应用场景(手写实现分布式锁和rpc框架)

    文章目录 Zookeeper的基本使用 Zookeeper单机部署 Zookeeper集群搭建 JavaAPI的使用 Zookeeper的应用场景 分布式锁的实现 独享锁 可重入锁 实现RPC框架 基 ...

随机推荐

  1. Selenium Webdriver弹出框 微博分享的内容控制与结果生成

    browser.window_handles for i in ugc_url_l: js = 'window.location.href="{}"'.format(i) brow ...

  2. 安装Node.js 以及命令行使用

    安装 官方的安装包 安装完成之后,会自动添加到环境变量中 通过visual studio安装 命令行 查看版本 PS C:\Users\clu\Desktop> node --version v ...

  3. JFreeChart自我总结

    想飞就别怕摔 大爷的并TM骂人 JFreeChart自我总结 1.饼图.柱状图.折线图生成的工具类   1 package com.text.util;  2   3 import java.awt. ...

  4. git 配置代理

    1.目的:配置proxy,使得git可以克隆github上的代码 2.方法:执行下面三条命令,配置下git的代理 git config --global https.proxy https://w00 ...

  5. Java 高级数据结构 —— Properties

    1. Properties Properties 是 Java 的内置实现: public class Properties extends Hashtable<Object,Object> ...

  6. bzoj1875 [SDOI2009]HH去散步——矩阵快速幂

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1875 有个限制是不能走回头路,比较麻烦: 所以把矩阵中的元素设成边的经过次数,单向边之间就好 ...

  7. CrystalQuartz实现Quartz的window服务的远程管理

    1. 建一个空的ASP.NET WebSite,利用NuGet安装CrystalQuartz.Remote 包 我们可以看到,配置文件中多了如下节点: <crystalQuartz> &l ...

  8. C#面向过程之编译原理、变量、运算符

    .net基础:.net与C# .net是一个平台 c#是一门语言 .net的用途a.桌面应用程序 b.网站应用程序 c.专业游戏开发(XBOX360) d.嵌入式设备软件开发 e.智能手机APP开发 ...

  9. Java properties配置文件

    Java中的配置文件常为properties文件,格式为文本文件,文件内容的格式是“键=值”格式.注释信息使用“#”来注释. Properties类的常用方法 String getProperty(S ...

  10. 1617: [Usaco2008 Mar]River Crossing渡河问题(dp)

    1617: [Usaco2008 Mar]River Crossing渡河问题 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1219  Solved:  ...