亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Node.js深入分析Koa源碼

 更新時(shí)間:2022年08月08日 09:06:34   作者:夏安  
本文主要從源碼的角度來(lái)講述 Koa,尤其是其中間件系統(tǒng)是如何實(shí)現(xiàn)的。跟 Express 相比,Koa 的源碼異常簡(jiǎn)潔,Express 因?yàn)榘崖酚上嚓P(guān)的代碼嵌入到了主要邏輯中,因此讀 Express 的源碼可能長(zhǎng)時(shí)間不得要領(lǐng),而直接讀 Koa 的源碼幾乎沒(méi)有什么障礙

Koa 的主要代碼位于根目錄下的 lib 文件夾中,只有 4 個(gè)文件,去掉注釋后的源碼不到 1000 行,下面列出了這 4 個(gè)文件的主要功能。

  • request.js:對(duì) http request 對(duì)象的封裝。
  • response.js:對(duì) http response 對(duì)象的封裝。
  • context.js:將上面兩個(gè)文件的封裝整合到 context 對(duì)象中
  • application.js:項(xiàng)目的啟動(dòng)及中間件的加載。

1. Koa 的啟動(dòng)過(guò)程

首先回憶一下一個(gè) Koa 應(yīng)用的結(jié)構(gòu)是什么樣子的。

const Koa = require('Koa');
const app = new Koa();
//加載一些中間件
app.use(...);
app.use(....);
app.use(.....);
app.listen(3000);

Koa 的啟動(dòng)過(guò)程大致分為以下三個(gè)步驟:

  • 引入 Koa 模塊,調(diào)用構(gòu)造方法新建一個(gè) app 對(duì)象。
  • 加載中間件。
  • 調(diào)用 listen 方法監(jiān)聽端口。

我們逐步來(lái)看上面三個(gè)步驟在源碼中的實(shí)現(xiàn)。

首先是類和構(gòu)造函數(shù)的定義,這部分代碼位于 application.js 中。

// application.js
const response = require('./response')
const context = require('./context')
const request = require('./request')
const Emitter = require('events')
const util = require('util')
// ...... 其他模塊
module.exports = class Application extends Emitter {
  constructor (options) {
    super()
    options = options || {}
    this.proxy = options.proxy || false
    this.subdomainOffset = options.subdomainOffset || 2
    this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For'
    this.maxIpsCount = options.maxIpsCount || 0
    this.env = options.env || process.env.NODE_ENV || 'development'
    if (options.keys) this.keys = options.keys
    this.middleware = []
    // 下面的 context,request,response 分別是從其他三個(gè)文件夾中引入的
    this.context = Object.create(context)
    this.request = Object.create(request)
    this.response = Object.create(response)
    // util.inspect.custom support for node 6+
    /* istanbul ignore else */
    if (util.inspect.custom) {
      this[util.inspect.custom] = this.inspect
    }
  }
  // ...... 其他類方法
}

首先我們注意到該類繼承于 Events 模塊,然后當(dāng)我們調(diào)用 Koa 的構(gòu)造函數(shù)時(shí),會(huì)初始化一些屬性和方法,例如以context/response/request為原型創(chuàng)建的新的對(duì)象,還有管理中間件的 middleware 數(shù)組等。

2. 中間件的加載

中間件的本質(zhì)是一個(gè)函數(shù)。在 Koa 中,該函數(shù)通常具有 ctxnext 兩個(gè)參數(shù),分別表示封裝好的 res/req 對(duì)象以及下一個(gè)要執(zhí)行的中間件,當(dāng)有多個(gè)中間件的時(shí)候,本質(zhì)上是一種嵌套調(diào)用,就像洋蔥圖一樣。

Koa 和 Express 在調(diào)用上都是通過(guò)調(diào)用 app.use() 的方式來(lái)加載一個(gè)中間件,但內(nèi)部的實(shí)現(xiàn)卻大不相同,我們先來(lái)看application.js 中相關(guān)方法的定義。

/**
 * Use the given middleware `fn`.
 *
 * Old-style middleware will be converted.
 *
 * @param {Function} fn
 * @return {Application} self
 * @api public
 */
