成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

玩轉(zhuǎn)Koa -- koa-router原理解析

wthee / 2408人閱讀

摘要:四路由注冊(cè)構(gòu)造函數(shù)首先看了解一下構(gòu)造函數(shù)限制必須采用關(guān)鍵字服務(wù)器支持的請(qǐng)求方法,后續(xù)方法會(huì)用到保存前置處理函數(shù)存儲(chǔ)在構(gòu)造函數(shù)中初始化的和屬性最為重要,前者用來保存前置處理函數(shù),后者用來保存實(shí)例化的對(duì)象。

一、前言

??Koa為了保持自身的簡(jiǎn)潔,并沒有捆綁中間件。但是在實(shí)際的開發(fā)中,我們需要和形形色色的中間件打交道,本文將要分析的是經(jīng)常用到的路由中間件 -- koa-router。

??如果你對(duì)Koa的原理還不了解的話,可以先查看Koa原理解析。

二、koa-router概述

??koa-router的源碼只有兩個(gè)文件:router.js和layer.js,分別對(duì)應(yīng)Router對(duì)象和Layer對(duì)象。

??Layer對(duì)象是對(duì)單個(gè)路由的管理,其中包含的信息有路由路徑(path)、路由請(qǐng)求方法(method)和路由執(zhí)行函數(shù)(middleware),并且提供路由的驗(yàn)證以及params參數(shù)解析的方法。

??相比較Layer對(duì)象,Router對(duì)象則是對(duì)所有注冊(cè)路由的統(tǒng)一處理,并且它的API是面向開發(fā)者的。

??接下來從以下幾個(gè)方面全面解析koa-router的實(shí)現(xiàn)原理:

Layer對(duì)象的實(shí)現(xiàn)

路由注冊(cè)

路由匹配

路由執(zhí)行流程

三、Layer

??Layer對(duì)象主要是對(duì)單個(gè)路由的管理,是整個(gè)koa-router中最小的處理單元,后續(xù)模塊的處理都離不開Layer中的方法,這正是首先介紹Layer的重要原因。

function Layer(path, methods, middleware, opts) {
  this.opts = opts || {};
  // 支持路由別名
  this.name = this.opts.name || null;
  this.methods = [];
  this.paramNames = [];
  // 將路由執(zhí)行函數(shù)保存在stack中,支持輸入多個(gè)處理函數(shù)
  this.stack = Array.isArray(middleware) ? middleware : [middleware];

  methods.forEach(function(method) {
    var l = this.methods.push(method.toUpperCase());
    // HEAD請(qǐng)求頭部信息與GET一致,這里就一起處理了。
    if (this.methods[l-1] === "GET") {
      this.methods.unshift("HEAD");
    }
  }, this);

  // 確保類型正確
  this.stack.forEach(function(fn) {
    var type = (typeof fn);
    if (type !== "function") {
      throw new Error(
        methods.toString() + " `" + (this.opts.name || path) +"`: `middleware` "
        + "must be a function, not `" + type + "`"
      );
    }
  }, this);

  this.path = path;
  // 1、根據(jù)路由路徑生成路由正則表達(dá)式
  // 2、將params參數(shù)信息保存在paramNames數(shù)組中
  this.regexp = pathToRegExp(path, this.paramNames, this.opts);
};

??Layer構(gòu)造函數(shù)主要用來初始化路由路徑、路由請(qǐng)求方法數(shù)組、路由處理函數(shù)數(shù)組、路由正則表達(dá)式以及params參數(shù)信息數(shù)組,其中主要采用path-to-regexp方法根據(jù)路徑字符串生成正則表達(dá)式,通過該正則表達(dá)式,可以實(shí)現(xiàn)路由的匹配以及params參數(shù)的捕獲:

// 驗(yàn)證路由
Layer.prototype.match = function (path) {
  return this.regexp.test(path);
}

