[评论送书 ]手撕源码,实现一个Koa。,2024年最新学生会面试答题技巧

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

Koa Context 将 node 的 requestresponse 对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。 这些操作在 HTTP 服务器开发中频繁使用,它们被添加到此级别而不是更高级别的框架,这将强制中间件重新实现此通用功能。

request.js和response.js文件

在核心目录,我们提到了这两个文件,这两个文件此时就派上了用场。这两个文件具体实现了啥呢?这两个文件定义了ctx.resopnse和ctx.request的结构,也就是上面使用dir输出的结果。在koa中文文档中可以具体的看到结构,可以自行查阅。

Koa Request 对象是在 node 的 原生请求对象之上的抽象,提供了诸多对 HTTP 服务器开发有用的功能。

Koa Response 对象是在 node 的原生响应对象之上的抽象,提供了诸多对 HTTP 服务器开发有用的功能。

实现ctx

定义context.js

const context={

}

module.exports=context

定义request.js

const request={

}

module.exports=request

定义response.js

const resposne={

}

module.exports=response

use中封装ctx

我们在上面导出koa章节中可以看到,在app.use的时候,我们传的参数是(request,response),源koa传的的ctx,所以我们就知道了,koa是在app.use的时候创建了一个ctx。

在本章开头的时候,我们又提到每次请求的ctx都是全新的ctx。

综合以上两点,我们可以基本编写出下面的代码。(为了代码的清晰易读,我们封装了一个createcontext函数来创建上下文。)

const Context = require(‘./context’)

const Request = require(‘./request’)

const Response = require(‘./response’)

class Application {

constructor(){

this.context = Object.create(Context);

this.request = Object.create(Request);

this.response = Object.create(Response);

}

use(fn) {

this.fn = fn

}

createContext = (req, res) => {

const ctx = Object.create(this.context);

const request = Object.create(this.request);

const response = Object.create(this.response);

ctx.app = request.app = response.app = this

ctx.request = request;

ctx.request.req = ctx.req = req;

ctx.response = response;

ctx.response.res = ctx.res = res;

ctx.originalUrl = request.originalUrl = req.url

ctx.state = {}

return ctx

}

callback = (req, res) => {

let ctx = this.createContext(req, res)

this.fn(ctx)

}

listen() {

const server = http.createServer(this.callback);

console.log(…arguments)

server.listen(…arguments)

}

}

首先我们在constructor中定义了一个context对象,这里会在constructor定义是因为Koa的app上默认导出context属性。

app.context 是从其创建 ctx 的原型。您可以通过编辑 app.contextctx 添加其他属性。这对于将 ctx 添加到整个应用程序中使用的属性或方法非常有用,这可能会更加有效(不需要中间件)和/或 更简单(更少的 require()),而更多地依赖于ctx,这可以被认为是一种反模式。

例如,要从 ctx 添加对数据库的引用:

app.context.db = db();

app.use(async ctx => {

console.log(ctx.db);

});

然后再callback中,我们针对response和request进行了二次封装。

再来看看这段代码:

app.context.db = db();

app.use(async ctx => {

console.log(ctx.db);

});

再使用use之前,通过app.context对context进行了修改。当使用use函数的时候,是不是直接进入了callback函数,此时的this.context已经是修改过的了。

测试

const Koa=require(‘./application.js’)

const app=new Koa()

app.use((ctx) => {

// 测试1

ctx.response.res.end(" hello my koa")

// 测试2

ctx.res.end(" hello my koa")

})

app.listen(3000,()=>{

console.log(‘3000’)

})

正常访问!

image.png

封装request.js


明确一个事实:request类的属性是通过getter和setter设置的。为什么会这样设置?这样设置的好处是可以方便的设置和获取到值。是不是有点懵逼!请听我细细道来。

先来看一下Koa中request类所绑定的属性,官方链接

我这里简单的列举几个:

request.header=

设置请求头对象。

request.headers

请求头对象。别名为 request.header.

request.headers=

设置请求头对象。别名为 request.header=