use(fn) {
  if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')
  debug('use %s', fn._name || fn.name || '-')
  this.middleware.push(fn)
  return this
}

Koa 在 application.js 中維持了一個(gè) middleware 的數(shù)組,如果有新的中間件被加載,就 push 到這個(gè)數(shù)組中,除此之外沒(méi)有任何多余的操作,相比之下,Express 的 use 方法就麻煩得多,讀者可以自行參閱其源碼。

此外,之前版本中該方法中還增加了 isGeneratorFunction 判斷,這是為了兼容 Koa1.x 的中間件而加上去的,在 Koa1.x 中,中間件都是 Generator 函數(shù),Koa2 使用的 async 函數(shù)是無(wú)法兼容之前的代碼的,因此 Koa2 提供了 convert 函數(shù)來(lái)進(jìn)行轉(zhuǎn)換,關(guān)于這個(gè)函數(shù)我們不再介紹。

if (isGeneratorFunction(fn)) {
  // ......
  fn = convert(fn)
}

接下來(lái)我們來(lái)看看對(duì)中間件的調(diào)用。

/**
 * Return a request handler callback
 * for node's native http server.
 *
 * @return {Function}
 * @api public
 */
callback () {
  const fn = compose(this.middleware)
  if (!this.listenerCount('error')) this.on('error', this.onerror)
  const handleRequest = (req, res) => {
    const ctx = this.createContext(req, res)
    return this.handleRequest(ctx, fn)
  }
  return handleRequest
}

可以看出關(guān)于中間件的核心邏輯應(yīng)該位于 compose 方法中,該方法是一個(gè)名為 Koa-compose 的第三方模塊https://github.com/Koajs/compose,我們可以看看其內(nèi)部是如何實(shí)現(xiàn)的。

該模塊只有一個(gè)方法 compose,調(diào)用方式為 compose([a, b, c, ...]),該方法接受一個(gè)中間件的數(shù)組作為參數(shù),返回的仍然是一個(gè)中間件(函數(shù)),可以將這個(gè)函數(shù)看作是之前加載的全部中間件的功能集合。

/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */
function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }
  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */
  return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

該方法的核心是一個(gè)遞歸調(diào)用的 dispatch 函數(shù),為了更好地說(shuō)明這個(gè)函數(shù)的工作原理,這里使用一個(gè)簡(jiǎn)單的自定義中間件作為例子來(lái)配合說(shuō)明。

function myMiddleware(context, next) {
  process.nextTick(function () {
    console.log('I am a middleware');
  })
  next();
}

可以看出這個(gè)中間件除了打印一條消息,然后調(diào)用 next 方法之外,沒(méi)有進(jìn)行任何操作,我們以該中間件為例,在 Koa 的 app.js 中使用 app.use 方法加載該中間件兩次。

const Koa = require('Koa');
const myMiddleware = require("./myMiddleware");
app.use(md1);
app.use(dm2);
app.listen(3000);

app 真正實(shí)例化是在調(diào)用 listen 方法之后,那么中間件的加載同樣位于 listen 方法之后。

那么 compose 方法的實(shí)際調(diào)用為 compose[myMiddleware,myMiddleware],在執(zhí)行 dispatch(0) 時(shí),該方法實(shí)際可以簡(jiǎn)化為:

function compose(middleware) {
  return function (context, next) {
    try {
      return Promise.resolve(md1(context, function next() {
        return Promise.resolve(md2(context, function next() {
        }))
      }))
    } catch (err) {
      return Promise.reject(err)
    }
  }
}

可以看出 compose 的本質(zhì)仍是嵌套的中間件。

3. listen() 方法

這是 app 啟動(dòng)過(guò)程中的最后一步,讀者會(huì)疑惑:為什么這么一行也要算作單獨(dú)的步驟,事實(shí)上,上面的兩步都是為了 app 的啟動(dòng)做準(zhǔn)備,整個(gè) Koa 應(yīng)用的啟動(dòng)是通過(guò) listen 方法來(lái)完成的。下面是 application.js 中 listen 方法的定義。

/**
 * Shorthand for:
 *
 *    http.createServer(app.callback()).listen(...)
 *
 * @param {Mixed} ...
 * @return {Server}
 * @api public
 */
