由淺入深理解express源碼(一)


 
 
 

准備

項目以 mocha + chai + supertest 測試驅動開發,閱讀者需要儲備的知識有:

1、mocha作為測試框架在項目中的運用 mochajs.org

2、chai斷言庫的api使用 www.chaijs.com

3、使用supertest驅動服務器的啟動,並模擬訪問服務器地址。npm.taobao.org/package/sup…

4、node http服務端和客戶端的基本知識 nodejs.cn/api/http.ht…

實現目標

git: github.com/kaisela/mye…

gitee: gitee.com/kaisela/mye…

本系列項目和文章的目標是一步一步實現一個簡化版的express,這就需要將express的源碼進行一步一步的剝離。迭代一的目標是實現服務器的啟動、app 對象get方法的簡化版本以及項目基本結構的確定。對於get方法只實現到通過path找到對應的callback。path不做分解和匹配

項目結構

express1
  |
  |-- lib
  |    | 
  |    |-- express.js //負責實例化application對象
  |    |-- application.js //包裹app層
  |
  |-- examples
  |    |-- index.js // express1 實現的使用例子
  |
  |-- test
  |    |
  |    |-- index.js // 自動測試examples的正確性
  |
  |-- index.js //框架入口
  |-- package.json // node配置文件

復制代碼

執行流程

 

 

test/index.js 主要是集成mocha + chai + supertest 自動發送examples/index.js中注冊的get請求。

examples/index.js 主要是對lib文件下的兩個源碼功能實現的驗證。

lib/express.js 對application中的對象進行初始化,完成createServer方法的callback。

lib/application.js 一期迭代的主要功能和實現。主要實現了listen接口和get兩個對外接口和handle供express.js 使用

代碼解析

首先看看lib/application.js,代碼中有_init, _defaultConfiguration, _set, handle, listen, get幾個方法:

_init: 初始化app對象需要的一些基礎設置

_defaultConfiguration: 設置環境變量env,后期迭代預留

_set: 對app中setting對象的操作,為后期迭代預留

handle: http.createServer 中的回調函數最終執行,遍歷paths,確定調用哪個get函數中的回調函數

listen: 啟動http服務。靈活使用arguments將http服務中listen方法的參數留給用戶自行配置。同時createServer方法傳入this,將在express.js中定義定app方法作為服務請求的回調函數。

get: 實現app的get接口,主要是對所有的get請求進行注冊,存入app對象的paths數組中,方便handle中實現精准回調。

源碼:

'use strict'
/** * 采用的是設計模式中的模塊模式,定義app對象,為其掛載方法 */
const http = require('http')
let app = exports = module.exports = {}
/** * 初始化app對象需要的一些基礎設置 * paths: 存放所有使用get方法注冊的請求,單體對象的格式為: * { * pathURL 請求的地址 cb 請求對應的回調函數 * } */
app._init = function init() {
  this.setting = {}
  this.paths = []
  this.defaultConfiguration()
}
/** * 設置環境變量env,后期迭代預留 */
app._defaultConfiguration = function defaultConfiguration() {
  let env = process.env.NODE_ENV || 'development'
  this.set('env', env)
  this.set('jsonp callback name', 'callback')
}
/** * 對app中setting對象的操作,為后期迭代預留 */
app._set = function set(key, val) {
  if (arguments.length === 1) {
    this.setting[key]
  }
  this.setting[key] = val
}
/** * http.createServer 中的回調函數最終執行,遍歷paths,確定調用哪個get函數中的回調函數 */
app.handle = function handle(req, res) {
  let pathURL = req.url
  for (let path of this.paths) {
    if (pathURL === path.pathURL) {
      path.cb(req, res)
    }
  }
}
/** * 啟動http服務 */
app.listen = function listen() {
  let server = http.createServer(this)
  return server
    .listen
    .apply(server, arguments)
}
/** * 實現app的get接口,主要是對所有的get請求進行注冊,方便handle中實現精准回調 */
app.get = function get(path, cb) {
  let pathObj = {
    pathURL: path,
    cb: cb
  }
  this
    .paths
    .push(pathObj)
}
復制代碼

exammple/index.js 啟動服務,如果根據訪問地址的不同,給出不同的輸出

const express = require('../index.js')
const app = express()
app.listen(3000) // 啟動端口為3000的服務
// localhost:3000/path 時調用
app.get('/path', function (req, res) {
  console.log('visite /path , send : path')
  res.end('path')
})
// localhost:3000/ 時調用
app.get('/', function (req, res) {
  console.log('visite /, send: root')
  res.end('root')
})

exports = module.exports = app
復制代碼

test/index.js 測試exapmles中的代碼,驗證是否按照地址的不同,進了不同的回調函數

'use strict'

const assert = require('chai').assert

const app = require('../examples/index.js')
const request = require('supertest')(app)
describe('服務器測試', () => {
  // 如果走的不是examples中的get:/ 測試不通過
  it('GET /', (done) => {
    request
      .get('/')
      .expect(200)
      .end((err, res) => {
        if (err) 
          return done(err)
        assert.equal(res.text, 'root', 'res is wrong') // 根據response調用end方法時的輸出為: root
        done()
      })
  })
  // 如果走的不是examples中的get:/path 測試不通過
  it('GET /path', (done) => {
    request
      .get('/path')
      .expect(200)
      .end((err, res) => {
        if (err) 
          return done(err)
        assert.equal(res.text, 'path', 'res is wrong') // 根據response調用end方法時的輸出為: path
        done()
      })
  })
})


復制代碼

test測試結果如下:

 

 

下期預告

先做個簡單的嘗試,下一期我們實現app的get,post等方法,主要是http中的methods,以及簡單的路由處理。

 

 

 

由淺入深理解express源碼(一)

由淺入深理解express源碼(二)

由淺入深理解express源碼(三)

由淺入深理解express源碼(四)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM