新竹縣自建焚化爐案 副議長籲充分溝通再進行

摘錄自2020年3月21日中央社報導

新竹縣沒有焚化爐,垃圾處理仰賴外縣市焚化爐,新竹縣政府預計在竹北尚義里興建焚化爐,新竹縣副議長王炳漢21日上午在竹北市舉辦「新竹縣促進民間參與高效能垃圾熱處理設施投資BOO(興建、擁有、營運)案」座談會,300多名附近居民到場。

座談會中,許多民眾反對在現址設立焚化爐,另也有民眾關心對於生活、環境(車輛經過路面)及空氣品質,如何去把關、是否會影響附近民眾的身體健康,甚至影響附近烏魚子產業。

王炳漢說,這項建設影響層面很廣,應要與地方民眾充分溝通,才能進行。,23日將會向新竹縣長楊文科要求預計於3月25日舉辦的公聽會延期,並將民眾提出的意見整合,一起向新竹縣環保局提出。

【其他文章推薦】

飲水機設備有哪些?

※如何選購橡膠製品規格有哪些?

AVX DistributorAVX TPSAVX鉭質電容器 規格有哪些?各別作用在於?

※真空封口機該不該買?使用心得分享

定量泵浦如何運用在食品加工上呢?

空壓機這裡買最划算!

消防局第六大隊副大隊長陳俊宏指出,起火原因尚待調查

消防局第六大隊副大隊長陳俊宏指出,起火原因尚待調查,但往年清明掃墓常引發山區火警,不排除與掃墓焚燒紙錢或放鞭炮有關,消防局呼籲民眾,星星之火可以燎原,應遵守「人離火熄」的原則,隨身攜帶簡易滅火水源,避免危害公眾安全。
【其他文章推薦】

※廢氣洗滌塔,叫得動, 找得到的專業廠商‎

※市面十大品牌封口機!該如何選購?

塑膠射出成型加工商品有哪些?

貨梯使用安全與保養

※掌握產品行銷策略,帶你認識商品包裝設計基本要素

臭氧機推薦

環團籲清除鳥嘴潭旁垃圾場 地方難覓他處陷兩難

摘錄自2020年3月21日中央社報導

經濟部水利署在草屯鎮烏溪流域推動鳥嘴潭人工湖工程計畫,但一小部分湖區旁邊就是草屯鎮垃圾轉運暫置場,台灣水資源保育聯盟、彰化縣環境保護聯盟等環保團體20日在立法院中興大樓舉辦記者會,希望「新垃圾不能再進場,舊垃圾應清除」。

中央社記者實地走訪發現,垃圾暫置場與人工湖工區相隔一條道路及堤防,空氣中混有淡淡臭味,但地表未見污水流入烏溪;一旁的小型焚化爐早已停用多年,並轉型成草屯鎮環保教育園區,旁邊空地則變成垃圾轉運暫置場,垃圾堆得像小山。

草屯鎮公所清潔隊人員說,設置垃圾場需遠離住家,這處靠近溪邊,附近人煙稀少,但約20年前規劃設置就遭嚴重抗議,甚至出動保安警察維持秩序,更何況現今大眾環保意識抬頭,要重新找土地更困難,也無合適鎮有地,若中央能協助,地方也願意配合。

【其他文章推薦】

※使用真空封口機常見問題?

※影響示波器測試準確度的五大因素

※各大百貨每波促銷贈品活動,限量知名LOGOL型資料夾,獨家販售中!!

※哪裡有合版印刷優惠,尋找L夾客製化印刷廠商?

※如何正確使用飲水機?

洗滌塔運作原理介紹

無塵擦拭布各大品牌廠商販售比價網!

亂丟口罩比沒戴更可怕 屏縣府加強宣導稽查

摘錄自2020年3月20日中央社報導

屏東縣環保局指出,3月中發動志工在屏東市、東港鎮的醫院旁道路撿拾廢棄口罩,結果在某家醫院附近撿拾到500多個口罩,當時志工檢視醫院設置的垃圾桶,裡面只有4個口罩,顯示民眾離開醫院後有將口罩隨意丟、不丟垃圾桶的傾向,易衍生環保、衛生問題。

為避免口罩隨意丟棄成為防疫破口,環保局20日再動員清潔隊人員前往墾丁等地區向民眾宣導「亂丟口罩比沒戴口罩更可怕」及加強取締查證,避免橫生公衛問題。