listen(...args) {
  debug('listen')
  const server = http.createServer(this.callback())
  return server.listen(...args)
}

上面的代碼就是 listen 方法的內(nèi)容,可以看出第 3 行才真正調(diào)用了 http.createServer 方法建立了 http 服務(wù)器,參數(shù)為上節(jié) callback 方法返回的 handleRequest 方法,源碼如下所示,該方法做了兩件事:

  • 封裝 requestresponse 對(duì)象。
  • 調(diào)用中間件對(duì) ctx 對(duì)象進(jìn)行處理。
/**
 * Handle request in callback.
 *
 * @api private
 */
handleRequest (ctx, fnMiddleware) {
  const res = ctx.res
  res.statusCode = 404
  const onerror = err => ctx.onerror(err)
  const handleResponse = () => respond(ctx)
  onFinished(res, onerror)
  return fnMiddleware(ctx).then(handleResponse).catch(onerror)
}

4. next()與return next()

我們前面也提到過(guò),Koa 對(duì)中間件調(diào)用的實(shí)現(xiàn)本質(zhì)上是嵌套的 promise.resolve 方法,我們可以寫一個(gè)簡(jiǎn)單的例子。

let ctx = 1;
const md1 = function (ctx, next) {
  next();
}
const md2 = function (ctx, next) {
  return ++ctx;
}
const p = Promise.resolve(
  mdl(ctx, function next() {
    return Promise.resolve(
      md2(ctx, function next() {
        //更多的中間件...
      })
    )
  })
)
p.then(function (ctx) {
  console.log(ctx);
})

代碼在第一行定義的變量 ctx,我們可以將其看作 Koa 中的 ctx 對(duì)象,經(jīng)過(guò)中間件的處理后,ctx 的值會(huì)發(fā)生相應(yīng)的變化。

我們定義了 md1md2 兩個(gè)中間件,md1 沒(méi)有做任何操作,只調(diào)用了 next 方法,md2 則是對(duì) ctx 執(zhí)行加一的操作,那么在最后的 then 方法中,我們期望 ctx 的值為 2。

我們可以嘗試運(yùn)行上面的代碼,最后的結(jié)果卻是 undefined,在 md1next 方法前加上 return 關(guān)鍵字后,就能得到正常的結(jié)果了。

在 Koa 的源碼 application.js 中,callback 方法的最后一行:

/**
 * Return a request handler callback
 * for node's native http server.
 *
 * @return {Function}
 * @api public
 */
callback () {
  const fn = compose(this.middleware)
  if (!this.listenerCount('error')) this.on('error', this.onerror)
  const handleRequest = (req, res) => {
    const ctx = this.createContext(req, res)
    return this.handleRequest(ctx, fn)
  }
  return handleRequest
}
/**
 * Handle request in callback.
 *
 * @api private
 */
handleRequest (ctx, fnMiddleware) {
  const res = ctx.res
  res.statusCode = 404
  const onerror = err => ctx.onerror(err)
  const handleResponse = () => respond(ctx)
  onFinished(res, onerror)
  return fnMiddleware(ctx).then(handleResponse).catch(onerror)
}

中的 fnMiddleware(ctx) 相當(dāng)于之前代碼第 8 行聲明的 Promise 對(duì)象 p,被中間件方法修改后的 ctx 對(duì)象被 then 方法傳給 handleResponse 方法返回給客戶端。

每個(gè)中間件方法都會(huì)返回一個(gè) Promise 對(duì)象,里面包含的是對(duì) ctx 的修改,通過(guò)調(diào)用 next 方法來(lái)調(diào)用下一個(gè)中間件。

fn(context, function next () {
  return dispatch(i + 1);
})

再通過(guò) return 關(guān)鍵字將修改后的 ctx 對(duì)象作為 resolve 的參數(shù)返回。

如果多個(gè)中間件同時(shí)操作了 ctx 對(duì)象,那么就有必要使用 return 關(guān)鍵字將操作的結(jié)果返回到上一級(jí)調(diào)用的中間件里。

事實(shí)上,如果讀者去讀 Koa-router 或者 Koa-static 的源碼,也會(huì)發(fā)現(xiàn)它們都是使用 return next 方法。