// 捕獲params參數(shù)
Layer.prototype.captures = function (path) {
  // 后續(xù)會(huì)提到 對(duì)于路由級(jí)別中間件 無需捕獲params
  if (this.opts.ignoreCaptures) return [];
  return path.match(this.regexp).slice(1);
}

??根據(jù)paramNames中的參數(shù)信息以及captrues方法,可以獲取到當(dāng)前路由params參數(shù)的鍵值對(duì):

Layer.prototype.params = function (path, captures, existingParams) {
  var params = existingParams || {};
  for (var len = captures.length, i=0; i

??需要注意上述代碼中的safeDecodeURIComponent方法,為了避免服務(wù)器收到不可預(yù)知的請(qǐng)求,對(duì)于任何用戶輸入的作為URI部分的內(nèi)容都需要采用encodeURIComponent進(jìn)行轉(zhuǎn)義,否則當(dāng)用戶輸入的內(nèi)容中含有"&"、"="、"?"等字符時(shí),會(huì)出現(xiàn)預(yù)料之外的情況。而當(dāng)我們獲取URL上的參數(shù)時(shí),則需要通過decodeURIComponent進(jìn)行解碼,而decodeURIComponent只能解碼由encodeURIComponent方法或者類似方法編碼,如果編碼方法不符合要求,decodeURIComponent則會(huì)拋出URIError,所以作者在這里對(duì)該方法進(jìn)行了安全化的處理:

function safeDecodeURIComponent(text) {
  try {
    return decodeURIComponent(text);
  } catch (e) {
    // 編碼方式不符合要求,返回原字符串
    return text;
  }
}

??Layer還提供了對(duì)于單個(gè)param前置處理的方法:

Layer.prototype.param = function (param, fn) {
  var stack = this.stack;
  var params = this.paramNames;
  var middleware = function (ctx, next) {
    return fn.call(this, ctx.params[param], ctx, next);
  };
  middleware.param = param;
  var names = params.map(function (p) {
    return p.name;
  });
  var x = names.indexOf(param);
  if (x > -1) {
    stack.some(function (fn, i) {
      if (!fn.param || names.indexOf(fn.param) > x) {
        // 將單個(gè)param前置處理函數(shù)插入正確的位置
        stack.splice(i, 0, middleware);
        return true; // 跳出循環(huán)
      }
    });
  }

  return this;
};

??上述代碼中通過some方法尋找單個(gè)param處理函數(shù)的原因在于以下兩點(diǎn):

保持param處理函數(shù)位于其他路由處理函數(shù)的前面;

路由中存在多個(gè)param參數(shù),需要保持param處理函數(shù)的前后順序。

Layer.prototype.setPrefix = function (prefix) {
  if (this.path) {
    this.path = prefix + this.path; // 拼接新的路由路徑
    this.paramNames = [];
    // 根據(jù)新的路由路徑字符串生成正則表達(dá)式
    this.regexp = pathToRegExp(this.path, this.paramNames, this.opts);
  }
  return this;
};

??Layer中的setPrefix方法用于設(shè)置路由路徑的前綴,這在嵌套路由的實(shí)現(xiàn)中尤其重要。

??最后,Layer還提供了根據(jù)路由生成url的方法,主要采用path-to-regexp的compile和parse對(duì)路由路徑中的param進(jìn)行替換,而在拼接query的環(huán)節(jié),正如前面所說需要對(duì)鍵值對(duì)進(jìn)行繁瑣的encodeURIComponent操作,作者采用了urijs提供的簡(jiǎn)潔api進(jìn)行處理。

四、路由注冊(cè)
1、Router構(gòu)造函數(shù)

??首先看了解一下Router構(gòu)造函數(shù):

function Router(opts) {
  if (!(this instanceof Router)) {
    // 限制必須采用new關(guān)鍵字
    return new Router(opts);
  }

  this.opts = opts || {};
  // 服務(wù)器支持的請(qǐng)求方法, 后續(xù)allowedMethods方法會(huì)用到
  this.methods = this.opts.methods || [
    "HEAD",
    "OPTIONS",
    "GET",
    "PUT",
    "PATCH",
    "POST",
    "DELETE"
  ];

  this.params = {}; // 保存param前置處理函數(shù)
  this.stack = []; // 存儲(chǔ)layer
};

??在構(gòu)造函數(shù)中初始化的params和stack屬性最為重要,前者用來保存param前置處理函數(shù),后者用來保存實(shí)例化的Layer對(duì)象。并且這兩個(gè)屬性與接下來要講的路由注冊(cè)息息相關(guān)。

??koa-router中提供兩種方式注冊(cè)路由:

具體的HTTP動(dòng)詞注冊(cè)方式,例如:router.get("/users", ctx => {})

支持所有的HTTP動(dòng)詞注冊(cè)方式,例如:router.all("/users", ctx => {})

2、http METHODS

??源碼中采用methods模塊獲取HTTP請(qǐng)求方法名,該模塊內(nèi)部實(shí)現(xiàn)主要依賴于http模塊:

http.METHODS && http.METHODS.map(function lowerCaseMethod (method) {
  return method.toLowerCase()
})
3、router.verb() and router.all()

??這兩種注冊(cè)路由的方式的內(nèi)部實(shí)現(xiàn)基本類似,下面以router.verb()的源碼為例:

methods.forEach(function (method) {
  Router.prototype[method] = function (name, path, middleware) {
    var middleware;

    // 1、處理是否傳入name參數(shù)
    // 2、middleware參數(shù)支持middleware1, middleware2...的形式
    if (typeof path === "string" || path instanceof RegExp) {
      middleware = Array.prototype.slice.call(arguments, 2);
    } else {
      middleware = Array.prototype.slice.call(arguments, 1);
      path = name;
      name = null;
    }
    
    // 路由注冊(cè)的核心處理邏輯
    this.register(path, [method], middleware, {
      name: name
    });

    return this;
  };
});

??該方法第一部分是對(duì)傳入?yún)?shù)的處理,對(duì)于middleware參數(shù)的處理會(huì)讓大家聯(lián)想到ES6中的rest參數(shù),但是rest參數(shù)與arguments其中一個(gè)致命的區(qū)別:

  rest參數(shù)只包含那些沒有對(duì)應(yīng)形參的實(shí)參,而arguments則包含傳給函數(shù)的所有實(shí)參。