清潔隊人員也在恆春公車轉運站及醫院前等地,手持宣導標語,並撿拾丟棄地面的口罩,藉以提醒民眾應將使用過的口罩丟入垃圾桶,不要任意隨地丟棄。

【其他文章推薦】

※(全省)堆高機租賃保養一覽表

※高效率洗滌塔活性碳設備,能去除多少有機溶劑?

塑膠射出成型技師工作甘苦談

※人氣推薦-獨家專業技術熱傳導玻璃煎台

※推薦優質工業用攪拌機製作商

臭氧機推薦

鼻頭角公園週遭海域 小丑魚大量消失

摘錄自2020年3月22日公共電視報導

鼻頭角海域的小丑魚大量消失,海漁基金會3月份調查發現,照理說要有2千4百隻以上的野生小丑魚,但現在只剩下兩隻。

基金會和中研院研究員陳昭倫合作,統計出鼻頭角公園內外海域的海葵數量,超過400株,由於1株海葵,可以有6隻小丑魚共生,所以當地照理說要有2千4百隻以上的野生小丑魚,但現在只有2隻小丑魚的蹤跡,讓基金會擔心該地區的海洋生態系統,會發生「破窗效應」。

照片提供:海漁基金會。

海漁基金會懷疑,小丑魚消失的主要原因,是被人類捕撈、拿去賣錢。為了復育生態,基金會採取放流魚苗行動,但不是直接把小丑魚放流大海,而是先讓人工繁殖的小丑魚,待在附近小學的生態魚缸,適應海葵。

【其他文章推薦】

※連續封口機購物網-不怕你比價,就怕你買貴!

※測試專家告訴你如何好好使用示波器

※多功能滑鼠墊、多用途卡套、全客製贈品節慶促銷活動開跑中  

※廢氣洗滌塔,叫得動, 找得到的專業廠商‎

※快速搞懂塑膠射出成型原理

※(全省)堆高機租賃保養一覽表

※客製化探針、鐘錶等細小電子零組件車製

座談會中,許多民眾反對在現址設立焚化爐

座談會中,許多民眾反對在現址設立焚化爐,另也有民眾關心對於生活、環境(車輛經過路面)及空氣品質,如何去把關、是否會影響附近民眾的身體健康,甚至影響附近烏魚子產業。

王炳漢說,這項建設影響層面很廣,應要與地方民眾充分溝通,才能進行。,23日將會向新竹縣長楊文科要求預計於3月25日舉辦的公聽會延期,並將民眾提出的意見整合,一起向新竹縣環保局提出。

【其他文章推薦】

※影響示波器測試準確度的五大因素

※全自動飲水機與一般飲水機差異在哪?

※噴霧洗滌塔實際應用案例分享

貨梯使用安全與保養

※幫你考照過關,堆高機裝卸操作教學影片大公開 !

※早餐店適用哪種商用煎台呢?

板式熱交換器規格有哪些?

高市推6座滯洪池發電 五甲尾滯洪池2020年招商

摘錄自2020年3月22日自由時報報導

南台灣年平均日照為3.3小時,高雄有15座滯洪池,水利局配合水利署推動「水庫及滯洪池太陽能發電計畫」,選定永安等7座滯洪池,發展水域型太陽光電,前市府完成永安、典寶溪B區一期滯洪池「水域光電」。

市府2019年延續政策,完成山仔頂滯洪池「水域光電」,2020年推動典寶溪B區二期、鳳山圳滯洪池「水域光電」,前峰子滯洪池是前市府規劃,因用地取得較慢,目前已施工中,預計今年6月底完工。

水利局表示,6座滯洪池「水域光電」每年總發電量1800萬度,節能減碳達1萬1000噸,相當於種植33萬棵樹,可提供51,370戶住家1年使用,每年可挹注市庫1,750萬元。

【其他文章推薦】

※廢氣洗滌塔,叫得動, 找得到的專業廠商‎

※選擇示波器的10 項考量因素

無塵擦拭布各大品牌廠商販售比價網!

飲水機品牌迷思?有牌代表最好?錯! 了解用途才能買對!

※什麼規格的隔熱紙能有效提升冷房效果?

※快速搞懂塑膠射出成型原理