request.url

获取请求 URL.

request.url=

设置请求 URL, 对 url 重写有用。

request.originalUrl

获取请求原始URL。

  1. 这里对于每个属性都有设置和获取的功能,使用getter和setter可以很好的实现。

get url () {

return this.req.url

},

  1. 这里的每个属性是如何设置的,如果我们对request本身设置有效吗?

const request={

url:‘’,

header:{

}

}

const request={

set url (val) {

this.req.url = val

}

get url () {

return this.req.url

},

}

request.socket的getter

socket在这里指套接字。套接字的概念这里不赘述!

get socket () {

return this.req.socket

},

request.protocol的getter

返回请求协议,“https” 或 “http”。当 app.proxytrue 时支持 X-Forwarded-Proto

先判断套接字中是否存在encrypted(加密),如果加密,就是https,

X-Forwarded-Proto用来确定客户端与代理服务器或者负载均衡服务器之间的连接所采用的传输协议(HTTP 或 HTTPS)

X-Forwarded-Proto: https

X-Forwarded-Proto: http

get protocol () {

if (this.socket.encrypted) return ‘https’

if (!this.app.proxy) return ‘http’

const proto = this.get(‘X-Forwarded-Proto’)

return proto ? proto.split(/\s*,\s*/, 1)[0] : ‘http’

},

这里有一个get函数,主要时根据字段,从请求头中获取数据。

get (field) {

const req = this.req

switch (field = field.toLowerCase()) {

case ‘referer’:

case ‘referrer’:

return req.headers.referrer || req.headers.referer || ‘’

default:

return req.headers[field] || ‘’

}

},

request.host的getter

存在时获取主机(hostname:port)。当 app.proxytrue 时支持 X-Forwarded-Host,否则使用 Host

get host () {

const proxy = this.app.proxy

let host = proxy && this.get(‘X-Forwarded-Host’)

if (!host) {

if (this.req.httpVersionMajor >= 2) host = this.get(‘:authority’)

if (!host) host = this.get(‘Host’)

}

if (!host) return ‘’

return host.split(/\s*,\s*/, 1)[0]

},

request.origin的getter

获取URL的来源,包括 protocolhost

例如我请求:http://localhost:3000/index?a=3,

origin返回的是http://localhost:3000

get origin () {

return ${this.protocol}://${this.host}

},

request.href的getter

获取完整的请求URL,包括 protocolhosturl

href支持解析 GET http://example.com/foo

例如我访问http://localhost:3000/index?a=3

href返回http://localhost:3000/index?a=3

