切換語言為:簡體

聊聊 Koa 框架的基本使用方法

  • 爱糖宝
  • 2024-07-09
  • 2065
  • 0
  • 0

前言

如果不使用koa,用原生的node來建立一個簡單的 HTTP 伺服器,並定義埠號為3000

const http = require('http')

const server = http.createServer((req, res) => {
    res.end('hello world')
})

server.listen(3000, () => {
    console.log('server is running on 3000 port');
})


聊聊 Koa 框架的基本使用方法

接下來我們用對此進行二次封裝的KOA來建立這個demo

KOA

“Koa”通常指的是 Koa 框架,它是一個基於 Node.js 的 Web 應用框架,由 Express 原班人馬打造。

Koa 致力於成為一個更小、更富有表現力、更健壯的 Web 框架。它使用 async/await 語法來處理非同步流程,使得程式碼更加簡潔和易讀。

Koa 具有輕量、靈活、中介軟體機制等特點,能夠幫助開發者更高效地構建 Node.js 應用。

首先安裝依賴

npm i koa


小試牛刀

const Koa = require('koa');

const app = new Koa();

app.listen(3000, () => {
    console.log('server is running at port 3000');
});


能夠看出來其實這就是對剛剛的程式碼做了一個封裝。接下來我們建立一個函式體,然後用app給use掉,攜帶koa的上下文物件context,事實上他整合了我們最初始的res和req兩個物件。

const Koa = require('koa');

const app = new Koa();

const main = (ctx) => {
    console.log(ctx);
}
app.use(main);


app.listen(3000, () => {
    console.log('server is running at port 3000');
});


  1. const main = (ctx) => { console.log(ctx); } :定義了一個名為 main 的中介軟體函式,它接收一個 ctx 引數,即上下文物件,並將其列印到控制檯。

  2. app.use(main); :使用 app.use 方法註冊中介軟體函式 main,使其在每次請求處理時被呼叫。

ctx

ctx 是 Koa 中的上下文(Context)物件,它封裝了 Node.js 中的原生 req(請求)和 res(響應)物件。可以將 ctx 看做是一次 HTTP 請求和響應過程中的相關資訊的集合。

透過 ctx,開發者可以方便地訪問和操作請求及響應的各種屬性和方法。

ctx 物件具有以下主要屬性:

  • ctx.req:原生的 req 物件。

  • ctx.res:原生的 res 物件。

  • ctx.request:Koa 自己封裝的請求物件,該物件不僅包含原生 req 物件的屬性,還有一些額外的便捷方法和屬性。例如,可以更方便地獲取查詢引數(ctx.query )、解析 URL(ctx.path )等。

  • ctx.response:Koa 自己封裝的響應物件,同樣具有一些額外的方法和屬性,方便設定響應的相關資訊。

  • ctx 本身還代理了 ctx.request 和 ctx.response 身上的屬性,這意味著可以直接透過 ctx 來訪問 ctx.request 和 ctx.response 的部分屬性,例如直接使用 ctx.query 來獲取查詢引數,而無需使用 ctx.request.query

我們把ctx列印出來是這樣的:

{
  request: {
    method: 'GET',
    url: '/',
    header: {
      host: 'localhost:3000',
      connection: 'keep-alive',
      'cache-control': 'max-age=0',
      'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-platform': '"Windows"',
      'upgrade-insecure-requests': '1',
      'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0',
      accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'sec-fetch-site': 'none',
      'sec-fetch-mode': 'navigate',
      'sec-fetch-user': '?1',
      'sec-fetch-dest': 'document',
      'accept-encoding': 'gzip, deflate, br, zstd',
      'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6'
    }
  },
  response: {
    status: 404,
    message: 'Not Found',
    header: [Object: null prototype] {}
  },
  app: { subdomainOffset: 2, proxy: false, env: 'development' },
  originalUrl: '/',
  req: '<original node req>',
  res: '<original node res>',
  socket: '<original node socket>'
}


因此此時我們想向前端響應一個hello world,甚至可以像最開始那樣,直接使用他封裝好了的ctx裡面的res.end。

const main = (ctx) => {
    // console.log(ctx);
    ctx.res.end('hello koa');
}


除此之外,我們還可以給response響應體上掛一個body屬性

const main = (ctx) => {
    // console.log(ctx);
    // ctx.res.end('hello koa');
    ctx.response.body = 'hello koa';
}
app.use(main);


但是這麼寫屬實是比原生更加麻煩了,因此koa做了一個代理,允許我們直接省略response