貨梯使用安全與保養

美濃火燒山徹夜延燒18小時撲滅 燒毀5公頃山林

摘錄自2020年3月22日自由時報報導

高雄市美濃區福美路靈山21日發生火燒山,在徹夜延燒18個小時之後,22日上午空勤總隊直升機出動空中灑水救援,火勢終於在11點左右撲滅,初估約燒毀5公頃山林,所幸並未波及民宅。

消防局第六大隊副大隊長陳俊宏指出,起火原因尚待調查,但往年清明掃墓常引發山區火警,不排除與掃墓焚燒紙錢或放鞭炮有關,消防局呼籲民眾,星星之火可以燎原,應遵守「人離火熄」的原則,隨身攜帶簡易滅火水源,避免危害公眾安全。

【其他文章推薦】

AVX代理商NICHICON代理商授權有哪幾家?

※大樓隔熱紙施工分享說明,教你如何善用空間裝潢設計 !

※買不起高檔茶葉,精緻包裝茶葉罐,也能撐場面!

塑膠射出成型模具過程大公開 !

※廢氣洗滌塔設計及注意事項 ?

實驗型均質機乳化分離效果好嗎?

2020年東京都櫻花開花是從1953年開始觀測以來最早的一年

日本讀賣新聞報導,2020年東京都櫻花開花是從1953年開始觀測以來最早的一年,滿開則是次於2002年的3月21日第2早的一年。

東京都青梅市最高溫來到攝氏27.1度,相當於往年7月上旬的氣溫;東京都八王子市也有26.2度、埼玉縣秩父市有26.7度、茨城縣古河市為25.6度、櫪木縣佐野市有25.4度、群馬縣高崎市也有25.2度。而在沖繩縣宇流麻市及愛媛縣新居濱市,則雙雙創下觀測史上3月最高溫紀錄。

【其他文章推薦】

※找工作! 想知道堆高機駕駛日薪是多少嗎? 哪裡有職缺?幫你快速媒合

※隨時健康喝好水,高品質飲水機,優質安全有把關  

※防爆隔熱紙規格資訊說明

※好的茗茶,更需要密封性高的茶葉罐,才能留住香氣!

※【找人才】台北塑膠射出成型工廠徵選技師,薪資優,福利佳

貨梯使用安全與保養

實驗型均質機各品牌功能、價格介紹

Koa源碼解析,帶你實現一個迷你版的Koa

前言

本文是我在閱讀 Koa 源碼后,並實現迷你版 Koa 的過程。如果你使用過 Koa 但不知道內部的原理,我想這篇文章應該能夠幫助到你,實現一個迷你版的 Koa 不會很難。

本文會循序漸進的解析內部原理,包括:

  1. 基礎版本的 koa
  2. context 的實現
  3. 中間件原理及實現

文件結構

  • application.js: 入口文件,裡面包括我們常用的 use 方法、listen 方法以及對 ctx.body 做輸出處理
  • context.js: 主要是做屬性和方法的代理,讓用戶能夠更簡便的訪問到requestresponse的屬性和方法
  • request.js: 對原生的 req 屬性做處理,擴展更多可用的屬性和方法,比如:query 屬性、get 方法
  • response.js: 對原生的 res 屬性做處理,擴展更多可用的屬性和方法,比如:status 屬性、set 方法

基礎版本

用法:

const Coa = require('./coa/application')
const app = new Coa()

// 應用中間件
app.use((ctx) => {
  ctx.body = '<h1>Hello</h1>'
})

app.listen(3000, '127.0.0.1')

application.js:

const http = require('http')

module.exports = class Coa {
  use(fn) {
    this.fn = fn
  }
  // listen 只是語法糖  本身還是使用 http.createServer
  listen(...args) {
    const server = http.createServer(this.callback())
    server.listen(...args)
  }
  callback() {
    const handleRequest = (req, res) => {
      // 創建上下文
      const ctx = this.createContext(req, res)
      // 調用中間件
      this.fn(ctx)
      // 輸出內容
      res.end(ctx.body)
    }
    return handleRequest
  }
  createContext(req, res) {
    let ctx = {}
    ctx.req = req
    ctx.res = res
    return ctx
  }
}

基礎版本的實現很簡單,調用 use 將函數存儲起來,在啟動服務器時再執行這個函數,並輸出 ctx.body 的內容。

