美文网首页
koa2学习笔记

koa2学习笔记

作者: 地平线0530 | 来源:发表于2019-02-10 23:49 被阅读0次

    快速开始

    安装

    node.js 版本:v7.6 以上

    npm i koa -S

    运行

    const Koa = require('koa')
    const app = new Koa()
    
    app.use(async ctx => {
      ctx.body = 'Hello Koa'
    })
    
    app.listen(3000, () => {
      console.log(`服务运行于:http://localhost:3000`)
    })
    

    async / await 使用

    简单理解:

    async 是让方法变成异步,返回值是一个 Promise 对象

    await 是等待异步方法执行完成

    async(MDN)

    await(MDN)

    Promise(MDN)

    async function fnA() {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve('A')
        }, 3000)
      })
    }
    async function fnB() {
      return 'B'
    }
    
    
    async function test() {
      const v1 = await fnA()
      const v2 = await fnB()
      console.log(v1, v2)
    }
    test()  // 3秒后打印结果: A, B
    

    如果将程序改写为下面这样:

    async function test() {
      const v1 = fnA()
      const v2 = fnB()
      console.log(v1, v2)
    }
    test()  // 直接打印结果: Promise{<pending>},Promise{<pending>}
    

    使用 koa应用生成器生成项目

    npm i -g koa-generator

    koa demo

    通过 -V 查看版本,-h 查看帮助信息:

    Usage: koa [options] [dir]
    
    Options:
    
    -h, --help          output usage information
    -V, --version       output the version number
    -e, --ejs           add ejs engine support (defaults to jade)
    --hbs           add handlebars engine support
    -H, --hogan         add hogan.js engine support
    -c, --css <engine>  add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
    --git           add .gitignore
    -f, --force         force on non-empty directory
    

    生成器生成如下文件和目录:

    create : demo
    create : demo/package.json
    create : demo/app.js
    create : demo/public
    create : demo/routes
    create : demo/routes/index.js
    create : demo/routes/users.js
    create : demo/views
    create : demo/views/index.jade
    create : demo/views/layout.jade
    create : demo/views/error.jade
    create : demo/bin
    create : demo/bin/www
    create : demo/public/stylesheets
    create : demo/public/stylesheets/style.css
    
    install dependencies:
    > cd demo && npm install
    
    run the app:
    > SET DEBUG=demo:* & npm start
    
    create : demo/public/javascripts
    create : demo/public/images
    

    路由及中间件

    基本使用

    路由(Router)就是根据不同 URL 地址,加载不同的页面实现不同的功能。

    Koa中的路由需要安装路由模块来实现:

    npm i -S koa-router

    Github地址

    const app = new Koa()
    const router = new Router()
    
    router.get('/', (ctx, next) => {
      ctx.body = 'Hello Koa!'
    })
    
    router.get('/about', (ctx, next) => {
      ctx.body = "这时一个测试网站"
    })
    
    // 启动路由
    app.use(router.routes())
    app.use(router.allowedMethods())  // 官方推荐用法,当所有路由中间件调用完后,根据 ctx.status 设置 response 响应头
    
    app.listen(3000, () => {
      console.log(`服务运行于:http://localhost:3000`)
    })
    

    get传值

    GET 传值通过 request 接收,有两种方法:

    • query:返回格式化的参数对象
    • querystring:返回请求字符串
    router.get('/about', (ctx, next) => {
      let _url = ctx.url
      // 从 request 中获取
      let _request = ctx.request
      let req_query = _request.query
      let req_querystring = _request.querystring
      // 从上下文中直接获取
      let ctx_query = ctx.query
      let ctx_querystring = ctx.querystring
    
      const obj = {
        _url,
        _request,
        req_query,
        req_querystring,
        ctx_query,
        ctx_querystring
      }
    
      ctx.body = obj
    
    })
    

    浏览器输入:http://localhost:3000/about?name=ddd&age=18 可以查看效果:

    {
      "_url": "/about?name=ddd&age=18",
      "_request": {
        "method": "GET",
        "url": "/about?name=ddd&age=18",
        "header": {
          "host": "localhost:3000",
          "connection": "keep-alive",
          "cache-control": "max-age=0",
          "upgrade-insecure-requests": "1",
          "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
          "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
          "accept-encoding": "gzip, deflate, br",
          "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"
        }
      },
      "req_query": {
        "name": "ddd",
        "age": "18"
      },
      "req_querystring": "name=ddd&age=18",
      "ctx_query": {
        "name": "ddd",
        "age": "18"
      },
      "ctx_querystring": "name=ddd&age=18"
    }
    

    动态路由

    router.get('/page/:uid', async (ctx) => {
      let _params = ctx.params
      ctx.body = _params
    })
    

    当浏览器请求:http://localhost:3000/page/1 时,打印如下信息:

    {"uid":"1"}
    

    中间件

    什么是中间件

    中间件就是程序中可织入的,可重用的,与业务逻辑无关的各种组件。

    中间件的功能包括:

    • 执行任何代码
    • 修改请求和响应对象
    • 终结请求-响应循环
    • 调用堆栈中的下一个中间件

    Koa 应用可以使用如下几种中间件

    • 应用级中间件
    • 路由级中间件
    • 错误处理中间件
    • 第三方中间件
    1. 应用级中间件
    const Koa = require('koa')
    const Router = require('koa-router')
    
    const app = new Koa()
    const router = new Router()
    
    app.use(async (ctx, next) => {
      console.log(new Date())
      await next()  // 这里必须有 next() 才能继续执行下面的中间件
    })
    
    router.get('/', (ctx, next) => {
      ctx.body = 'Hello Koa!'
    })
    
    router.get('/about', (ctx, next) => {
      ctx.body = '关于我'
    })
    
    // 启动路由
    app.use(router.routes())
    app.use(router.allowedMethods())  // 官方推荐用法,当所有路由中间件调用完后,根据 ctx.status 设置 response 响应头
    
    app.listen(3000, () => {
      console.log(`服务运行于:http://localhost:3000`)
    })
    

    如上,程序先调用第一个中间件,打印当前时间,然后再执行后面的调用,如果这里没有写 next(),则不能调用路由,访问页面出错。

    2. 路由中间件
    router.get('/', async (ctx, next) => {
      console.log('来了老弟!')
      next()
    })
    
    router.get('/', (ctx, next) => {
      ctx.body = 'Hello Koa!'
    })
    
    3. 错误处理中间件
    app.use(async (ctx, next) => {
      next()
      if (ctx.status == 404) {
        ctx.status = 404
        ctx.body = '您访问的页面好像丢了'
      }
    })
    
    4. 第三方中间件
    // 支持访问静态文件
    const path = require('path')
    const static = require('koa-static')
    const staticPath = './static'
    
    app.use(static(path.join(__dirname, staticPath)))
    
    // 解析 POST 中间件
    const bodyParser = require('koa-bodyparser')
    app.use(bodyParser())
    

    洋葱模型

    洋葱模型
    app.use((ctx, next) => {
      console.log('===== Start 1 =====')
      next()
      console.log('===== End 1 =====')
    })
    
    app.use((ctx, next) => {
      console.log('===== Start 2 =====')
      next()
      console.log('===== End 2 =====')
    })
    
    app.use((ctx, next) => {
      console.log('===== Start 3 =====')
      console.log('===== End 3 =====')
    })
    

    刷新页面,控制台打印:

    ===== Start 1 =====
    ===== Start 2 =====
    ===== Start 3 =====
    ===== End 3 =====
    ===== End 2 =====
    ===== End 1 =====
    

    请求数据获取

    GET获取数据

    • 从上下文中直接获取
      • ctx.query
      • ctx.querystring
    • 从上下文的 request 对象中获取
      • ctx.request.query
      • ctx.request.querystring

    原生POST请求数据获取

    const Koa = require('koa')
    const app = new Koa()
    
    // 解析上下文里的 POST 参数
    function parsePostData(ctx) {
      return new Promise((resolve, reject) => {
        try {
          let postData = ''
          ctx.req.addListener('data', (data) => {
            postData += data
          })
          ctx.req.addListener('end', () => {
            let parseData = parseQueryStr(postData)
            resolve(parseData)
          })
        }
        catch(err) {
          reject(err)
        }
      })
    }
    
    // 将 POST 参数解析为 JSON
    function parseQueryStr(queryStr) {
      let queryData = {}
      let queryStrList = queryStr.split('&')
      console.log(queryStrList)
    
      for (let [index, queryStr] of queryStrList.entries()) {
        let itemList = queryStr.split('=')
        queryData[itemList[0]] = decodeURIComponent(itemList[1])
      }
      return queryData
    }
    
    app.use(async (ctx) => {
      console.log(ctx.url)
      console.log(ctx.method)
      if (ctx.url === '/form' && ctx.method === 'GET') {
        // GET 请求时,返回表单
        let html = `
          <h1>模拟表单提交</h1>
          <form method="POST" action="/form">
            <label for="user-name">用户名:</label>
            <input name="userName" type="text" id="user-name">
            <br>
            <label for="password">密码:</label>
            <input name="pwd" type="password" id="password">
            <br>
            <button type="submit">提交</button>
          </form>
        `
        ctx.body = html
      }
      else if (ctx.url === '/form' && ctx.method === 'POST') {
        console.log('yo')
        // POST 请求时,解析表单中的数据,并显示
        let postData = await parsePostData(ctx)
        ctx.body = postData
      }
      else {
        ctx.body = `<h1>好像哪里出了错误!</h1>`
      }
      
    })
    
    app.listen(3000, () => {
      console.log(`服务运行于:http://localhost:3000`)
    })
    

    koa-bodyparser中间件

    使用koa-bodyparser中间件可以简化我们的操作,省去繁琐的配置。

    安装

    npm i -S koa-bodyparser

    引入

    const Koa = require('koa')
    const bodyParser = require('koa-bodyparser')
    const app = new Koa()
    
    app.use(bodyParser())
     
    app.use(async ctx => { 
      ctx.body = ctx.request.body
    })
    

    静态资源中间件

    koa-static 中间件的作用

    koa-static 中间件用于处理静态资源的请求

    安装

    npm i -S koa-static

    Github

    使用

    const Koa = require('koa')
    const path = require('path')
    const static = require('koa-static')
    const app = new Koa()
     
    //设置静态资源的路径 
    const staticPath = './static'
     
    app.use(static(
      path.join( __dirname,  staticPath)
    ))
    
    app.use( async ( ctx ) => {
      ctx.body = 'hello world'
    })
    

    这时在 static 目录里新建一个 index.html 文件,随便写的内容,刷新页面,就能看到我们的新页面了。

    cookie 和 session

    Cookie

    什么是 cookie

    简单说就是服务器发送给用户浏览器并保存在本地的一小块数据,用来记录一些信息,方便下次访问时做一些事情。

    百度百科

    MDN

    koa2 中 Cookie 的使用

    设置Cookie的值
    ctx.cookies.set(name, value, [options])
    

    options 值可选,用来配置 cookie 的一些参数:

    • maxAge:一个数字,cookie的保留时长,单位豪秒,优先级高于 expires
    • expires:失效时间,为GMT格式(Wdy, DD-Mon-YYYY HH:MM:SS GMT),如果不设置或设置为以前的日期,则cookie会被立刻删除
    • signed:签名,布尔值
    • path:路径,默认是:“/”
    • domain:域名
    • secure:安全
    • httpOnly:服务器可访问,默认是 true
    • overwrite:是否覆盖以前的同名 cookie,默认是 false
    获取Cookie的值
    ctx.cookies.get(name)
    

    Session

    什么是 session

    session是一种保存在服务器上,用来记录客户状态的机制。

    百度百科

    Session 的工作流程

    当浏览器访问服务器并发送第一次请求时,服务器端会创建一个 session 对象,生产一个类似于 key,value 的键值对,然后将 key(cookie) 返回到浏览器端,浏览器下次再访问时,携带 key(cookie),找到对应的 session(value) 。客户的信息都保存在 session 中。

    使用 koa-session

    安装

    npm i -S koa-session

    Github

    引入
    const session = require('koa-session')
    
    设置
    // 配置 session 中间件
    app.keys = ['this is a key']
    const CONFIG = {
      key: 'xxx',  // cookie key 默认值:koa:sess
      maxAge: 1000 * 60 * 60 * 24,  // 过期时间
      autoCommit: true,  // 自动提交头,默认:true
      overwrite: true,  // 是否可以覆盖,默认:true
      httpOnly: true,  // 只有服务端可以获取,默认:true
      signed: true,  // 签名,默认:true
      rolling: false,  // 强制重置 cookie,默认:false
      renew: false  // 会话快过期时更新,已始终保持用户的登录状态,默认:false
    }
    app.use(session(CONFIG, app))
    
    使用
    设置值  ctx.session.username = '东方未明'
    获取值  ctx.session.username
    

    示例:

    const Koa = require('koa')
    const Router = require('koa-router')
    const session = require('koa-session')
    
    const app = new Koa()
    const router = new Router()
    
    // 配置 session 中间件
    app.keys = ['this is a key']
    const CONFIG = {
      key: 'xxx',  // cookie key 默认值:koa:sess
      maxAge: 1000 * 60 * 60 * 24,  // 过期时间
      autoCommit: true,  // 自动提交头,默认:true
      overwrite: true,  // 是否可以覆盖,默认:true
      httpOnly: true,  // 只有服务端可以获取,默认:true
      signed: true,  // 签名,默认:true
      rolling: false,  // 强制重置 cookie,默认:false
      renew: false  // 会话快过期时更新,已始终保持用户的登录状态,默认:false
    }
    app.use(session(CONFIG, app))
    
    router.get('/', (ctx) => {
      
      let name = ctx.session.username
      if (name) {
        ctx.body = `<h1>${name}你好!</h1>`
      }
      else {
        // 第一次访问时,设置session
        ctx.session.username = '东方未明'
        ctx.body = `<h1>欢迎来到武侠世界!</h1>`
      }
    
    })
    
    app.use(async (ctx, next) => {
      next()
      if (ctx.status == 404) {
        ctx.status = 404
        ctx.body = '您访问的页面好像丢了'
      }
    })
    
    // 启动路由
    app.use(router.routes())
    app.use(router.allowedMethods())  // 官方推荐用法,当所有路由中间件调用完后,根据 ctx.status 设置 response 响应头
    
    app.listen(3000, () => {
      console.log(`服务运行于:http://localhost:3000`)
    })
    
    客户端cookie

    Cookie 和 Session 区别

    1. cookie 数据存放在客户浏览器上,session 存在服务器上。
    2. cookie 安全性低于 session。
    3. session 会在一定时间内保存在服务器上,当访问增多时,会影响服务器性能,cookie不会。
    4. cookie 大小不超过4k,有的浏览器对 cookie 数有限制。

    引擎模板(pug)

    安装

    npm i -S pug

    npm i -S koa-views

    pug中文文档

    koa-views Github

    pug 原名 Jade,是一款健壮、灵活、功能丰富的模板引擎,专门为 Node.js 平台开发。

    koa-views 是一个支持模板呈现的中间件。

    使用

    项目目录中新建 views 目录,并新建 index.pug 文件:

    项目目录

    编写 index.pug

    doctype html
    html(lang='zh-Hans')
      head
        meta(charset='UTF-8')
        meta(name='viewport' content='width=device-width, initial-scale=1.0')
        meta(http-equiv='X-UA-Compatible' content='ie=edge')
        //- 这里得到传递来的参数
        title= pageTitle
        link(rel='stylesheet' href='./css/main.css')
      body
        h1 Hello Koa2
        p(class='test') 测试
        div(class='image-box')
          img(src='./img/mm.jpg' alt='美女')
    

    注意上面引用静态资源时链接的写法,不是 ../static/css/main.css 而是 ./css/main.css 这时因为我们用 koa-static 中间件来处理静态资源的请求,这里只需写我们设置的默认地址下的地址就可以了。

    const Koa = require('koa')
    const Router = require('koa-router')
    const path = require('path')
    const views = require('koa-views')  // 引入模板呈现中间件
    const static = require('koa-static')  // 引入静态资源中间件
    const viewsPath = './views'  // 模板默认地址
    const staticPath = './static'  // 静态资源默认地址
    
    const app = new Koa()
    const router = new Router()
    
    app.use(views(path.join(__dirname, viewsPath), {
      extension: 'pug'   // 视图默认扩展名
    }))
    
    router.get('/', async (ctx, next) => {
      await ctx.render('index', {
        pageTitle: '首页'   // 传递的参数
      })
    })
    
    app.use(static(path.join(__dirname, staticPath)))
    
    // 加载路由
    app.use(router.routes())
    app.use(router.allowedMethods())
    
    app.listen(3000, () => {
      console.log(`服务运行于:http://localhost:3000`)
    })
    

    相关文章

      网友评论

          本文标题:koa2学习笔记

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