get href () {

if (/^https?😕//i.test(this.originalUrl)) return this.originalUrl

return this.origin + this.originalUrl

},

注意:这里的this.originalUrl在封装ctx的时候已经绑定过了

image.png

request.header 的getter和setter

请求头对象。这与 node http.IncomingMessage 上的 headers 字段相同

get header () {

return this.req.headers

},

set header (val) {

this.req.headers = val

},

request的属性是很多的,我们就不展开了,反正知道了原理,大家慢慢自己加吧。

封装response.js


对比request的封装,response的封装稍微有些不同,因为,对于request来说大部分的封装是getter,而response的封装大部分都是setter

在request部分我们阐述了三个使用getter和setter的原因。在resposne中最主要的原因我觉得是改变set的对象。

其实想一想很简单,例如在网络请求中我们会经常遇到各种状态:404 200等等,这些在node的http模块中,是用resposne.status进行改变的。假设我们在koa的response直接设置,你觉得会有用吗?简单概括一句话:koa的request和respsone是对nodehttp模块的二次封装,并且底层还是对nodehttp模块的操作。

response.status的getterh和setter

获取响应状态。默认情况下,response.status 设置为 404 而不是像 node 的 res.statusCode 那样默认为 200

默认’404’,这里的默认是在何时默认的时候呢,其实是在接收到请求后就设置为404,也就是说在callback的时候开始设置为404。(注意:http中res.statusCode用来标记状态码,在Koa中这个被封装成status

callback = (req, res) => {

let ctx = this.createContext(req, res)

const res = ctx.res

res.statusCode = 404

this.fn(ctx)

}

response.status的实现

get status () {

return this.res.statusCode

},

set status (code) {

if (this.headerSent) return

assert(Number.isInteger(code), ‘status code must be a number’)

assert(code >= 100 && code <= 999, invalid status code: ${code})

this._explicitStatus = true

this.res.statusCode = code

if (this.req.httpVersionMajor < 2) this.res.statusMessage = statuses[code]

if (this.body && statuses.empty[code]) this.body = null

},

response.body的getter和setter


首先我们要知道body是用来干嘛的。body是用来设置响应主体的,也就是返回响应的内容的。这些内容支持以下格式:

  • string 写入
  • Buffer 写入
  • Stream 管道
  • Object || Array JSON-字符串化
  • null 无内容响应
  1. nodehttp中是 res.end(“我是返回内容”) 返回响应内容的。在koa中我们是通过ctx.body=“” 来设置响应内容的。这里有人会问了,ctx.body和resopnse.body 有啥关系。其实他们是一个东西,ctx里面封装了response.body。

  2. koa中通过设置ctx.body,就能返回内容,其实本质还是使用了res.end(),通过res.end(ctx.body)来返回内容。res.end的调用时机在这里是放在callback中(具体的原因我们后面会说到

const response = {

_body: undefined,

get body() {

return this._body

},

set body(originContent) {

this.res.statusCode = 200;

this._body = originContent;

}

};

封装context.js


先谈谈Koa用到的delegates。这是一个实现了代理模式的包。对于Koa来说,context就是response和request的代理,通过ctx可以直接拿到request和response的属性和方法。

下面的是Koa主要用到的两个方法。其实最终的效果和封装request和response的效果一致。

__defineGetter__ 方法可以将一个函数绑定在当前对象的指定属性上,当那个属性的值被读取时,你所绑定的函数就会被调用。

__defineSetter__ 方法可以将一个函数绑定在当前对象的指定属性上,当那个属性被赋值时,你所绑定的函数就会被调用。

(这两个方法已废弃: 该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。)

Delegator.prototype.setter = function (name) {

var proto = this.proto;

var target = this.target;

this.setters.push(name);

proto.defineSetter(name, function (val) {

return this[target][name] = val;

});

return this;

};

Delegator.prototype.getter = function (name) {

var proto = this.proto;

var target = this.target;

this.getters.push(name);

proto.defineGetter(name, function () {

return this[target][name];

});

return this;

};

这里我们将delegates的核心逻辑抽离,封装context

function defineGetter(target, key) {

context.defineGetter(key, function () {

return this[target][key]

})

}

function defineSetter(target, key) {

context.defineSetter(key, function (value) {

this[target][key] = value

})

}

const context = {};

defineGetter(‘request’, ‘path’)

defineGetter(‘response’, ‘body’)

)

module.exports = context;

这里我们就列了两个,其他的不再赘述。

ctx.body再追述


在上面我们谈到了response.body以及ctx通过代理模式,拿到了response.body.

在Koa的源码中,针对不同格式的内容进行了不同的处理.大家简单看一下就可以。

response = {

set body (val) {

const original = this._body

this._body = val

// no content

if (val == null) {

if (!statuses.empty[this.status]) {

if (this.type === ‘application/json’) {

this._body = ‘null’

return

}

this.status = 204

}

return

}

// 内容存在(设置了内容),这是状态码为200

if (!this._explicitStatus) this.status = 200

// string 字符串

if (typeof val === ‘string’) {

if (setType) this.type = /^\s*</.test(val) ? ‘html’ : ‘text’

this.length = Buffer.byteLength(val)

return

}

// buffer

if (Buffer.isBuffer(val)) {

if (setType) this.type = ‘bin’

this.length = val.length

return

}

Vue 面试题

1.Vue 双向绑定原理
2.描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?
3.你是如何理解 Vue 的响应式系统的?
4.虚拟 DOM 实现原理
5.既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?
6.Vue 中 key 值的作用?
7.Vue 的生命周期
8.Vue 组件间通信有哪些方式?
9.watch、methods 和 computed 的区别?
10.vue 中怎么重置 data?
11.组件中写 name 选项有什么作用?
12.vue-router 有哪些钩子函数?
13.route 和 router 的区别是什么?
14.说一下 Vue 和 React 的认识,做一个简单的对比
15.Vue 的 nextTick 的原理是什么?
16.Vuex 有哪几种属性?
17.vue 首屏加载优化
18.Vue 3.0 有没有过了解?
19.vue-cli 替我们做了哪些工作?

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
tatus = 200

// string 字符串

if (typeof val === ‘string’) {

if (setType) this.type = /^\s*</.test(val) ? ‘html’ : ‘text’

this.length = Buffer.byteLength(val)

return

}

// buffer

if (Buffer.isBuffer(val)) {

if (setType) this.type = ‘bin’

this.length = val.length

return

}

Vue 面试题

1.Vue 双向绑定原理
2.描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?
3.你是如何理解 Vue 的响应式系统的?
4.虚拟 DOM 实现原理
5.既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?
6.Vue 中 key 值的作用?
7.Vue 的生命周期
8.Vue 组件间通信有哪些方式?
9.watch、methods 和 computed 的区别?
10.vue 中怎么重置 data?
11.组件中写 name 选项有什么作用?
12.vue-router 有哪些钩子函数?
13.route 和 router 的区别是什么?
14.说一下 Vue 和 React 的认识,做一个简单的对比
15.Vue 的 nextTick 的原理是什么?
16.Vuex 有哪几种属性?
17.vue 首屏加载优化
18.Vue 3.0 有没有过了解?
19.vue-cli 替我们做了哪些工作?
[外链图片转存中…(img-ejkXKRld-1713418325941)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-BwBXWg4N-1713418325941)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面我来给你简单介绍一下搭建Koa服务器并实现注册功能的步骤。 1. 搭建Koa服务器 首先,你需要安装Node.js和npm包管理工具。然后,通过npm安装koakoa-router模块。 ``` npm install koa koa-router ``` 接着,创建一个koa应用。在项目根目录下创建一个app.js文件,输入以下代码: ``` const Koa = require('koa'); const Router = require('koa-router'); const app = new Koa(); const router = new Router(); router.get('/', async (ctx, next) => { ctx.body = 'Hello World'; }); app.use(router.routes()).use(router.allowedMethods()); app.listen(3000, () => { console.log('Server is running at http://localhost:3000'); }); ``` 上述代码创建了一个koa应用,在浏览器中访问http://localhost:3000,会显示“Hello World”。 2. 实现注册功能 接下来,我们来实现注册功能。首先,我们需要安装koa-bodyparser模块,这个模块可以将post请求的参数解析到ctx.request.body中。 ``` npm install koa-bodyparser ``` 在app.js文件中,引入koa-bodyparser模块,并添加一个post路由: ``` const Koa = require('koa'); const Router = require('koa-router'); const bodyParser = require('koa-bodyparser'); const app = new Koa(); const router = new Router(); router.get('/', async (ctx, next) => { ctx.body = 'Hello World'; }); router.post('/register', async (ctx, next) => { const { username, password } = ctx.request.body; // 这里可以将用户名和密码存储到数据库中 ctx.body = { code: 0, message: '注册成功' }; }); app.use(bodyParser()); app.use(router.routes()).use(router.allowedMethods()); app.listen(3000, () => { console.log('Server is running at http://localhost:3000'); }); ``` 上述代码添加了一个post路由,当访问http://localhost:3000/register时,会将post请求的参数解析到ctx.request.body中,并将用户名和密码存储到数据库中(这里省略了数据库操作),最后返回一个json数据表示注册成功。 好了,到这里为止,我们就成功地搭建了一个Koa服务器并实现了注册功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值