但是這樣是沒有靈魂的。接下來,實現 context 和中間件原理,Koa 才算完整。

Context

ctx 為我們擴展了很多好用的屬性和方法,比如 ctx.queryctx.set()。但它們並不是 context 封裝的,而是在訪問 ctx 上的屬性時,它內部通過屬性劫持將 requestresponse 內封裝的屬性返回。就像你訪問 ctx.query,實際上訪問的是 ctx.request.query

說到劫持你可能會想到 Object.defineProperty,在 Kao 內部使用的是 ES6 提供的對象的 settergetter,效果也是一樣的。所以要實現 ctx,我們首先要實現 requestresponse

在此之前,需要修改下 createContext 方法:

// 這三個都是對象
const context = require('./context')
const request = require('./request')
const response = require('./response')

module.exports = class Coa {
  constructor() {
    this.context = context
    this.request = request
    this.response = response
  }
  createContext(req, res) {
    const ctx = Object.create(this.context)
    // 將擴展的 request、response 掛載到 ctx 上
    // 使用 Object.create 創建以傳入參數為原型的對象,避免添加屬性時因為衝突影響到原對象
    const request = ctx.request = Object.create(this.request)
    const response = ctx.response = Object.create(this.response)
    
    ctx.app = request.app = response.app = this;
    // 掛載原生屬性
    ctx.req = request.req = response.req = req
    ctx.res = request.res = response.res = res
    
    request.ctx = response.ctx = ctx;
    request.response = response;
    response.request = request;
    
    return ctx
  }
}

上面一堆花里胡哨的賦值,是為了能通過多種途徑獲取屬性。比如獲取 query 屬性,可以有 ctx.queryctx.request.queryctx.app.query 等等的方式。

如果你覺得看起來有點冗餘,也可以主要理解這幾行,因為我們實現源碼時也就用到下面這些:

const request = ctx.request = Object.create(this.request)
const response = ctx.response = Object.create(this.response)

ctx.req = request.req = response.req = req
ctx.res = request.res = response.res = res

request

request.js

const url = require('url')

module.exports = {
 /* 查看這兩步操作
  * const request = ctx.request = Object.create(this.request)
  * ctx.req = request.req = response.req = req 
  * 
  * 此時的 this 是指向 ctx,所以這裏的 this.req 訪問的是原生屬性 req
  * 同樣,也可以通過 this.request.req 來訪問
  */
  // 請求的 query 參數
  get query() {
    return url.parse(this.req.url).query
  },
  // 請求的路徑
  get path() {
    return url.parse(this.req.url).pathname
  },
  // 請求的方法
  get method() {
    return this.req.method.toLowerCase()
  }
}

response

response.js

module.exports = {
  // 這裏的 this.res 也和上面同理 
  // 返回的狀態碼
  get status() {
    return this.res.statusCode
  },
  set status(val) {
    return this.res.statusCode = val
  },
  // 返回的輸出內容
  get body() {
    return this._body
  },
  set body(val) {
    return this._body = val
  },
  // 設置頭部
  set(filed, val) {
    if (typeof filed === 'string') {
      this.res.setHeader(filed, val)
    }
    if (toString.call(filed) === '[object Object]') {
      for (const key in filed) {
        this.set(key, filed[key])
      }
    }
  }
}

屬性代理

通過上面的實現,我們可以使用 ctx.request.query 來訪問到擴展的屬性。但是在實際應用中,更常用的是 ctx.query。不過 query 是在 request 的屬性,通過 ctx.query 是無法訪問的。

這時只需稍微做個代理,在訪問 ctx.query 時,將 ctx.request.query 返回就可以實現上面的效果。

context.js:

module.exports = {
    get query() {
        return this.request.query
    }
}

實際的代碼中會有很多擴展的屬性,總不可能一個一個去寫吧。為了優雅的代理屬性,Koa 使用 delegates 包實現。這裏我就直接簡單封裝下代理函數,代理函數主要用到__defineGetter____defineSetter__ 兩個方法。

在對象上都會帶有 __defineGetter____defineSetter__,它們可以將一個函數綁定在當前對象的指定屬性上,當屬性被獲取或賦值時,綁定的函數就會被調用。就像這樣:

let obj = {}
let obj1 = {
    name: 'JoJo'
}
obj.__defineGetter__('name', function(){
    return obj1.name
})

此時訪問 obj.name,獲取到的是 obj1.name 的值。

了解這個兩個方法的用處后,接下來開始修改 context.js

const proto = module.exports = {
}

// getter代理
function delegateGetter(prop, name){
  proto.__defineGetter__(name, function(){
    return this[prop][name]
  })
}
// setter代理
function delegateSetter(prop, name){
  proto.__defineSetter__(name, function(val){
    return this[prop][name] = val
  })
}
// 方法代理
function delegateMethod(prop, name){
  proto[name] = function() {
    return this[prop][name].apply(this[prop], arguments)
  }
}

delegateGetter('request', 'query')
delegateGetter('request', 'path')
delegateGetter('request', 'method')

delegateGetter('response', 'status')
delegateSetter('response', 'status')
delegateGetter('response', 'body')
delegateSetter('response', 'body')
delegateMethod('response', 'set')

中間件原理

中間件思想是 Koa 最精髓的地方,為擴展功能提供很大的幫助。這也是它雖然小,卻很強大的原因。還有一個優點,中間件使功能模塊的職責更加分明,一個功能就是一个中間件,多个中間件組合起來成為一個完整的應用。

下面是著名的“洋蔥模型”。這幅圖很形象的表達了中間件思想的作用,它就像一個流水線一樣,上游加工后的東西傳遞給下游,下游可以繼續接着加工,最終輸出加工結果。

原理分析

在調用 use 註冊中間件的時候,內部會將每个中間件存儲到數組中,執行中間件時,為其提供 next 參數。調用 next 即執行下一个中間件,以此類推。當數組中的中間件執行完畢后,再原路返回。就像這樣:

app.use((ctx, next) => {
  console.log('1 start')
  next()
  console.log('1 end')
})

app.use((ctx, next) => {
  console.log('2 start')
  next()
  console.log('2 end')
})

app.use((ctx, next) => {
  console.log('3 start')
  next()
  console.log('3 end')
})

輸出結果如下:

1 start
2 start
3 start
3 end
2 end
1 end

有點數據結構知識的同學,很快就想到這是一個“棧”結構,執行的順序符合“先入后出”。

下面我將內部中間件實現原理進行簡化,模擬中間件執行:

function next1() {
  console.log('1 start')
  next2()
  console.log('1 end')
}
function next2() {
  console.log('2 start')
  next3()
  console.log('2 end')
}
function next3() {
  console.log('3 start')
  console.log('3 end')
}
next1()

執行過程:

  1. 調用 next1,將其入棧執行,輸出 1 start
  2. 遇到 next2 函數,將其入棧執行,輸出 2 start
  3. 遇到 next3 函數,將其入棧執行,輸出 3 start
  4. 輸出 3 end,函數執行完畢,next3 彈出棧
  5. 輸出 2 end,函數執行完畢,next2 彈出棧
  6. 輸出 1 end,函數執行完畢,next1 彈出棧
  7. 棧空,全部執行完畢

相信通過這個簡單的例子,都大概明白中間件的執行過程了吧。

原理實現

中間件原理實現的關鍵點主要是 ctxnext 的傳遞。

function compose(middleware) {
  return function(ctx) {
    return dispatch(0)
    function dispatch(i){
      // 取出中間件
      let fn = middleware[i]
      if (!fn) {
        return
      }
      // dispatch.bind(null, i + 1) 為應用中間件接受到的 next
      // next 即下一個應用中間件
      fn(ctx, dispatch.bind(null, i + 1))
    }
  }
}

可以看到,實現過程本質是函數的遞歸調用。在內部實現時,其實 next 沒有做什麼神奇的操作,它就是下一个中間件調用的函數,作為參數傳入供使用者調用。

下面我們來單獨測試 compose,你可以將它粘貼到控制台上運行:

function next1(ctx, next) {
  console.log('1 start')
  next()
  console.log('1 end')
}
function next2(ctx, next) {
  console.log('2 start')
  next()
  console.log('2 end')
}
function next3(ctx, next) {
  console.log('3 start')
  next()
  console.log('3 end')
}

let ctx = {}
let fn = compose([next1, next2, next3])
fn(ctx)

最後,因為 Koa 中間件是可以使用 async/await 異步執行的,所以還需要修改下 compose 返回 Promise