??如果采用rest參數(shù)的方式,上述函數(shù)則必須要求開發(fā)者傳入name參數(shù)。但是也可以將name和path參數(shù)整合成對(duì)象,再結(jié)合rest參數(shù):

Router.prototype[method] = function (options, ...middleware) {
  let { name, path } = options
  if (typeof options === "string" || options instanceof RegExp) {
    path = options
    name = null
  }
  // ...
  return this;
};

??采用ES6的新特性,代碼變得簡(jiǎn)潔多了。

??第二部分是register方法,傳入的method參數(shù)的形式就是router.verb()與router.all()的最大區(qū)別,在router.verb()中傳入的method是單個(gè)方法,后者則是以數(shù)組的形式傳入HTTP所有的請(qǐng)求方法,所以對(duì)于這兩種注冊(cè)方法的實(shí)現(xiàn),本質(zhì)上是沒有區(qū)別的。

4、register
Router.prototype.register = function (path, methods, middleware, opts) {
  opts = opts || {};

  var router = this;
  var stack = this.stack;

  // 注冊(cè)路由中間件時(shí),允許path為數(shù)組
  if (Array.isArray(path)) {
    path.forEach(function (p) {
      router.register.call(router, p, methods, middleware, opts);
    });
    return this;
  }

  // 實(shí)例化Layer
  var route = new Layer(path, methods, middleware, {
    end: opts.end === false ? opts.end : true,
    name: opts.name,
    sensitive: opts.sensitive || this.opts.sensitive || false,
    strict: opts.strict || this.opts.strict || false,
    prefix: opts.prefix || this.opts.prefix || "",
    ignoreCaptures: opts.ignoreCaptures
  });

  // 設(shè)置前綴
  if (this.opts.prefix) {
    route.setPrefix(this.opts.prefix);
  }

  // 設(shè)置param前置處理函數(shù)
  Object.keys(this.params).forEach(function (param) {
    route.param(param, this.params[param]);
  }, this);

  stack.push(route);

  return route;
};

