簡單的源碼分析之Koa和XHR

對於前端開發者來講,koa和xhr都再熟悉不過了。Koa 是Node.js 的 Web 框架。由 Express 團隊設計。旨在提供一個更小型、更富有表現力、更可靠的 Web 應用和API的開發基礎。XHR是用來向服務器發送請求的。但咱們在使用它們的時候都是直接使用它們的API,彷佛忘記了它們原生的底層實現。這篇文章我就來和你們聊聊它們的源碼分析。

Koa源碼實現

  • 使用koa
const Koa = require("koa");
const app = new Koa();

app.use((ctx) => {
    ctx.body = 'Hello World!'
})

app.listen(3000)
複製代碼

這就是咱們koa的使用了。其中ctx是koa的一大發明,它是koa的上下文。ctx包含了req和res。以上代碼其實就是一個http服務,源碼實現其實也就是要考慮在Node裏的哪一個模塊的哪一個方法能夠實如今端口上實現服務監聽。前端

  • 手寫koa
// index.js文件
const MyKoa = require("./lib/application"); 
const app = new MyKoa();
app.use((req, res) => {
    res.end('Hello World!')
})
app.listen(3000, () => {
  console.log('你的應用在3000端口啓動');
});
複製代碼
// application.js文件
// 在index.js同級目錄下建立lib文件,再建立子文件application.js文件
const http = require('http');
class MyKoa {
    constructor() {
        console.log('手寫koa!')
        // 初始化定義屬性
        this.fn = undefined;
    }
    // ...args動態接收參數
    listen(...args) {
        // 啓動server傳入異步的回調函數給用戶反饋
        let server = http.createServer(this.fn);
        server.listen(...args);
    }
    use(fn) {
        // 利用對象屬性保存
        this.fn = fn;
    }
}
module.exports = MyKoa
複製代碼

在這裏請區分一下兩個listen: app.listen和server.listen。第一個listen是koa的api,第二個listen是使用node原生代碼來實現這個api。node

XHR底層實現

  • "石器時代"收發報文
    先請熟悉一下HTTP報文的格式,再來看代碼會更清楚。

// server.js
const http = require('http');
http.createServer((req, res) => {
  // 向前端返回的內容
  res.end('OKOK!');
})
.listen(8088, ()=> {
  console.log(8088);
})
複製代碼
// 引入net模塊(封裝了傳輸層)
const net = require('net');
// 傳輸層 源端口號 目的地端口號
// 建立請求
// 建立一個到端口port和主機host的TCP鏈接(去到目的地址發起請求)
const client = net.createConnection({ port: 8088, host: '127.0.0.1' }, () => {
  let json = JSON.stringify({a: 1});
  // 一行一行拼接報文,空格也不能省
  // 不區分URL,使用'/'
  // CR LF爲行結束符,用\r\n表示
  client.write('POST / HTTP/1.1\r\n');
  client.write('HOST: 127.0.0.1\r\n');
  client.write('Content-Type: application/json\r\n');
  // 首部和實體的分隔符
  client.write('\r\n');
  client.write(json);
  // 報文拼接結束
  client.write('\r\n');
})
// 服務器返回的數據
client.on('data', (data) => {
  // 原始數據爲Buffer,將其轉換爲字符串
  console.log('receive:', data.toString());
})
// 接收完數據後斷開
client.on('end', () => {
  console.log('disconnect');
})

複製代碼

  • "近現代"收發報文
const net = require('net');
class Xmlhttprequest {
  constructor() {
    // 放入屬性保存
    // 請求部分
    this.method = null;
    this.url = null;
    this.headers = null;
    this.body = null;
    // 響應部分
    this.resStatusLine = null;
    this.resHeaders = null;
    this.response = null;
  }
  // 設置操做
  open(method, url) {
    this.method = method;
    this.url = url;
  }
  setHeader(headers) {
    this.headers = headers;
  }
  // 切割報文
  parse(string) {
    const lines = string.split('\r\n');
    console.log(lines);
    this.resStatusLine = lines[0];
    this.statusCode = this.resStatusLine.split(' ')[1];
    this.resHeaders = lines.slice(1, lines.length - 2);
    this.response = lines[lines.length - 1];
  }
  send(body) {
    // 瀏覽器發出http請求後開始拼接http報文
    this.body = body;
    // 創建鏈接
    const client = net.createConnection({ port: 8088, host: '127.0.0.1' }, () => {
      // 拼接報文
      client.write(`${this.method} ${this.url} HTTP/1.1\r\nHOST: 127.0.0.1\r\nContent-Type: application/json\r\nContent-Length: ${this.body.length}\r\n\r\n${this.body}\r\n`)
      // 發送完畢
      client.end();
    })
    // 收到後端返回的數據
    client.on('data', (chunk) => {
      // 服務端返回給瀏覽器的也是一個原始的http報文
      // 解析報文
      console.log('receive:', JSON.stringify(chunk.toString()));
      // 調用專用的解析函數parse解析報文
      this.parse(chunk.toString());
      // 接受完數據後調用onload
      this.onload();
    })
    client.on('end', () => {
      console.log('disconnect');
    })
  }
  
}
// ajax
const xhr = new Xmlhttprequest();
xhr.open("POST", "/");
// 定義請求頭
xhr.setHeader({
  'Content-Type': 'application/json'
})

// 回調數據加載回來纔會調用
xhr.onload = function() {
  // 
  console.log('響應數據:');
  // 拿到響應的狀態碼
  console.log(xhr.statusCode)
  // 拿到響應的內容
  console.log(xhr.response)
  // 拿到響應頭
  console.log(xhr.resHeaders)
}
// 發出請求
xhr.send(JSON.stringify({a: 1}))
複製代碼

以上解析報文的方法並非最通用和最"優雅"的方法,它是一種比較古老的方法。但對咱們理解底層實現仍是有一點幫助的。ajax

總結

在前端學習中,咱們會遇到各類各樣的框架和API。咱們不能只停留在會用的階段,而是要儘量地掌握底層實現的原理。畢竟,框架和API會一直更新換代,掌握底層最基本的原理才能以不變應萬變。json

相關文章
相關標籤/搜索