function compose(middleware) {
  return function(ctx) {
    return dispatch(0)
    function dispatch(i){
      // 取出中間件
      let fn = middleware[i]
      if (!fn) {
        return Promise.resolve()
      }
      // dispatch.bind(null, i + 1) 為應用中間件接受到的 next
      // next 即下一個應用中間件
      try {
        return Promise.resolve( fn(ctx, dispatch.bind(null, i + 1)) )
      } catch (error) {
        return Promise.reject(error)
      }
    }
  }
}

應用

實現完成中間件的邏輯后,將它應用到迷你版Koa中,原來的代碼邏輯要做一些修改(部分代碼忽略)

application.js:

module.exports = class Coa {
  constructor() {
    // 存儲中間件的數組 
    this.middleware = []
  }

  use(fn) {
    if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
    // 將中間件加入數組
    this.middleware.push(fn)
    return this
  }
  
  listen(...args) {
    const server = http.createServer(this.callback())
    server.listen(...args)
  }

  callback() {
    const handleRequest = (req, res) => {
      // 創建上下文
      const ctx = this.createContext(req, res)
      // fn 為第一個應用中間件
      const fn = this.compose(this.middleware)
      // 在所有中間件執行完畢后 respond 函數用於處理 ctx.body 輸出
      return fn(ctx).then(() => respond(ctx)).catch(console.error)
    }
    return handleRequest
  }
  
  compose(middleware) {
    return function(ctx) {
      return dispatch(0)
      function dispatch(i){
        let fn = middleware[i]
        if (!fn) {
          return Promise.resolve()
        }
        // dispatch.bind(null, i + 1) 為應用中間件接受到的 next
        try {
          return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
        } catch (error) {
          return Promise.reject(error)
        }
      }
    }
  }
}

function respond(ctx) {
  let res = ctx.res
  let body = ctx.body
  if (typeof body === 'string') {
    return res.end(body)
  }
  if (typeof body === 'object') {
    return res.end(JSON.stringify(body))
  }
}

完整實現

application.js:

const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')

module.exports = class Coa {
  constructor() {
    this.middleware = []
    this.context = context
    this.request = request
    this.response = response
  }

  use(fn) {
    if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
    this.middleware.push(fn)
    return this
  }

  listen(...args) {
    const server = http.createServer(this.callback())
    server.listen(...args)
  }

  callback() {
    const handleRequest = (req, res) => {
      // 創建上下文
      const ctx = this.createContext(req, res)
      // fn 為第一個應用中間件
      const fn = this.compose(this.middleware)
      return fn(ctx).then(() => respond(ctx)).catch(console.error)
    }
    return handleRequest
  }

  // 創建上下文
  createContext(req, res) {
    const ctx = Object.create(this.context)
    // 處理過的屬性
    const request = ctx.request = Object.create(this.request)
    const response = ctx.response = Object.create(this.response)
    // 原生屬性
    ctx.app = request.app = response.app = this;
    ctx.req = request.req = response.req = req
    ctx.res = request.res = response.res = res

    request.ctx = response.ctx = ctx;
    request.response = response;
    response.request = request;

    return ctx
  }

  // 中間件處理邏輯實現
  compose(middleware) {
    return function(ctx) {
      return dispatch(0)
      function dispatch(i){
        let fn = middleware[i]
        if (!fn) {
          return Promise.resolve()
        }
        // dispatch.bind(null, i + 1) 為應用中間件接受到的 next
        // next 即下一個應用中間件
        try {
          return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
        } catch (error) {
          return Promise.reject(error)
        }
      }
    }
  }
}

// 處理 body 不同類型輸出
function respond(ctx) {
  let res = ctx.res
  let body = ctx.body
  if (typeof body === 'string') {
    return res.end(body)
  }
  if (typeof body === 'object') {
    return res.end(JSON.stringify(body))
  }
}

寫在最後

本文的簡單實現了 Koa 主要的功能。有興趣最好還是自己去看源碼,實現自己的迷你版 Koa。其實 Koa 的源碼不算多,總共4個文件,全部代碼包括註釋也就 1800 行左右。而且邏輯不會很難,很推薦閱讀,尤其適合源碼入門級別的同學觀看。

最後附上完整實現的代碼:github

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準