??register方法主要負(fù)責(zé)實(shí)例化Layer對(duì)象、更新路由前綴和前置param處理函數(shù),這些操作在Layer中已經(jīng)提及過,相信大家應(yīng)該輕車熟路了。

5、use

??熟悉Koa的同學(xué)都知道use是用來注冊(cè)中間件的方法,相比較Koa中的全局中間件,koa-router的中間件則是路由級(jí)別的。

Router.prototype.use = function () {
  var router = this;
  var middleware = Array.prototype.slice.call(arguments);
  var path;

  // 支持多路徑在于中間件可能作用于多條路由路徑
  if (Array.isArray(middleware[0]) && typeof middleware[0][0] === "string") {
    middleware[0].forEach(function (p) {
      router.use.apply(router, [p].concat(middleware.slice(1)));
    });

    return this;
  }
  // 處理路由路徑參數(shù)
  var hasPath = typeof middleware[0] === "string";
  if (hasPath) {
    path = middleware.shift();
  }

  middleware.forEach(function (m) {
    // 嵌套路由
    if (m.router) {
      // 嵌套路由扁平化處理
      m.router.stack.forEach(function (nestedLayer) {
        // 更新嵌套之后的路由路徑
        if (path) nestedLayer.setPrefix(path);
        // 更新掛載到父路由上的路由路徑
        if (router.opts.prefix) nestedLayer.setPrefix(router.opts.prefix);

        router.stack.push(nestedLayer);
      }); 

      // 不要忘記將父路由上的param前置處理操作 更新到新路由上。
      if (router.params) {
        Object.keys(router.params).forEach(function (key) {
          m.router.param(key, router.params[key]);
        });
      }
    } else {
      // 路由級(jí)別中間件 創(chuàng)建一個(gè)沒有method的Layer實(shí)例
      router.register(path || "(.*)", [], m, { end: false, ignoreCaptures: !hasPath });
    }
  });

  return this;
};

??koa-router中間件注冊(cè)方法主要完成兩項(xiàng)功能:

將路由嵌套結(jié)構(gòu)扁平化,其中涉及到路由路徑的更新和param前置處理函數(shù)的插入;

路由級(jí)別中間件通過注冊(cè)一個(gè)沒有method的Layer實(shí)例進(jìn)行管理。

五、路由匹配
Router.prototype.match = function (path, method) {
  var layers = this.stack;
  var layer;
  var matched = {
    path: [],
    pathAndMethod: [],
    route: false
  };

  for (var len = layers.length, i = 0; i < len; i++) {
    layer = layers[i];
    if (layer.match(path)) {
      // 路由路徑滿足要求
      matched.path.push(layer);

      if (layer.methods.length === 0 || ~layer.methods.indexOf(method)) {
        // layer.methods.length === 0 該layer為路由級(jí)別中間件
        // ~layer.methods.indexOf(method) 路由請(qǐng)求方法也被匹配
        matched.pathAndMethod.push(layer);
        // 僅當(dāng)路由路徑和路由請(qǐng)求方法都被滿足才算是路由被匹配
        if (layer.methods.length) matched.route = true;
      }
    }
  }
  return matched;
};

??match方法主要通過layer.match方法以及methods屬性對(duì)layer進(jìn)行篩選,返回的matched對(duì)象包含以下幾個(gè)部分:

path: 保存所有路由路徑被匹配的layer;

pathAndMethod: 在路由路徑被匹配的前提下,保存路由級(jí)別中間件和路由請(qǐng)求方法被匹配的layer;

