Egg简介

Egg

前言

Egg.js 为企业级框架和应用而生。基于Koa开发封装,性能优异,内置多进程管理,具有高扩展性,且提供了基于Egg定制上层框架的能力,帮助开发团队降低了开发维护成本。

约定先于配置,相较于express更加灵活可配。

Koa

Koa是Express原班人马导致的,致力于web应用和API领域更小,更丰富的web框架。其和express设计风格类似,底层采用同一套HTTP基础库。

  • koa采用中间件洋葱图,所有请求经过一个中间件时均会被执行2次,可以方便进行后置逻辑处理,而express中间件只会执行一次
  • koa新增了Context对象,其作为上下文贯穿整个请求过程,其上也挂载了ResquestResponse对象,而express只有ResquestResponse挂载在中间件上。
  • 使用async await,可以同步的方式书写异步代码,书写便捷,异常捕获错误。

使用

安装egg

npm install -g egg

使用脚手架搭建项目

$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm i

npm run dev 启动项目,默认可访问http://localhost:7001

约定目录

egg-project
├── package.json
├── app.js (可选)							//项目入口文件
├── agent.js (可选)						//进程处理,例如日志打印等
├── app
|   ├── router.js							//路由定义
│   ├── controller						//处理用户输入,返回所需结果
│   |   └── home.js
│   ├── service (可选)				 //处理业务层逻辑,调用后端api
│   |   └── user.js
│   ├── middleware (可选)			 //中间件
│   |   └── response_time.js
│   ├── schedule (可选)				 //定时任务
│   |   └── my_task.js
│   ├── public (可选)					 //静态资源
│   |   └── reset.css
│   ├── view (可选)						 //前端页面,模板文件
│   |   └── home.tpl
│   └── extend (可选)					 //扩展配置
│       ├── helper.js (可选)
│       ├── request.js (可选)
│       ├── response.js (可选)
│       ├── context.js (可选)
│       ├── application.js (可选)
│       └── agent.js (可选)
├── config									 //配置文件
|   ├── plugin.js						 //插件配置
|   ├── config.default.js
│   ├── config.prod.js
|   ├── config.test.js (可选)
|   ├── config.local.js (可选)
|   └── config.unittest.js (可选)
└── test									  //单元测试
    ├── middleware
    |   └── response_time.test.js
    └── controller
        └── home.test.js

文件加载顺序

  • 加载 plugin,找到应用和框架,加载 config/plugin.js
  • 加载 config,遍历 loadUnit 加载 config/config.{env}.js
  • 加载 extend,遍历 loadUnit 加载 app/extend/xx.js
  • 自定义初始化,遍历 loadUnit 加载 app.jsagent.js
  • 加载 service,遍历 loadUnit 加载 app/service 目录
  • 加载 middleware,遍历 loadUnit 加载 app/middleware 目录
  • 加载 controller,加载应用的 app/controller 目录
  • 加载 router,加载应用的 app/router.js

优先级:插件>框架>应用,被依赖的优先加载,越底层的越先加载

生命周期

app.js

// 项目启动时执行
class AppBootHook {
    constructor(app) {
      this.app = app;
    }
  
    configWillLoad() {
      console.log("配置文件即将加载,这是最后动态修改配置的时机")
    }
  
    configDidLoad() {
        console.log("配置文加载完成,可在此处获取配置文件信息")
    }
  
    async didLoad() {
        console.log("所有文件加载完成")
    }
  
    async willReady() {
        console.log("插件启动完成")
    }
  
    async didReady() {
        console.log("worker准备就绪")
    }
  
    async serverDidReady() {
        console.log("应用启动完成,服务已在监听状态")
    }
  
    async beforeClose() {
        console.log("应用即将关闭")
    }
  }
  
  module.exports = AppBootHook;

路由

路由配置在:app/router.js

router.get(routerName, path,middleware,controller);
  • routerName:路由别名
  • path:路由可访问的路径
  • middleware:中间件(可选)
  • controller:映射到具体的controller逻辑方法

get为路由请求方式,controller会以.为区分,查找app/controller目录下对用的文件,最后一个标识表示controller中的method名,如果不存在则默认访问index()

例如:

  router.post('/admin', isAdmin, app.controller.admin);

isAdmin是中间件,访问/admin时,将执行app/controller/admin文件下的index()

请求参数获取

//get请求
let name = ctx.query.name

//post请求
let name = ctx.body.name

//路由上动态参数
let name = ctx.params.name

内置对象

  • Application:全局唯一实例对象,在应用任意地方均可以获取。获取方式:this.app
  • Context:请求级别对象,包含了用户请求信息响应信息,一般存在于MiddlewareControllerService中。获取方式:this.ctx
  • Config:应用配置信息,存在于ControllerServiceHelper获取方式:app.configthis.config
  • Logger:日志对象。使用方式:app.logger

ControllerServiceHelper:这三个类,均内置了如下属性:

  • ctx - 当前请求的 Context 实例。
  • app - 应用的 Application 实例。
  • config - 应用的配置
  • service - 应用所有的 service
  • logger - 为当前 controller 封装的 logger 对象。
  • helper-公共函数辅助对象

中间件

中间件配置在:app/middleware下。

写法

module.exports = (options,app)=>{
    //每个中间件都接收2个参数,options:中间件属性,app:应用实例对象
    //可获取配置信息
    return async (ctx, next) => {
        //中间件返回一个异步的方法,方法接收2个参数,ctx:请求上下文,next:下一次执行的函数
        //中间件逻辑处理
        await next()
        
    }
}

