学习egg.js,看这一篇就够了!

egg 介绍

egg 是什么?

egg 是阿里出品的一款 node.js 后端 web 框架,基于 koa 封装,并做了一些约定。

为什么叫 egg ?

egg 有孕育的含义,因为 egg 的定位是企业级 web 基础框架,旨在帮助开发者孕育适合自己团队的框架。

哪些产品是用 egg 开发的?

语雀 就是用 egg 开发的,架构图如下:

语雀架构图

哪些公司在用 egg?

盒马,转转二手车、PingWest、小米、58同城等(技术栈选型参考链接

egg 支持 Typescript 吗?

虽然 egg 本身是用 JavaScript 写的,但是 egg 应用可以采用 Typescript 来写,使用下面的命令创建项目即可(参考链接):

$ npx egg-init --type=ts showcase

用ts写egg案例

用 JavaScript 写 egg 会有智能提示吗?

会的,只要在 package.json 中添加下面的声明之后,会在项目根目录下动态生成 typings 目录,里面包含各种模型的类型声明(参考链接):

"egg": {
  "declarations": true
}

egg 和 koa 是什么关系?

koa 是 egg 的基础框架,egg 是对 koa 的增强。

学习 egg 需要会 koa 吗?

不会 koa 也可以直接上手 egg,但是会 koa 的话有助于更深层次的理解 egg。

创建项目

我们采用基础模板、选择国内镜像创建一个 egg 项目:

$ npm init egg --type=simple --registry=china
# 或者 yarn create egg --type=simple --registry=china

解释一下 npm init egg 这种语法:

npm@6 版本引入了 npm-init <initializer> 语法,等价于 npx create-<initializer> 命令,而 npx 命令会去 $PATH 路径和 node_modules/.bin 路径下寻找名叫 create-<initializer> 的可执行文件,如果找到了就执行,找不到就去安装。

也就是说,npm init egg 会去寻找或下载 create-egg 可执行文件,而 create-egg 包就是 egg-init 包的别名,相当于调用了 egg-init

创建完毕之后,目录结构如下(忽略 README文件 和 test 目录):

├── app
│?? ├── controller
│?? │?? └── home.js
│?? └── router.js
├── config
│?? ├── config.default.js
│?? └── plugin.js
├── package.json

这就是最小化的 egg 项目,用 npm iyarn 安装依赖之后,执行启动命令:

$ npm run dev

[master] node version v14.15.1
[master] egg version 2.29.1
[master] agent_worker#1:23135 started (842ms)
[master] egg started on http://127.0.0.1:7001 (1690ms)

打开 http://127.0.0.1:7001/ 会看到网页上显示 hi, egg

目录约定

上面创建的项目只是最小化结构,一个典型的 egg 项目有如下目录结构:

egg-project
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── app/
|   ├── router.js # 用于配置 URL 路由规则
│   ├── controller/ # 用于存放控制器(解析用户的输入、加工处理、返回结果)
│   ├── model/ (可选) # 用于存放数据库模型
│   ├── service/ (可选) # 用于编写业务逻辑层
│   ├── middleware/ (可选) # 用于编写中间件
│   ├── schedule/ (可选) # 用于设置定时任务
│   ├── public/ (可选) # 用于放置静态资源
│   ├── view/ (可选) # 用于放置模板文件
│   └── extend/ (可选) # 用于框架的扩展
│       ├── helper.js (可选)
│       ├── request.js (可选)
│       ├── response.js (可选)
│       ├── context.js (可选)
│       ├── application.js (可选)
│       └── agent.js (可选)
├── config/
|   ├── plugin.js # 用于配置需要加载的插件
|   ├── config.{env}.js # 用于编写配置文件(env 可以是 default,prod,test,local,unittest)

这是由 egg 框架或内置插件约定好的,是阿里总结出来的最佳实践,虽然框架也提供了让用户自定义目录结构的能力,但是依然建议大家采用阿里的这套方案。在接下来的篇章当中,会逐一讲解上述约定目录和文件的作用。

路由(Router)

路由定义了**请求路径(URL)控制器(Controller)**之间的映射关系,即用户访问的网址应交由哪个控制器进行处理。我们打开 app/router.js 看一下:

module.exports = app => {
  const { router, controller } = app
  router.get('/', controller.home.index)
};

可以看到,路由文件导出了一个函数,接收 app 对象作为参数,通过下面的语法定义映射关系:

router.verb('path-match', controllerAction)

其中 verb 一般是 HTTP 动词的小写,例如:

  • HEAD - router.head
  • OPTIONS - router.options
  • GET - router.get
  • PUT - router.put
  • POST - router.post
  • PATCH - router.patch
  • DELETE - router.deleterouter.del

除此之外,还有一个特殊的动词 router.redirect 表示重定向。

controllerAction 则是通过点(·)语法指定 controller 目录下某个文件内的某个具体函数,例如:

controller.home.index // 映射到 controller/home.js 文件的 index 方法
controller.v1.user.create // controller/v1/user.js 文件的 create 方法

下面是一些示例及其解释:

module.exports = app => {
  const { router, controller } = app
  // 当用户访问 news 会交由 controller/news.js 的 index 方法进行处理
  router.get('/news', controller.news.index)
  // 通过冒号 `:x` 来捕获 URL 中的命名参数 x,放入 ctx.params.x
  router.get('/user/:id/:name', controller.user.info)
  // 通过自定义正则来捕获 URL 中的分组参数,放入 ctx.params 中
  router.get(/^/package/([w-.]+/[w-.]+)$/, controller.package.detail)
}

除了使用动词的方式创建路由之外,egg 还提供了下面的语法快速生成 CRUD 路由:

// 对 posts 按照 RESTful 风格映射到控制器 controller/posts.js 中
router.resources('posts', '/posts', controller.posts)

会自动生成下面的路由:

HTTP方法

请求路径

路由名称

控制器函数

GET

/posts

posts

app.controller.posts.index

GET

/posts/new

new_post

app.controller.posts.new

GET

/posts/:id

post

app.controller.posts.show

GET

/posts/:id/edit

edit_post

app.controller.posts.edit

POST

/posts

posts

app.controller.posts.create

PATCH

/posts/:id

post

app.controller.posts.update

DELETE

/posts/:id

post

app.controller.posts.destroy

只需要到 controller 中实现对应的方法即可。

当项目越来越大之后,路由映射会越来越多,我们可能希望能够将路由映射按照文件进行拆分,这个时候有两种办法:

  1. 手动引入,即把路由文件写到 app/router 目录下,然后再 app/router.js 中引入这些文件。示例代码:

    // app/router.js
    module.exports = app => {
      require('./router/news')(app)
      require('./router/admin')(app)
    };
    
    // app/router/news.js
    module.exports = app => {
      app.router.get('/news/list', app.controller.news.list)
      app.router.get('/news/detail', app.controller.news.detail)
    };
    
    // app/router/admin.js
    module.exports = app => {
      app.router.get('/admin/user', app.controller.admin.user)
      app.router.get('/admin/log', app.controller.admin.log)
    };
    
  2. 使用 egg-router-plus 插件自动引入 app/router/**/*.js,并且提供了 namespace 功能:

    // app/router.js
    module.exports = app => {
      const subRouter = app.router.namespace('/sub')
      subRouter.get('/test', app.controller.sub.test) // 最终路径为 /sub/test
    }
    

除了 HTTP verb 之外,Router 还提供了一个 redirect 方法,用于内部重定向,例如:

module.exports = app => {
  app.router.get('index', '/home/index', app.controller.home.index)
  app.router.redirect('/', '/home/index', 302)
}

中间件(Middleware)

egg 约定一个中间件是一个放置在 app/middleware 目录下的单独文件,它需要导出一个普通的函数,该函数接受两个参数:

  • options: 中间件的配置项,框架会将 app.config[${middlewareName}] 传递进来。
  • app: 当前应用 Application 的实例。

我们新建一个 middleware/slow.js 慢查询中间件,当请求时间超过我们指定的阈值,就打印日志,代码为:

module.exports = (options, app) => {
  return async function (ctx, next) {
    const startTime = Date.now()
    await next()
    const consume = Date.now() - startTime
    const { threshold = 0 } = options || {}
    if (consume > threshold) {
      console.log(`${ctx.url}请求耗时${consume}毫秒`)
    }
  }
}

然后在 config.default.js 中使用:

module.exports = {
  // 配置需要的中间件,数组顺序即为中间件的加载顺序
  middleware: [ 'slow' ],
  // slow 中间件的 options 参数
  slow: {
    enable: true
  },
}

这里配置的中间件是全局启用的,如果只是想在指定路由中使用中间件的话,例如只针对 /api 前缀开头的 url 请求使用某个中间件的话,有两种方式:

  1. config.default.js 配置中设置 match 或 ignore 属性:

    module.exports = {
      middl
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Egg.js 是一个基于 Node.js 和 Koa.js 的企业级应用框架,用于构可扩展的后端应用程序。在部署 Egg.js 应用之前,你需要进行打包以及配置服务器环境。 下面是一般的 Egg.js 打包部署步骤: 1. 打包应用:首先,你需要将 Egg.js 应用打包成可执行文件。你可以使用 Egg.js 提供的命令行工具进行打包,例如运行 `npm run build` 或 `yarn build`。 2. 配置服务器环境:接下来,你需要在服务器上进行配置。确保服务器已经安装了 Node.js 运行环境,并且版本符合应用的要求。你还需要安装 PM2 或其他的进程管理工具,以便在服务器上守护进程运行应用。 3. 上传文件:将打包好的应用文件上传到服务器。你可以使用 FTP、SCP 或其他文件传输工具将文件上传到指定的目录。 4. 安装依赖:在服务器上进入应用所在的目录,运行 `npm install` 或 `yarn install` 命令,安装应用所需的依赖包。 5. 启动应用:使用 PM2 或其他进程管理工具来启动应用。例如,运行 `pm2 start app.js` 命令来启动应用,并将它作为一个守护进程在服务器上运行。 6. 配置反向代理(可选):如果你希望通过域名访问应用,则需要配置反向代理。可以使用 Nginx 或其他的反向代理服务器来进行配置,将请求转发到应用所在的端口。 以上是一般的 Egg.js 打包部署步骤,具体的配置和步骤可能因服务器环境和需求而有所不同。你可以根据实际情况进行调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值