5. 關(guān)于 Can’t set headers after they are sent.

這是使用 Express 或者 Koa 常見的錯(cuò)誤之一,其原因如字面意思,對(duì)于同一個(gè) HTTP 請(qǐng)求重復(fù)發(fā)送了 HTTP HEADER 。服務(wù)器在處理HTTP 請(qǐng)求時(shí)會(huì)先發(fā)送一個(gè)響應(yīng)頭(使用 writeHeadsetHeader 方法),然后發(fā)送主體內(nèi)容(通過(guò) send 或者 end 方法),如果對(duì)一個(gè) HTTP 請(qǐng)求調(diào)用了兩次 writeHead 方法,就會(huì)出現(xiàn) Can't set headers after they are sent 的錯(cuò)誤提示,例如下面的例子:

const http = require("http");
http.createServer(function (req, res) {
  res.setHeader('Content-Type', 'text/html');
  res.end('ok');
  resend(req, res); // 在響應(yīng)結(jié)束后再次發(fā)送響應(yīng)信息
}).listen(5000);
function resend(req, res) {
  res.setHeader('Content-Type', 'text/html');
  res.end('error');
}

試著訪問(wèn) localhost:5000 就會(huì)得到錯(cuò)誤信息,這個(gè)例子太過(guò)直白了。下面是一個(gè) Express 中的例子,由于中間件可能包含異步操作,因此有時(shí)錯(cuò)誤的原因比較隱蔽。

const express = require('express');
const app = express();
app.use(function (req, res, next) {
  setTimeout(function () {
    res.redirect("/bar");
  }, 1000);
  next();
});
app.get("/foo", function (req, res) {
  res.end("foo");
});
app.get("/bar", function (req, res) {
  res.end("bar");
});
app.listen(3000);

運(yùn)行上面的代碼,訪問(wèn) http://localhost:3000/foo 會(huì)產(chǎn)生同樣的錯(cuò)誤,原因也很簡(jiǎn)單,在請(qǐng)求返回之后,setTimeout 內(nèi)部的 redirect 會(huì)對(duì)一個(gè)已經(jīng)發(fā)送出去的 response 進(jìn)行修改,就會(huì)出現(xiàn)錯(cuò)誤,在實(shí)際項(xiàng)目中不會(huì)像 setTimeout 這么明顯,可能是一個(gè)數(shù)據(jù)庫(kù)操作或者其他的異步操作,需要特別注意。

6. Context 對(duì)象的實(shí)現(xiàn)

關(guān)于 ctx 對(duì)象是如何得到 request/response 對(duì)象中的屬性和方法的,可以閱讀 context.js 的源碼,其核心代碼如下所示。此外,delegate 模塊還廣泛運(yùn)用在了 Koa 的各種中間件中。

const delegate = require('delegates')
delegate(proto, 'response')
  .method('attachment')
  .method('redirect')
  .method('remove')
  .method('vary')
  .method('has')
  .method('set')
  .method('append')
  .method('flushHeaders')
  .access('status')
  .access('message')
  .access('body')
  .access('length')
  .access('type')
  .access('lastModified')
  .access('etag')
  .getter('headerSent')
  .getter('writable')

delegate 是一個(gè) Node 第三方模塊,作用是把一個(gè)對(duì)象中的屬性和方法委托到另一個(gè)對(duì)象上。

讀者可以訪問(wèn)該模塊的項(xiàng)目地址 https://github.com/tj/node-delegates,然后就會(huì)發(fā)現(xiàn)該模塊的主要貢獻(xiàn)者還是TJ Holowaychuk。

這個(gè)模塊的代碼同樣非常簡(jiǎn)單,源代碼只有 100 多行,我們這里詳細(xì)介紹一下。

在上面的代碼中,我們使用了如下三個(gè)方法:

  • method:用于委托方法到目標(biāo)對(duì)象上。
  • access:綜合 gettersetter,可以對(duì)目標(biāo)進(jìn)行讀寫。
  • getter:為目標(biāo)屬性生成一個(gè)訪問(wèn)器,可以理解成復(fù)制了一個(gè)只讀屬性到目標(biāo)對(duì)象上。