route: 僅當(dāng)存在路由路徑和路由請(qǐng)求方法都被匹配的layer,才能算是本次路由被匹配上。

??另外,在ES7之前,對(duì)于判斷數(shù)組是否包含一個(gè)元素,都需要通過indexOf方法來實(shí)現(xiàn), 而該方法返回元素的下標(biāo),這樣就不得不通過與-1的比較得到布爾值:

  if (layer.methods.indexOf(method) > -1) {
    ...
  }

??而作者巧妙地利用位運(yùn)算省去了“討厭的-1”,當(dāng)然在ES7中可以愉快地使用includes方法:

  if (layer.methods.includes(method)) {
    ...
  }
六、路由執(zhí)行流程

??理解koa-router中路由的概念以及路由注冊(cè)的方式,接下來就是如何作為一個(gè)中間件在koa中執(zhí)行。

??koa中注冊(cè)koa-router中間件的方式如下:

const Koa = require("koa");
const Router = require("koa-router");

const app = new Koa();
const router = new Router();

router.get("/", (ctx, next) => {
  // ctx.router available
});

app
  .use(router.routes())
  .use(router.allowedMethods());

??從代碼中可以看出koa-router提供了兩個(gè)中間件方法:routes和allowedMethods。

1、allowedMethods()
Router.prototype.allowedMethods = function (options) {
  options = options || {};
  var implemented = this.methods;

  return function allowedMethods(ctx, next) {
    return next().then(function() {
      var allowed = {};

      if (!ctx.status || ctx.status === 404) {
        ctx.matched.forEach(function (route) {
          route.methods.forEach(function (method) {
            allowed[method] = method;
          });
        });

        var allowedArr = Object.keys(allowed);

        if (!~implemented.indexOf(ctx.method)) {
          // 服務(wù)器不支持該方法的情況
          if (options.throw) {
            var notImplementedThrowable;
            if (typeof options.notImplemented === "function") {
              notImplementedThrowable = options.notImplemented();
            } else {
              notImplementedThrowable = new HttpError.NotImplemented();
            }
            throw notImplementedThrowable;
          } else {
            // 響應(yīng) 501 Not Implemented
            ctx.status = 501;
            ctx.set("Allow", allowedArr.join(", "));
          }
        } else if (allowedArr.length) {
          if (ctx.method === "OPTIONS") {
            // 獲取服務(wù)器對(duì)該路由路徑支持的方法集合
            ctx.status = 200;
            ctx.body = "";
            ctx.set("Allow", allowedArr.join(", "));
          } else if (!allowed[ctx.method]) {
            if (options.throw) {
              var notAllowedThrowable;
              if (typeof options.methodNotAllowed === "function") {
                notAllowedThrowable = options.methodNotAllowed();
              } else {
                notAllowedThrowable = new HttpError.MethodNotAllowed();
              }
              throw notAllowedThrowable;
            } else {
              // 響應(yīng) 405 Method Not Allowed
              ctx.status = 405;
              ctx.set("Allow", allowedArr.join(", "));
            }
          }
        }
      }
    });
  };
};

??allowedMethods()中間件主要用于處理options請(qǐng)求,響應(yīng)405和501狀態(tài)。上述代碼中的ctx.matched中保存的正是前面matched對(duì)象中的path(在routes方法中設(shè)置,后面會(huì)提到。),在matched對(duì)象中的path數(shù)組不為空的前提條件下:

服務(wù)器不支持當(dāng)前請(qǐng)求方法,返回501狀態(tài)碼;

當(dāng)前請(qǐng)求方法為OPTIONS,返回200狀態(tài)碼;

path中的layer不支持該方法,返回405狀態(tài);

對(duì)于上述三種情況,服務(wù)器都會(huì)設(shè)置Allow響應(yīng)頭,返回該路由路徑上支持的請(qǐng)求方法。

