• JS 流行框架(三):Koa2


    JS 流行框架(三):Koa2

    Koa2 是 Express 团队基于 ES7 编写的 Web 框架,在此框架中,Koa2 利用 Promise 和 async 实现异步方法,在 Koa 中最显著的特点就是将原本内置在 Express 中的已经封装好的中间件分别封装到了不同的模块中,所以 Koa 与 Express 相比是更轻量级的

    基本使用

    在使用 Koa2 之前,必须先下载 Koa2,示例如下:

    npm install koa --save
    
    • 1

    默认情况下,若未特别说明版本,那么下载的就是 Koa2.X

    在 Koa2 下载完成之后,就可以非常快速地创建一个服务器应用程序,示例如下:

    // 1. 导入 Koa
    const Koa = require('koa');
    
    // 2. 创建 koa 实例
    const app = new Koa();
    
    // 利用 koa 实例响应请求
    app.use(ctx => {
      ctx.body = 'Hello Koa!';
    })
    
    // 3. 利用 koa 实例监听端口
    app.listen(3000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    上述示例中,系统将自动向 koa 实例 use 方法中的回调函数传入 ctx 实例,此实例中包含了请求实例 request 和响应实例 response,也包含了 Koa 补充的属性和方法

    网页

    静态资源

    由于 Koa2 将处理静态资源的中间件封装到了 koa-static 模块中,所以在使用之前必须先下载此模块,示例如下:

    npm install koa-static --save
    
    • 1

    在下载之后就可以利用此模块处理静态资源,示例如下:

    // 导入 koa-static
    const serve = require('koa-static');
    
    // 利用 koa-static 模块处理静态资源
    app.use(serve('public'));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述示例中,参数 public 为静态资源所在的目录

    动态资源

    由于 Koa2 将处理动态资源的中间件封装到了 koa-views 模块中,所以在使用之前必须先下载此模块,示例如下:

    npm install koa-views --save
    
    • 1

    在下载之后就可以利用此模块处理动态资源,示例如下:

    // 导入 koa-views
    const views = require('koa-views');
    
    // 利用 koa-views 模块处理动态资源
    app.use(views('views', {extension: 'ejs'}));
    
    // 响应动态资源时渲染网页
    app.use(async ctx => {
      await ctx.render('login', {username: 'Reyn Morales', password: 1024});
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上述示例中,参数 views 为动态资源所在的目录,extension 属性说明渲染动态网页的模板引擎,类似于在 Express 中,动态资源的扩展名必须为模板引擎的名称

    路由

    由于 Koa2 将处理路由的中间件封装到了 koa-router 模块中,所以在使用之前必须先下载此模块,示例如下:

    npm install koa-router --save
    
    • 1

    在下载之后就可以利用此模块处理路由,示例如下:

    • /router/user.js
    // 导入 koa-router
    const Router = require('koa-router');
    
    // 创建 Router 实例
    const router = new Router();
    
    // 路由前缀
    router.prefix('/api/user');
    
    // get 请求
    router.get('/info', (ctx, next) => {
      ctx.body = {
        username: 'Reyn Morales',
        age: 21,
        gender: 'Male'
      }
    });
    
    // post 请求
    router.post('/login', (ctx, next) => {
      ctx.body = 'Login Success';
    });
    
    // 导出 router 实例
    module.exports = router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    上述示例中,通过 router 的 prefix 实例方法可以注册路由前缀,之后处理请求时可以省略此前缀,此外,ctx 实例的 body 方法可以为字符串或实例,如果被赋值为字符串,那么等价于 res.writeHead 和 res.end,如果被赋值为实例,那么等价于 res.writeHead 和 res.json

    • /app.js
    // 导入 router 实例
    const userRouter = require('./router/user');
    
    app
      .use(userRouter.routes())           // 启动路由
      .use(userRouter.allowedMethods());  // 响应头
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    参数

    get 请求

    静态路由

    示例如下:

    router.get('/info', (ctx, next) => {
      /* 以实例的形式 */
      console.log(ctx.request.query);
      /* 以字符串的形式 */
      console.log(ctx.request.querystring);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    动态路由

    示例如下:

    router.get('/register/:username/:password', (ctx, next) => {
      console.log(ctx.params);
    });
    
    • 1
    • 2
    • 3

    必须注意的是,在客户端发送请求时路由中必须以 / 字符分隔所有的参数

    post 请求

    由于 Koa2 将处理 post 请求参数的中间件封装到了 koa-bodyparser 模块中,所以在使用之前必须先下载此模块,示例如下:

    npm install koa-bodyparser --save
    
    • 1

    在下载之后就可以利用此模块处理 post 请求参数,示例如下:

    // 导入 koa-bodyparser
    const bodyParser = require('koa-bodyparser');
    
    // 利用 koa-bodyparser 模块处理 post 请求参数
    app.use(bodyParser());
    
    // 尝试获取 post 请求参数
    router.post('/login', (ctx, next) => {
      console.log(ctx.request.body);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Cookie

    在 Koa2 中不用导入任何模块即可以通过 ctx 实例操作 Cookie

    添加

    示例如下:

    router.get('/register/:username/:password', (ctx, next) => {
      ctx.cookies.set('user', ctx.params.username, {
        path: '/',
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000
      });
      ctx.body = 'Register Success';
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    必须注意的是,在添加 Cookie 时不能将中文字符作为 value 中存储到客户端中,此时可以将中文字符先转换为 base64 的字符串,然后将此字符串作为 value 存储到 Cookie 中,此处不再详细说明

    获取

    示例如下:

    router.get('/info', (ctx, next) => {
      console.log(ctx.cookies.get('user'));
      ctx.body = {
        username: 'Reyn Morales',
        age: 21,
        gender: 'Male'
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    错误处理

    由于 Koa2 将处理错误的中间件封装到了 koa-onerror 模块中,所以在使用之前必须先下载此模块,示例如下:

    npm install koa-onerror --save
    
    • 1

    在下载之后就可以利用此模块处理错误,示例如下:

    // 导入 koa-onerror
    const onerror = require('koa-onerror');
    
    // 启动 onerror
    onerror(app);
    
    // 错误处理
    app.use((err, ctx) => {
      console.log(err.status, err.message);
      ctx.body = err.message;
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    上述示例中,err 实例的 status 属性为错误状态码,而 message 属性为错误信息

    koa-generator

    koa-generator 是一个 Koa 项目的脚手架工具,通过此工具可以快速生成一个功能齐全的项目框架,步骤如下:

    1. 下载 koa-generator

      npm install koa-generator -g
      
      • 1
    2. 利用 koa2 命令创建一个 koa2 项目

      koa2 <ProjectName>
      
      • 1
    3. 利用 cd 命令切换至项目目录下

      cd <ProjectName>
      
      • 1
    4. 通过 npm 命令下载项目所依赖的包

      npm install
      
      • 1

    常用模块

    • koa-static
      • 静态资源
    • koa-views
      • 动态资源
    • koa-router
      • 路由
    • koa-bodyparser
      • POST 请求
    • koa-onerror
      • 错误处理
    • koa-json
      • JSON 格式化
    • koa-logger
      • 日志记录
    • koa-generic-session
      • Session
    • koa-redis
      • Redis
    • koa-morgan
      • 日志记录

    Cookie 跨域

    默认情况下,Cookie 不能跨域访问,此时可以通过 Nginx 反向代理实现 Cookie 的跨域访问

    在 URL 中,如果协议、域名(不同级别)或端口号中任何一个或若干个不同,即为跨域

    代理

    正向

    正向代理即顺着请求的方向代理,例如,通常情况下我们不能访问谷歌服务器,而通过一台海外服务器可以访问谷歌,那么我们可以访问此海外服务器,此海外服务器访问谷歌,从而实现访问谷歌,此时,这台海外服务器即为正向代理服务器,在正向代理中,代理服务器为用户服务,对于谷歌来说,并不知道是哪一个用户访问,只知道有一台服务器访问了自己,用途如下:

    • 访问类似于谷歌等无法访问的资源
    • 对客户端访问授权,上网认证
    反向

    反向代理和正向代理相反,在反向代理中,代理服务器为服务器服务,对于用户来说,并不知道自己真正访问的服务器是谁,只知道自己访问了一台服务器,用途如下:

    • 负载均衡,通过反向代理服务器来优化网站的负载
    • 实现前后端分离开发, 统一请求地址

    反向代理服务器可以根据用户的路由地址将请求分发到不同的服务器,例如,如果用户访问根路由,那么将请求发送至前端服务器,如果用户访问接口路由,那么将请求发送至后端服务器,如果在后端添加或访问 Cookie,那么最终都是通过代理服务器向浏览器中添加或访问,因此解决了 Cookie 的跨域问题

    Nginx

    通常情况下,我们可以利用 Nginx 实现反向代理,在 Nginx 的配置文件 /conf/nginx.conf 中可以编写相关的代理信息,如下所示

    worker_processes 4;
    
    location / {
        proxy_pass ;
    }
    
    location /api {
        proxy_pass ;
        proxy_set_header Host $host;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    通过 http-server 模块可以快速以服务器的形式启动一个 Web 前端项目

    PM2

    在考虑将 Node 项目上线时,必须考虑如下问题:

    • 稳定性
      • 程序不会因为某个错误或异常导致项目停止服务
    • 日志记录
      • 除了记录访问日志以外,我们还需要记录错误日志和自定义日志
    • 资源利用
      • Node 程序是单线程的,而服务器是多核的,只运行一个 Node 程序太浪费资源

    此时可以通过 PM2 解决上述问题

    • PM2 的进程守护可以在程序崩溃后自动重启项目
    • PM2 附带日志记录功能,可以很方便的记录错误日志和自定义日志
    • PM2 可以同时启动多个 Node 程序以充分利用服务器资源

    基本使用

    在使用 PM2 之前,必须先下载,示例如下:

    npm install pm2 -g
    
    • 1

    通过 version 选项可以查看 PM2 的版本,示例如下:

    pm2 --version
    
    • 1

    通过如下指令可以启动一个 Node 程序:

    pm2 start <FileName>
    
    • 1

    示例如下:

    pm2 start app.js
    
    • 1

    常用指令

    PM2 的常用指令如下所示:

    指令含义
    pm2 start 启动应用程序
    pm2 list列出启动的所有应用程序
    pm2 restart 重启应用程序
    pm2 info 查看应用程序详细信息
    pm2 log 显示指定应用程序的日志
    pm2 monit 监控应用程序
    pm2 stop 停止应用程序
    pm2 delete 关闭并删除所有应用

    进程守护

    默认情况下,如果程序出现错误或抛出异常,那么 PM2 将自动重启 Node 程序以实现服务的稳定性

    日志记录

    如果通过 PM2 启动 Node 应用程序,在程序中,如果利用 console.log 方法输出信息,那么将自动被保存为自定义日志,如果利用 console.error 方法输出信息,那么将自动被保存为错误日志

    常用配置

    实际上,也可以通过 PM2 的配置文件启动 Node 应用程序,通过配置文件可以自行配置项目名称和日志记录等相关信息,示例如下:

    {
      "apps": {
        "name": "MyApp",
        "script": "app.js",
        "watch": true,
        "ignore_watch": [
          "node_modules",
          "logs"
        ],
        "error_file": "logs/err.log",
        "out_file": "logs/custom.log",
        "log_date_format": "YYYY-MM-DD HH:mm:ss"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    上述示例中,name 属性表示项目名称,script 属性表示入口文件,watch 属性表示是否开启监视,ignore_watch 属性表示省略监视哪些文件,error_file 属性表示错误日志记录位置,out_file 属性表示自定义日志记录位置,log_data_format 属性表示日志记录时的时间格式,此外,必须注意的是,PM2 配置文件的名称为 pm2.config.json

    负载均衡

    在 PM2 的配置文件中可以通过 instances 属性实现负载均衡,示例如下:

    {
      "apps": {
        "name": "MyApp",
        "script": "app.js",
        "watch": true,
        "ignore_watch": [
          "node_modules",
          "logs"
        ],
        "error_file": "logs/err.log",
        "out_file": "logs/custom.log",
        "log_date_format": "YYYY-MM-DD HH:mm:ss",
        "instances": 4
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    必须注意的是,instances 属性的取值不能超过服务器的 CPU 核数,此外,在修改了配置文件后,必须关闭项目后再次重启才能生效

  • 相关阅读:
    厨卫电器行业S2B2C系统网站解决方案:打造S2B2C平台全渠道商业系统
    7、android 高级控件(1)(下拉列表)
    Android Camera 测试环境搭建:Android 原生代码下载
    使用Navicat将服务器的Oracle数据库备份到本地(Windows版本)
    lombok
    webpack和vite的区别
    深入浅出 Linux 中的 ARM IOMMU SMMU I
    cmake简洁教程 - 第二篇
    支付宝支付接口的调用
    滑动谜题 -- BFS
  • 原文地址:https://blog.csdn.net/Raymiles/article/details/128134466