const main = (ctx) => {
    // console.log(ctx);
    // ctx.res.end('hello koa');
    ctx.body = 'hello koa';
}
app.use(main);`js


同理,想獲取url,就可以直接使用原生的req.url也可以用封裝好的,再直接可以省略。

const main = (ctx) => {
    // console.log(ctx);
    // ctx.res.end('hello koa');
    // ctx.response.body = 'hello koa';
    ctx.body = 'hello koa';
    console.log(ctx.req.url);
    console.log(ctx.request.url);
    console.log(ctx.url);
}
app.use(main);


不由感嘆,開源的力量,不用大家重複造輪子,這些封裝都很優雅,支援各種寫法,幫助大家省了很多麻煩。

我們來看看,koa還封裝了個accepts

const main = (ctx) => {
    if (ctx.request.header.accept === 'xml') {
        ctx.body = '<data>hello xml </data>';
    } else if (ctx.request.accepts('html')) {
        ctx.body = '<p>hello html</p>';
    }
}
app.use(main);


我們發現原生的拿accept無法匹配到,除非把請求頭全部拿過來(text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7)

為此koa封裝了ctx.request.accepts方法,可以直接匹配。

如果我們想讀取一個html檔案然後給他響應到瀏覽器上

const fs = require('fs');

const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    ctx.response.type = 'text/html';
    const context = fs.readFileSync('./template.html', 'utf-8') //toString()
    console.log(context);
    ctx.body = context;

}



app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


  • 要讓瀏覽器讀得懂,要設定響應頭,告訴瀏覽器這是一個html,如果沒有toString或者沒有設定utf8編碼,那麼會拿到一個buffer流,如果響應一個buffer流就會自動觸發下載,因此此時讓你去做一個觸發下載某個東西的操作你也可以做了。

  • 我們能夠理解,這裏ctx.response.type可以直接省略response了。

  • 接下來再換一種方式來寫

const fs = require('fs');

const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    // ctx.response.type = 'text/html';
    // const context = fs.readFileSync('./template.html', 'utf-8') //toString()
    // console.log(context);
    // ctx.body = context;

    // ctx.response.type = 'text/html';
    ctx.res.writeHead(200,{'Content-type':'text/html'}) // 原生的方式不愛用就用上面的
    const content = fs.createReadStream('./template.html');
    console.log(content);
    ctx.body = content;
}



app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


koa路由

用最開始的辦法寫路由

const fs = require('fs');

const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    if (ctx.url === '/') {
        ctx.type = 'text/html';
        ctx.body = '<h2>home</h2>'
    }else{
        ctx.type = 'text/html';
        ctx.body = '<a href="/">go home</a>'
    }
}


可以看出,這種寫法也是相當的噁心。因此我們使用koa-route,因為這個框架非常簡單,因此很多團隊都可以自己做封裝,大家可以自行選擇使用什麼koa-route/koa-router...

安裝依賴

npm i koa-route


小試牛刀

const router = require('koa-route');
const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<h2>home</h2>'

}
const about = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<a href="/">about,go home</a>'
}


app.use(router.get('/', main))
app.use(router.get('/about', about))
// app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


注意:事實上koa只能use一個,這裏的路由能use兩個是因為封裝過了,此時我們自己寫一個列印日誌方法如果不寫next,後面的use就無法使用,因此:

const router = require('koa-route');
const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<h2>home</h2>'

}
const about = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<a href="/">about,go home</a>'
}
const logger = (ctx, next) => {
    console.log(`${ctx.method} - ${ctx.url} - ${Date.now()}`);
    next();
}

app.use(logger)
app.use(router.get('/', main))
app.use(router.get('/about', about))

// app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


next

const Koa = require('koa');
const app = new Koa();

const one = (next) =>{ // 中介軟體
    console.log(1);
    console.log(2);
}

const two = () => {
    console.log(3);
    console.log(4);
}

const three = () => {
    console.log(5);
    console.log(6);
}

app.use(one);
app.use(two);
app.use(three); 

app.listen(3000, () => {
    console.log('server is running at port 3000');
});


如果是這樣一份程式碼,勢必只會列印1,2,因此要透過next的呼叫進入下一個中介軟體,一碰到next就會進入下一個中介軟體,如果走完了,就會回頭走沒走完的。

const Koa = require('koa');
const app = new Koa();

const one = (next) =>{ // 中介軟體
    console.log(1);
    next()
    console.log(2);
}

const two = (next) => {
    console.log(3);
    next()
    console.log(4);
}

const three = () => {
    console.log(5);
    console.log(6);
}

app.use(one);
app.use(two);
app.use(three); 

app.listen(3000, () => {
    console.log('server is running at port 3000');
});


因此,答案勢必是:1,3,5,6,4,2,看得出來,這底層一定是用遞迴寫的。不難理解,koa的中介軟體執行過程就是個洋蔥模型,用一根筷子插入一個洋蔥,進入第一層,第二層,第三層,然後到了中間,繼續深入就回到了第二層,第一層。

小結

“Koa”是一個基於 Node.js 的 Web 應用框架,它以簡潔、高效和靈活著稱。

本文介紹了一下Koa的基本使用方法,以及其中的一些細節內容,ctx包括什麼東西,next怎麼使用,如何使用koa做路由...

Koa主要特點包括:

  1. 基於 async/await :使用 ES6 的非同步函式語法,讓非同步流程的控制更加直觀和簡潔。

  2. 輕量級:核心模組非常小,只包含最基本的功能,開發者可以根據需求選擇和新增擴充套件。

  3. 中介軟體機制:透過 app.use 方法來新增中介軟體,中介軟體可以按照新增的順序依次執行,方便對請求和響應進行處理和修改。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.