ゼロからType Script版Kooaを実現

32948 ワード

この文章は何が話せますか?
  • どうやってゼロからコア機能をカバーするNode.jsクラス
  • を完成しますか?
  • KKAコードの書き方のいくつかの理由をコードレベルから説明します。なぜ中間部品がnext関数、ctxがどのようにして来たのかと要求は何の関係ですか?
    Kooaクラスライブラリは主に以下のいくつかの重要な特性があることを知っています。
  • は、玉葱リングモデルのミドルウェア機構
  • をサポートする。
  • カプセル化request、reponseはcontextオブジェクトを提供し、http操作が便利です。
  • 非同期関数、ミドルウェアのエラー処理機構
  • 第一歩:ベースServer運転
    目標:基礎を完成して新しいKooa Serverを実行します。
  • は、ap.listen傍受ポートをサポートし、Server
  • を起動する。
  • サポートap.use追加クラスmiddleware処理関数
  • コアコードは以下の通りです。
    class Koa {
      private middleware: middlewareFn = () => {};
      constructor() {}
      listen(port: number, cb: noop) {
        const server = http.createServer((req, res) => {
          this.middleware(req, res);
        });
        return server.listen(port, cb);
      }
      use(middlewareFn: middlewareFn) {
        this.middleware = middlewareFn;
        return this;
      }
    }
    
    const app = new Koa();
    app.use((req, res) => {
      res.writeHead(200);
      res.end("A request come in");
    });
    app.listen(3000, () => {
      console.log("Server listen on port 3000");
    });
    
    第二ステップ:オニオンリング中間部品機構の実現
    目標:これから私達はlistenとuseの方法を改善して、玉葱リングの中間部品のモデルを実現します。
    [外部チェーン写真の転載失敗(img-qyztqNsK-158619865571)(.asets/koaumidedleare.png)]
    以下のコードに示すように、このステップでは、複数の中間部品を追加することをサポートし、中間部品はタマネギ圏(同様の深さ再帰呼び出し)の方式で順次実行してほしい。
    app.use(async (req, res, next) => {
      console.log("middleware 1 start");
      //                   
      await next();
      console.log("middleware 1 end");
    });
    app.use(async (req, res, next) => {
      console.log("middleware 2 start");
      await next();
      console.log("middleware 2 end");
    });
    app.use(async (req, res, next) => {
      res.writeHead(200);
      res.end("An request come in");
      await next();
    });
    app.listen(3000, () => {
      console.log("Server listen on port 3000");
    });
    
    上記のデモには三つの注意点があります。
  • ミドルウェア内のnext()関数は必須であり、一回だけ
  • を呼び出すことができます。
  • next関数を呼び出すときはawait
  • を使用しなければなりません。
    私たちは次のコードの中でこれらの使い方の原因を一つ一つ分析します。具体的にどのようにこのオニオンリング機構を実現するかを見てみます。
    class Koa {
      ...
      use(middlewareFn: middlewareFn) {
        // 1、  use ,         middleware
        this.middlewares.push(middlewareFn);
        return this;
      }
      listen(port: number, cb: noop) {
        // 2、   composeMiddleware           [   ]     , createServer        
        //           composeMiddleware,     ,            
        // BTW:         fn   listen           ,                  middleware
        const fn = composeMiddleware(this.middlewares);
        const server = http.createServer(async (req, res) => {
          await fn(req, res);
        });
        return server.listen(port, cb);
      }
    }
    
    // 3、        :
    //   :        
    //   :            
    function composeMiddleware(middlewares: middlewareFn[]) {
      return (req: IncomingMessage, res: ServerResponse) => {
        let start = -1;
        // dispatch:   i      
        function dispatch(i: number) {
          //                  ,                
          //            start < i,   next()    start === i
          //       next(),               start === i  ,      start >= i
          if (i <= start) {
            return Promise.reject(new Error("next() call more than once!"));
          }
          if (i >= middlewares.length) {
            return Promise.resolve();
          }
          start = i;
          const middleware = middlewares[i];
          //     !!!
          //    i      ,  dispatch(i+1)  next           
          return middleware(req, res, () => {
            return dispatch(i + 1);
          });
        }
        return dispatch(0);
      };
    }
    
    主にPromiseに関するいくつかの知識点:
  • async関数はPromiseオブジェクトを返します。
  • async関数内部でawait呼出時にawait関数の実行が一時停止されます。戻り結果を待って、下の
  • を続けます。
  • async関数の内部にエラーが発生し、戻ってきたPromiseがreject状態
  • になります。
    今私たちが振り返る前に提出したいくつかの問題:
  • koaミドルウェアはなぜ必須で、一度だけnext関数
              next,     dispatch(i+1),            ,            
     
         next              
    
  • を呼び出すことができますか?
  • next()なぜawaitを追加する必要がありますか?
    第三ステップ:Contect提供
    目標:パッケージングContect、request、reponseの便利な操作方式を提供する。
                  ,     await next(),   next()【        】      ,       
    
    第四ステップ:非同期関数エラー処理メカニズム
    ターゲット:ap.on(「error」)によるエラーイベントの処理異常の監視
    私達はKooの中でどのように異常を処理するかを思い出します。コードは以下のようになるかもしれません。
    // 1、   KoaRequest、KoaResponse、KoaContext
    interface KoaContext {
      request?: KoaRequest;
      response?: KoaResponse;
      body: String | null;
    }
    const context: KoaContext = {
      get body() {
        return this.response!.body;
      },
      set body(body) {
        this.response!.body = body;
      }
    };
    
    function composeMiddleware(middlewares: middlewareFn[]) {
      return (context: KoaContext) => {
        let start = -1;
        function dispatch(i: number) {
          // ..      ..
          // 2、        context  
          middleware(context, () => {
            return dispatch(i + 1);
          });
        }
        return dispatch(0);
      };
    }
    
    class Koa {
      private context: KoaContext = Object.create(context);
      listen(port: number, cb: noop) {
        const fn = composeMiddleware(this.middlewares);
        const server = http.createServer(async (req, res) => {
          // 3、  req、res  context  
          //       :context         ,        this.context
          //   context        ,                 context  
          const context = this.createContext(req, res);
          await fn(context);
          if (context.response && context.response.res) {
            context.response.res.writeHead(200);
            context.response.res.end(context.body);
          }
        });
        return server.listen(port, cb);
      }
      // 4、  context  
      createContext(req: IncomingMessage, res: ServerResponse): KoaContext {
        //       Object.create       ?
        //              request、response、context     
        const request = Object.create(this.request);
        const response = Object.create(this.response);
        const context = Object.create(this.context);
        request.req = req;
        response.res = res;
        context.request = request;
        context.response = response;
        return context;
      }
    }
    
    上のコードからコアが見えます。
  • KKAインスタンスアプリは、イベントトリガ、イベント傍受能力をサポートする必要がある
  • は、非同期関数異常の捕獲を必要とし、errorイベント
  • をトリガする。
    具体的なコードはどうやって実現されますか?
    app.use(async (context, next) => {
      console.log("middleware 2 start");
      // throw new Error("   ");
      await next();
      console.log("middleware 2 end");
    });
    
    // koa      :  error  
    app.on("error", (error, context) => {
      console.error(`  ${context.url}     `);
    });
    
    締め括りをつける
    これまでType Scriptを使って、簡単版Kooaクラスライブラリを完成しました。サポートしました。
  • オニオンリング中間部品機構
  • Contectパッケージrequest、reponse
  • 非同期異常エラー処理機構
  • 完全なDemoコードは、koa 2-referenceを参照することができます。
    より多くの素晴らしい文章、皆様を歓迎します。私達は毎週いくつかの高品質の先端領域に関する文章を発表します。
    参考資料
  • Kooa 2中間部品機構を深く理解する
  • は、現在の最も完全なソースコード解析ガイドかもしれません。