美文网首页我爱编程
【免费公开课】手把手教你实现Node.js Express框架

【免费公开课】手把手教你实现Node.js Express框架

作者: 饥人谷_若愚 | 来源:发表于2017-07-05 10:59 被阅读205次

    手把手教你实现Node.js Express框架

    刚接触 js 的同学在学到 ajax 的时候经常会懵掉,归根结底就是对所谓的“后台”、“服务器”这些概念没有任何概念。课程中我讲过 Express 做后台,甚至写了个简单易用的 mock 工具 server-mock 来方便同学模拟数据,但经常会出现类似下面的对话:

    同学:“你推荐的框架和工具我用了,用的也很爽,可是框架工具的外衣下到底发生了什么?除了 mock 数据,我还想做 HTTP 的缓存控制的测试、想做白屏和 FOUC的效果重现测试、想做静态资源加载顺序的测试、想做跨域的测试... ,如果我不明白里面后台到底发生了什么还不如叫我去死...”

    我:"多用多练,学到后面你自然就懂了,不甘心你可以先看看 Express 的源码"

    同学:“我用都还没用熟练... 杀了我吧...”

    如果想追根溯源,看源码真的是唯一途径,无奈源码实在是太枯燥,为了功能的完善流行的框架引入太多和主线流程不先关的东西。即使偶尔能找到一些不错的源码解析的文章,也是又臭又硬,完全不适合缺少经验的初学者。所以之前答应同学近期安排一次好懂有用的直播公开课,专门讲解服务器和后端框架,尽量让不管是前端小白还是前端老鸟都有收获。

    直播内容

    本次直播课涉及的内容如下:

    • step0. 我们先使用 Nodejs 的入门知识搭建一个服务器
    • step1. 对搭建的服务器功能进行扩展,使之成为一个能用的静态服务器
    • step2. 继续扩展,让我们的静态服务器能解析路由,把服务器变成一个支持静态目录和动态路由的“网站”
    • step3. 模拟 Node.js 的后端框架 Express的使用方法,实现一个包含静态目录设置、中间件处理、路由匹配的迷你 Express 框架
    • step4. 完善这个框架

    适合的对象

    不需要你有万行代码的编写经验、不需要你精通/掌握/熟悉 Node.js,你只需要

    • 有一些 js 使用经验,有一点 nodejs的使用经验,即可理解1、2、3对应的内容。
    • 如果你有一点后端基础,有一点Express 框架的使用经验,那么你就能理解4、5对应的内容。

    我能学到什么

    • 你会对服务器、对后端框架有一个清晰的认识
    • 平时搭个静态服务器或者 Mock 数据不再需要使用别人的东西
    • 会对 HTTP 有更深入的认识
    • 中间件、异步有一定认识
    • 装 13利器,以后简历里可以写自己不使用任何第三方模块,实现一个类 Express 的后端框架

    课程安排

    直播时间: 本周三(7月5日)晚上8点30

    **参与方式:加 QQ 群:617043164 ,入群申请:简书Node框架。

    也可以在微信搜索小程序『饥人谷』,上课的时候如果不方便看电脑可在手机微信小程序上观看直播

    关于我

    我是若愚,曾经在百度、阿里巴巴做前端开发,后创业加入饥人谷做前端老师。亲手培养了近300名同学

    一些同学去了Facebook、大众点评、美团、头条、金山、百度、阿里(外包)、华为(外包)
    一些同学在小公司当技术 leader
    一些同学去国外做菠菜网站拿着让人"震惊"的待遇
    一些同学自己做外包当老板收入甚至远超老师
    当然还有一些同学,令人悲伤的中途退课转了行

    他们来自五湖四海各行各业,海龟、名校高材生、火车司机、航海员、旅游软件销售、全职妈妈、装配厂工人、方便面制造师、中科院研究所员工、美工、产品、后端、机械/化工/传媒/英语/新闻从业者....,但最终殊(误)途(入)同(歧)归(途)

    直播剧透

    以下是公开课课堂上涉及的代码,有兴趣参加公开课的同学可以提前阅读、理解、复制、允许代码,Github 的链接直播课堂上放出。课堂上会进行一步步讲解,任何疑问都可在直播课和老师直接互动。

    step0: 创建一个服务器

    index.js

    var http = require('http')
    
    var server = http.createServer(function(request, response){
      //response.setHeader('Content-Type', 'text/html')
      //response.setHeader('X-Powered-By',  'Jirengu')
      response.end('hello world')
    })
    
    console.log('open http://localhost:8080')
    server.listen(8080)
    

    step1:搭建静态服务器

    var http = require('http')
    var path = require('path')
    var fs = require('fs')
    var url = require('url')
    
    function staticRoot(staticPath, req, res){
    
      var pathObj = url.parse(req.url, true)
      var filePath = path.resolve(staticPath, pathObj.pathname.substr(1))
      var fileContent = fs.readFileSync(filePath,'binary')
    
      res.writeHead(200, 'Ok')
      res.write(fileContent, 'binary')
      res.end()
    }
    
    var server = http.createServer(function(req, res){
      staticRoot(path.resolve(__dirname, 'static'), req, res)
    })
    
    server.listen(8080)
    console.log('visit http://localhost:8080' )
    
    
    

    step2: 解析路由

    
    var http = require('http')
    var path = require('path')
    var fs = require('fs')
    var url = require('url')
    
    var routes = {
      '/a': function(req, res){
        res.end('match /a, query is:' + JSON.stringify(req.query))
      },
    
      '/b': function(req, res){
        res.end('match /b')
      },
    
      '/a/c.js': function(req, res){
        res.end('match /a/c.js')
      },
    
      '/search': function(req, res){
        res.end('username='+req.body.username+',password='+req.body.password)
      }
    
    }
    
    
    var server = http.createServer(function(req, res){
      routePath(req, res)
    })
    
    server.listen(8080)
    console.log('visit http://localhost:8080' )
    
    
    function routePath(req, res){
      var pathObj = url.parse(req.url, true)
      var handleFn = routes[pathObj.pathname]
        
      if(handleFn){
        req.query = pathObj.query
    
        var body = ''
        req.on('data', function(chunk){
          body += chunk
        }).on('end', function(){
          req.body = parseBody(body)
          handleFn(req, res)
        })
        
      }else {
        staticRoot(path.resolve(__dirname, 'static'), req, res)
      }
    }
    
    function staticRoot(staticPath, req, res){
      var pathObj = url.parse(req.url, true)
      var filePath = path.resolve(staticPath, pathObj.pathname.substr(1))
    
      fs.readFile(filePath,'binary', function(err, content){
        if(err){
          res.writeHead('404', 'haha Not Found')
          return res.end()
        }
    
        res.writeHead(200, 'Ok')
        res.write(content, 'binary')
        res.end()  
      })
    
    }
    
    function parseBody(body){
      var obj = {}
      body.split('&').forEach(function(str){
        obj[str.split('=')[0]] = str.split('=')[1]
      })
      return obj
    }
    
    
    

    step3:Express 雏形

    文件目录结构

    bin
      - www
    lib
      - express.js
    app.js
    
    

    可通过 node bin/www 命令启动服务器

    bin/www

    var app = require('../app')
    var http = require('http')
    
    http.createServer(app).listen(8080)
    console.log('open http://localhost:8080')
    
    

    app.js

    
    var express = require('./lib/express')
    var path = require('path')
    
    
    
    var app = express()
    
    app.use(function(req, res, next) {
      console.log('middleware 1')
      next()
    })
    
    app.use(function(req, res, next) {
      console.log('middleware 12')
      next()
    })
    
    
    app.use('/hello', function(req, res){
      console.log('/hello..')
      res.send('hello world')
    })
    
    app.use('/getWeather', function(req, res){
      res.send({url:'/getWeather', city: req.query.city})
    })
    
    app.use(function(req, res){
      res.send(404, 'haha Not Found')
    })
    
    module.exports = app
    
    

    lib/express.js

    var url = require('url')
    
    
    function express() {
    
      var tasks = []
    
      var app = function(req, res){
        makeQuery(req)
        makeResponse(res)
            //post 的解析未实现
    
        var i = 0
    
        function next() {
          var task = tasks[i++]
          if(!task) {
            return
          }
    
          //如果是普通的中间件 或者 是路由中间件  
          if(task.routePath === null || url.parse(req.url, true).pathname === task.routePath){
            task.middleWare(req, res, next)
          }else{
            //如果说路由未匹配上的中间件,直接下一个
            next()
          }
        }
    
        next()
      }
    
      app.use = function(routePath, middleWare){
        if(typeof routePath === 'function') {
          middleWare = routePath
          routePath = null
        }
            
        tasks.push({
          routePath: routePath,
          middleWare: middleWare
        })
      }
    
    
      return app
    
    }
    
    express.static = function(path){
    
      return function(req, res){
                //未实现
      }
    }
    
    module.exports = express
    
    function makeQuery(req){
      var pathObj = url.parse(req.url, true)
      req.query = pathObj.query
    }
    
    function makeResponse(res){
      res.send = function(toSend){
        if(typeof toSend === 'string'){
          res.end(toSend)
        }
        if(typeof toSend === 'object'){
          res.end(JSON.stringify(toSend))
        }
        if(typeof toSend === 'number'){
          res.writeHead(toSend, arguments[1])
          res.end()
        }
      }
    }
    
    
    

    step4: 框架完善

    文件目录结构

    bin
      - www
    lib
      - express.js
      - body-parser.js
    app.js
    
    

    可通过 node bin/www 命令启动服务器

    bin/www

    var app = require('../app')
    var http = require('http')
    
    http.createServer(app).listen(8080)
    console.log('open http://localhost:8080')
    
    

    app.js

    
    var express = require('./lib/express')
    var path = require('path')
    var bodyParser = require('./lib/body-parser')
    
    
    var app = express()
    
    
    //新增 bodyParser 中间件
    app.use(bodyParser)
    
    //新增 express.static 方法设置静态目录
    app.use(express.static(path.join(__dirname, 'static')))
    
    
    app.use(function(req, res, next) {
      console.log('middleware 1')
      next()
    })
    
    app.use(function(req, res, next) {
      console.log('middleware 12')
      next()
    })
    
    
    app.use('/hello', function(req, res){
      console.log('/hello..')
      res.send('hello world')
    })
    
    app.use('/getWeather', function(req, res){
      res.send({url:'/getWeather', city: req.query.city})
    })
    
    app.use('/search', function(req, res){
      res.send(req.body)
    })
    
    app.use(function(req, res){
      res.send(404, 'haha Not Found')
    })
    
    
    module.exports = app
    
    
    

    lib/express.js

    var url = require('url')
    var fs = require('fs')
    var path = require('path')
    
    
    function express() {
    
      var tasks = []
    
      var app = function(req, res){
    
        makeQuery(req)
        makeResponse(res)
        console.log(tasks)
    
        var i = 0
    
        function next() {
          var task = tasks[i++]
          if(!task) {
            return
          }
          if(task.routePath === null || url.parse(req.url, true).pathname === task.routePath){
            task.middleWare(req, res, next)
          }else{
            next()
          }
        }
    
        next()
      }
    
      app.use = function(routePath, middleWare){
        if(typeof routePath === 'function') {
          middleWare = routePath
          routePath = null
        }
    
        tasks.push({
          routePath: routePath,
          middleWare: middleWare
        })
      }
    
    
      return app
    
    }
    
    express.static = function(staticPath){
    
      return function(req, res, next){
        var pathObj = url.parse(req.url, true)
        var filePath = path.resolve(staticPath, pathObj.pathname.substr(1))
        console.log(filePath)
        fs.readFile(filePath,'binary', function(err, content){
          if(err){
            next()
          }else {
            res.writeHead(200, 'Ok')
            res.write(content, 'binary')
            res.end()         
          }
        })
      }
    }
    
    module.exports = express
    
    
    function makeQuery(req){
      var pathObj = url.parse(req.url, true)
      req.query = pathObj.query
    }
    
    function makeResponse(res){
      res.send = function(toSend){
        if(typeof toSend === 'string'){
          res.end(toSend)
        }
        if(typeof toSend === 'object'){
          res.end(JSON.stringify(toSend))
        }
        if(typeof toSend === 'number'){
          res.writeHead(toSend, arguments[1])
          res.end()
        }
      }
    }
    
    

    lib/body-parser.js

    
    function bodyParser(req, res, next){
        var body = ''
        req.on('data', function(chunk){
          body += chunk
        }).on('end', function(){
          req.body = parseBody(body)
          next()
        })
    }
    
    function parseBody(body){
      var obj = {}
      body.split('&').forEach(function(str){
        obj[str.split('=')[0]] = str.split('=')[1]
      })
      return obj
    }
    
    module.exports = bodyParser
    

    相关文章

      网友评论

        本文标题:【免费公开课】手把手教你实现Node.js Express框架

        本文链接:https://www.haomeiwen.com/subject/yiofhxtx.html