gettersetter 這兩個(gè)方法是用來(lái)控制對(duì)象的讀寫屬性的,下面是 method 方法與 access 方法的實(shí)現(xiàn)。

/**
 * Delegate method `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
Delegator.prototype.method = function(name){
  var proto = this.proto;
  var target = this.target;
  this.methods.push(name);
  proto[name] = function(){
    return this[target][name].apply(this[target], arguments);
  };
  return this;
};

method 方法中使用 apply 方法將原目標(biāo)的方法綁定到目標(biāo)對(duì)象上。

下面是 access 方法的定義,綜合了 getter 方法和 setter 方法。

/**
 * Delegator accessor `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
Delegator.prototype.access = function(name){
  return this.getter(name).setter(name);
};
/**
 * Delegator getter `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);
  proto.__defineGetter__(name, function(){
    return this[target][name];
  });
  return this;
};
/**
 * Delegator setter `name`.
 *
 * @param {String} name
 * @return {Delegator} self
 * @api public
 */
Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);
  proto.__defineSetter__(name, function(val){
    return this[target][name] = val;
  });
  return this;
};

最后是 delegate 的構(gòu)造函數(shù),該函數(shù)接收兩個(gè)參數(shù),分別是源對(duì)象和目標(biāo)對(duì)象。

/**
 * Initialize a delegator.
 *
 * @param {Object} proto
 * @param {String} target
 * @api public
 */
function Delegator(proto, target) {
  if (!(this instanceof Delegator)) return new Delegator(proto, target);
  this.proto = proto;
  this.target = target;
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
} 

可以看出 deletgate 對(duì)象在內(nèi)部維持了一些數(shù)組,分別表示委托得到的目標(biāo)對(duì)象和方法。

關(guān)于動(dòng)態(tài)加載中間件

在某些應(yīng)用場(chǎng)景中,開發(fā)者可能希望能夠動(dòng)態(tài)加載中間件,例如當(dāng)路由接收到某個(gè)請(qǐng)求后再去加載對(duì)應(yīng)的中間件,但在 Koa 中這是無(wú)法做到的。原因其實(shí)已經(jīng)包含在前面的內(nèi)容了,Koa 應(yīng)用唯一一次加載所有中間件是在調(diào)用 listen 方法的時(shí)候,即使后面再調(diào)用 app.use 方法,也不會(huì)生效了。

7. Koa 的優(yōu)缺點(diǎn)

通過(guò)上面的內(nèi)容,相信讀者已經(jīng)對(duì) Koa 有了大概的認(rèn)識(shí),和 Express 相比,Koa 的優(yōu)勢(shì)在于精簡(jiǎn),它剝離了所有的中間件,并且對(duì)中間件的執(zhí)行做了很大的優(yōu)化。

一個(gè)經(jīng)驗(yàn)豐富的 Express 開發(fā)者想要轉(zhuǎn)到 Koa 上并不需要很大的成本,唯一需要注意的就是中間件執(zhí)行的策略會(huì)有差異,這可能會(huì)帶來(lái)一段時(shí)間的不適應(yīng)。

現(xiàn)在我們來(lái)說(shuō)說(shuō) Koa 的缺點(diǎn),剝離中間件雖然是個(gè)優(yōu)點(diǎn),但也讓不同中間件的組合變得麻煩起來(lái),Express 經(jīng)過(guò)數(shù)年的沉淀,各種用途的中間件已經(jīng)很成熟;而 Koa 不同,Koa2.0 推出的時(shí)間還很短,適配的中間件也不完善,有時(shí)單獨(dú)使用各種中間件還好,但一旦組合起來(lái),可能出現(xiàn)不能正常工作的情況。

舉個(gè)例子,如果想同時(shí)使用 routerviews 兩個(gè)中間件,就要在 render 方法前加上 return 關(guān)鍵字(和 return next()一個(gè)道理),對(duì)于剛接觸 Koa 的開發(fā)者可能要花很長(zhǎng)時(shí)間才能定位問(wèn)題所在。再例如前面的 koa-sessionKoa-router,我初次接觸這兩個(gè)中間件時(shí)也著實(shí)花了一些功夫來(lái)將他們正確地組合在一塊。雖然中間件概念的引入讓Node開發(fā)變得像搭積木一樣,但積木之間如果不能很順利地拼接在一塊的話,也會(huì)增加開發(fā)成本。

到此這篇關(guān)于Node.js深入分析Koa源碼的文章就介紹到這了,更多相關(guān)Node.js Koa內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • 教你如何用Node實(shí)現(xiàn)API的轉(zhuǎn)發(fā)(某音樂(lè))

    教你如何用Node實(shí)現(xiàn)API的轉(zhuǎn)發(fā)(某音樂(lè))

    這篇文章主要介紹了教你如何用Node實(shí)現(xiàn)API的轉(zhuǎn)發(fā)(某音樂(lè)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 關(guān)于node編寫文件上傳的接口的坑及解決

    關(guān)于node編寫文件上傳的接口的坑及解決

    這篇文章主要介紹了關(guān)于node編寫文件上傳的接口的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 前端Electron新手入門教程詳解

    前端Electron新手入門教程詳解

    這篇文章主要介紹了Electron新手入門教程詳解,首先圍繞Electron框架的關(guān)鍵知識(shí)點(diǎn)進(jìn)行詳細(xì)講解,然后對(duì)DEMO程序進(jìn)行分析,讓前端開發(fā)人員對(duì)使用Electron開發(fā)桌面應(yīng)用程序有一個(gè)初步的了解。,需要的朋友可以參考下
    2019-06-06
  • Node.js中下包速度慢問(wèn)題解決方法分析

    Node.js中下包速度慢問(wèn)題解決方法分析

    這篇文章主要介紹了Node.js中下包速度慢問(wèn)題解決方法,分析了npm切換鏡像源以及nrm工具鏡像源管理相關(guān)使用技巧,需要的朋友可以參考下
    2023-04-04
  • Windows安裝Node.js報(bào)錯(cuò):2503、2502的解決方法

    Windows安裝Node.js報(bào)錯(cuò):2503、2502的解決方法

    這篇文章主要給大家介紹了關(guān)于在Windows系統(tǒng)下安裝Node.js報(bào)錯(cuò):2503、2502的解決方法,文中將解決的方法一步步介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • 從零開始學(xué)習(xí)Node.js系列教程二:文本提交與顯示方法

    從零開始學(xué)習(xí)Node.js系列教程二:文本提交與顯示方法

    這篇文章主要介紹了Node.js文本提交與顯示方法,結(jié)合實(shí)例形式分析了nodejs基于http的文本提交、傳輸與顯示相關(guān)操作技巧,需要的朋友可以參考下
    2017-04-04
  • Express實(shí)現(xiàn)定時(shí)發(fā)送郵件的示例代碼

    Express實(shí)現(xiàn)定時(shí)發(fā)送郵件的示例代碼

    在開發(fā)中我們有時(shí)候需要每隔?一段時(shí)間發(fā)送一次電子郵件,或者在某個(gè)特定的時(shí)間進(jìn)行發(fā)送郵件,無(wú)需手動(dòng)去操作,基于這樣的情況下我們需要用到了定時(shí)任務(wù)。本文就來(lái)用Express實(shí)現(xiàn)定時(shí)發(fā)送郵件吧
    2023-04-04
  • nodejs+express實(shí)現(xiàn)文件上傳下載管理網(wǎng)站

    nodejs+express實(shí)現(xiàn)文件上傳下載管理網(wǎng)站

    這篇文章主要為大家詳細(xì)介紹了nodejs+express實(shí)現(xiàn)文件上傳下載管理的網(wǎng)站,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • node.js中express模塊創(chuàng)建服務(wù)器和http模塊客戶端發(fā)請(qǐng)求

    node.js中express模塊創(chuàng)建服務(wù)器和http模塊客戶端發(fā)請(qǐng)求

    今天小編就為大家分享一篇關(guān)于node.js中express模塊創(chuàng)建服務(wù)器和http模塊客戶端發(fā)請(qǐng)求,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • Node.js 8 中的重要新特性

    Node.js 8 中的重要新特性

    這篇文章主要介紹了Node.js 8 中的重要新特性的相關(guān)資料,需要的朋友可以參考下
    2017-06-06

最新評(píng)論