每个中间件都接收2个参数,options:中间件自定义配置,app:应用实例

其需要导出一个异步函数。

使用

定义好的中间件需要我们手动进行加载配置。

config/config.default.js中定义需要加载的中间件,然后配置其所需属性

  config.middleware = [
    "test"	//注册test中间件需要加载
  ];

  config.test = {	//test中间件属性配置
    name: "中间件测试"
  }

每个中间件都有3个默认属性:

  • enable:控制中间件是否开启。
  • match:设置只有符合某些规则的请求才会经过这个中间件。
  • ignore:设置符合某些规则的请求不经过这个中间件。

控制器(Controller)

所有的controller文件均应放在app/controller目录下,因为应用约定在该目录下查找路由对应的逻辑处理,支持多级目录。

写法

// app/controller/post.js
const Controller = require('egg').Controller;
class PostController extends Controller {
  async create() {
    const { ctx, service } = this;
    const createRule = {
      title: { type: 'string' },
      content: { type: 'string' },
    };
    // 校验参数
    ctx.validate(createRule);
    // 组装参数
    const author = ctx.session.userId;
    const req = Object.assign(ctx.request.body, { author });
    // 调用 Service 进行业务处理
    const res = await service.post.create(req);
    // 设置响应内容和响应状态码
    ctx.body = { id: res.id };
    ctx.status = 201;
  }
}
module.exports = PostController;

每个自定义控制器都应该继承eggController类,这样才能将内置属性挂载到自定义控制器上。

每个方法都应该是一个async await函数

每个文件只能存在一个导出类,必须用module.exports导出。

模板渲染

Context提供了3个Promise接口进行模板渲染。

  • render(name, locals) 渲染模板文件, 并赋值给 ctx.body
  • renderView(name, locals) 渲染模板文件, 仅返回不赋值
  • renderString(tpl, locals) 渲染模板字符串, 仅返回不赋值

locals为需要传递给前端的数据,是一个对象

数据返回

ctx.body=data

服务(Service)

所有的controller文件均应放在app/service目录下。支持多级目录

// app/service/user.js
const Service = require('egg').Service;

class UserService extends Service {
  async find(uid) {
    const user = await this.ctx.db.query('select * from user where uid = ?', uid);
    return user;
  }
}

module.exports = UserService;

每个方法都应该是一个async await函数,且必须继承egg.Service

每个文件只能存在一个导出类,必须用module.exports导出。

service的ctx可以发起请求,也可以访问数据库。

  • this.ctx.curl 发起网络调用。
  • this.ctx.service.otherService 调用其他 Service。
  • this.ctx.db 发起数据库调用等, db 可能是其他插件提前挂载到 app 上的模块。

模板渲染

egg支持egg-view-nunjucks模板引擎,静态资源模板采用egg-view-assets

插件下载

npm i egg-view-nunjucks --save
npm i egg-view-assets --save

启动插件

// config/plugin.js
module.exports = {
  assets: {
    enable: true,
    package: 'egg-view-assets',
  },
  nunjucks: {
    enable: true,
    package: 'egg-view-nunjucks',
  }
};

插件配置

// config/config.default.js
//配置模板根目录,这样在controller中render时则可省略根目录直接在该目录下查找指定文件
config.view = {
    root: path.join(appInfo.baseDir, 'app/view/entry'),
    mapping: {
      ".jsx": "assets"
    }
  }

//指定模板位置,编译后输出位置,本地测试打包配置devServer
  config.assets={
    templatePath:path.join(appInfo.baseDir, 'app/view/template/index.html'),
    templateViewEngine: 'nunjucks',
    devServer: {
      command: 'roadhog dev',
      debug: true,
      port: 8000,
      timeout:180*1000,
      env: {
        BROWSER: 'none',
        ESLINT: 'none',
        SOCKET_SERVER: 'http://127.0.0.1:8000',
        PUBLIC_PATH: 'http://127.0.0.1:8000',
      },
    },

使用
模板文件app/view/template/index.html

<!doctype html>
<html>

<head>
    <meta charset="utf-8" />
    <title>模板渲染</title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="renderer" content="webkit">
    {{ helper.assets.getStyle() | safe }}
</head>

<body>
    <div id="ReactApp"></div>
    {{ helper.assets.getScript() | safe }}
</body>

</html>

helper.assets可动态的获取render参数,并且生成可访问的映射路径,例如:home.js -> http://127.0.0.1:8000/home.js
默认情况下publicPath:"/",如果配置publicPath:"public,则映射关系也将改变,home.js -> http://127.0.0.1:8000/public/home.js

webpack配置,.webpackrc

{
  "entry": "app/view/entry/*.jsx",
  "extraBabelPlugins": [
    [ "import", { "libraryName": "antd", "style": true } ]
  ],
  "outputPath": "app/public",
  "disableCSSModules": true,
  "hash": true,
  "manifest": {
    "fileName": "../../../config/manifest.json"
  }
}

这里引入了antd,所以package.json需含有 @babel/corebabel-plugin-import这两个依赖。
roadhog在开发环境下publicPath永远为/

入口文件,app/view/entry/home.jsx

import React from "react";
import ReactDom from "react-dom";

const Home = () => {
    return (
        <div>我是入口模板</div>
    )
}

ReactDom.render(<Home />, document.getElementById('ReactApp'));

模板渲染,app/controller/home.js

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    await ctx.render("home.jsx");
  }
}
module.exports = HomeController;

参考地址

egg官网:https://eggjs.org/zh-cn/basics/structure.html

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值