2、routes()
Router.prototype.routes = Router.prototype.middleware = function () {
  var router = this;
  // 返回中間件處理函數(shù)
  var dispatch = function dispatch(ctx, next) {
    var path = router.opts.routerPath || ctx.routerPath || ctx.path;
    var matched = router.match(path, ctx.method);
    var layerChain, layer, i;

    // 【1】為后續(xù)的allowedMethods中間件準(zhǔn)備
    if (ctx.matched) {
      ctx.matched.push.apply(ctx.matched, matched.path);
    } else {
      ctx.matched = matched.path;
    }

    ctx.router = router;

    // 未匹配路由 直接跳過
    if (!matched.route) return next();

    var matchedLayers = matched.pathAndMethod
    var mostSpecificLayer = matchedLayers[matchedLayers.length - 1]
    ctx._matchedRoute = mostSpecificLayer.path;
    if (mostSpecificLayer.name) {
      ctx._matchedRouteName = mostSpecificLayer.name;
    }
    layerChain = matchedLayers.reduce(function(memo, layer) {
      // 【3】路由的前置處理中間件 主要負(fù)責(zé)將params、路由別名以及捕獲數(shù)組屬性掛載在ctx上下文對(duì)象中。
      memo.push(function(ctx, next) {
        ctx.captures = layer.captures(path, ctx.captures);
        ctx.params = layer.params(path, ctx.captures, ctx.params);
        ctx.routerName = layer.name;
        return next();
      });
      return memo.concat(layer.stack);
    }, []);
    // 【4】利用koa中間件組織的方式,形成一個(gè)‘小洋蔥’模型
    return compose(layerChain)(ctx, next);
  };

  // 【2】router屬性用來use方法中區(qū)別路由級(jí)別中間件
  dispatch.router = this;
  return dispatch;
};

??routes()中間件主要實(shí)現(xiàn)了四大功能。

將matched對(duì)象的path屬性掛載在ctx.matched上,提供給后續(xù)的allowedMethods中間件使用。(見代碼中的【1】)

將返回的dispatch函數(shù)設(shè)置router屬性,以便在前面提到的Router.prototype.use方法中區(qū)別路由級(jí)別中間件和嵌套路由。(見代碼中的【2】)

插入一個(gè)新的路由前置處理中間件,將layer解析出來的params對(duì)象、路由別名以及捕獲數(shù)組掛載在ctx上下文中,這種操作同理Koa在處理請(qǐng)求之前先構(gòu)建context對(duì)象。(見代碼中的【3】)

而對(duì)于路由匹配到眾多l(xiāng)ayer,koa-router通過koa-compose進(jìn)行處理,這和koa對(duì)于中間件處理的方式一樣的,所以koa-router完全就是一個(gè)小型洋蔥模型。

七、總結(jié)

??koa-router雖然是koa的一個(gè)中間件,但是其內(nèi)部也包含眾多的中間件,這些中間件通過Layer對(duì)象根據(jù)路由路徑的不同進(jìn)行劃分,使得它們不再像koa的中間件那樣每次請(qǐng)求都執(zhí)行,而是針對(duì)每次請(qǐng)求采用match方法匹配出相應(yīng)的中間件,再利用koa-compose形成一個(gè)中間件執(zhí)行鏈。

??以上便是koa-router實(shí)現(xiàn)原理的全部?jī)?nèi)容,希望可以幫助你更好的理解koa-router。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101054.html

相關(guān)文章

  • 玩轉(zhuǎn)Koa -- koa-bodyparser原理解析

    摘要:主要通過處理二進(jìn)制數(shù)據(jù)流,但是它并不支持字符編碼方式,需要通過模塊進(jìn)行處理。最后留圖一張往期精彩回顧玩轉(zhuǎn)原理解析玩轉(zhuǎn)核心原理分析 一、前置知識(shí) ??在理解koa-bodyparser原理之前,首先需要了解部分HTTP相關(guān)的知識(shí)。 1、報(bào)文主體 ??HTTP報(bào)文主要分為請(qǐng)求報(bào)文和響應(yīng)報(bào)文,koa-bodyparser主要針對(duì)請(qǐng)求報(bào)文的處理。 ??請(qǐng)求報(bào)文主要由以下三個(gè)部分組成: 報(bào)文頭...

    andycall 評(píng)論0 收藏0
  • 【完結(jié)匯總】iKcamp出品基于Koa2搭建Node.js實(shí)戰(zhàn)共十一堂課(含視頻)

    摘要:云集一線大廠有真正實(shí)力的程序員團(tuán)隊(duì)云集一線大廠經(jīng)驗(yàn)豐厚的碼農(nóng),開源奉獻(xiàn)各教程。融合多種常見的需求場(chǎng)景網(wǎng)絡(luò)請(qǐng)求解析模板引擎靜態(tài)資源日志記錄錯(cuò)誤請(qǐng)求處理。結(jié)合語句中轉(zhuǎn)中間件控制權(quán),解決回調(diào)地獄問題。注意分支中的目錄為當(dāng)節(jié)課程后的完整代碼。 ?? ?與眾不同的學(xué)習(xí)方式,為你打開新的編程視角 獨(dú)特的『同步學(xué)習(xí)』方式 文案講解+視頻演示,文字可激發(fā)深層的思考、視頻可還原實(shí)戰(zhàn)操作過程。 云...

    sPeng 評(píng)論0 收藏0
  • 開始連載啦~每周2更共11堂iKcamp課|基于Koa2搭建Node.js實(shí)戰(zhàn)項(xiàng)目教學(xué)(含視頻)|

    摘要:玩轉(zhuǎn)同時(shí)全面掌握潮流技術(shù)采用新一代的開發(fā)框架更小更富有表現(xiàn)力更健壯。融合多種常見的需求場(chǎng)景網(wǎng)絡(luò)請(qǐng)求解析模板引擎靜態(tài)資源日志記錄錯(cuò)誤請(qǐng)求處理。結(jié)合語句中轉(zhuǎn)中間件控制權(quán),解決回調(diào)地獄問題。注意分支中的目錄為當(dāng)節(jié)課程后的完整代碼。 ?? ?與眾不同的學(xué)習(xí)方式,為你打開新的編程視角 獨(dú)特的『同步學(xué)習(xí)』方式 文案講解+視頻演示,文字可激發(fā)深層的思考、視頻可還原實(shí)戰(zhàn)操作過程。 云集一線大廠...

    B0B0 評(píng)論0 收藏0
  • iKcamp|基于Koa2搭建Node.js實(shí)戰(zhàn)(含視頻)? HTTP請(qǐng)求

    POST/GET請(qǐng)求——常見請(qǐng)求方式處理 ?? iKcamp 制作團(tuán)隊(duì) 原創(chuàng)作者:大哼、阿干、三三、小虎、胖子、小哈、DDU、可木、晃晃 文案校對(duì):李益、大力萌、Au、DDU、小溪里、小哈 風(fēng)采主播:可木、阿干、Au、DDU、小哈 視頻剪輯:小溪里 主站運(yùn)營(yíng):給力xi、xty 教程主編:張利濤 視頻地址:https://www.cctalk.com/v/15114357765870 ...

    張利勇 評(píng)論0 收藏0
  • 不到300行代碼構(gòu)建精簡(jiǎn)的koakoa-router(mini-koa)

    摘要:詳細(xì)代碼如下追蹤賦值里面的是子路由設(shè)計(jì)子路由設(shè)計(jì)這個(gè)比較簡(jiǎn)單,每個(gè)子路由維護(hù)一個(gè)路由監(jiān)聽列表,然后通過調(diào)用的函數(shù)添加到主路由列表上。 showImg(https://segmentfault.com/img/bVbruD0?w=756&h=378); 前言 鑒于之前使用express和koa的經(jīng)驗(yàn),這兩天想嘗試構(gòu)建出一個(gè)koa精簡(jiǎn)版,利用最少的代碼實(shí)現(xiàn)koa和koa-router,同時(shí)...

    tuomao 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<