$ npm i egg-init -g
$ egg-init egg-example --type=simple
$ cd egg-example
$ npm i
$ npm run dev
打开浏览器访问 http://127.0.0.1:7001/
$ npm install -g think-cli
$ thinkjs new demo;
$ cd demo;
$ npm install;
$ npm start;
打开浏览器访问 http://127.0.0.1:8360/
从创建项目的脚手架来看两者基本一致,但是think-cli 支持 使用命令行创建controller、service、model等
$ thinkjs controller <controller-name> [module-name]
$ thinkjs service <service-name> [module-name]
$ thinkjs model <model-name> [module-name]
更多使用方法查看https://github.com/thinkjs/think-cli
默认配置在config文件夹下config.default.js
写法
module.exports = {
keys:'my-cookie-secret-key',
};
// 或
exports.keys = 'my-cookie-secret-key';
// 或
module.exports = appInfo => {
return {
keys: 'my-cookie-secret-key'
};
};
支持多环境配置
多个配置文件会覆盖合并,框架在启动时会把合并后的最终配置 dump 到 run/application_config.json
(worker 进程)和 run/agent_config.json
(agent 进程)
根据不用功能划分不同配置文件
config.js
通用的一些配置adapter.js
adapter 配置router.js
自定义路由配置middleware.js
middlware 配置validator.js
数据校验配置extend.js
extend 配置写法
module.exports = {
keys:'my-cookie-secret-key',
};
// 或
exports.keys = 'my-cookie-secret-key';
支持多环境配置
最终配置文件会合并,合并后的配置在runtime/config/[env].json
中
支持动态设置配置
Egg.js和Thinkjs都用扩展Koa对象,app,request,response,context
启动自定义脚本获取
// app.js
module.exports = app => {
app.cache = new Cache();
};
controller 中使用 this.ctx.app
this.ctx
this.ctx.request
this.ctx.response
任何地方使用think.app
controller 中使用 this.ctx
this.ctx.req
this.ctx.res
两者都是基于Koa实现的,中间件基于洋葱圈模型
两者写法一致
// middleware/log.js
const defaultOptions = {
consoleExecTime: true // 是否打印执行时间的配置
}
module.exports = (options = {}) => {
// 合并传递进来的配置
options = Object.assign({}, defaultOptions, options);
return (ctx, next) => {
if(!options.consoleExecTime) {
return next(); // 如果不需要打印执行时间,直接调用后续执行逻辑
}
const startTime = Date.now();
let err = null;
// 调用 next 统计后续执行逻辑的所有时间
return next().catch(e => {
err = e; // 这里先将错误保存在一个错误对象上,方便统计出错情况下的执行时间
}).then(() => {
const endTime = Date.now();
console.log(`request exec time: ${endTime - startTime}ms`);
if(err) return Promise.reject(err); // 如果后续执行逻辑有错误,则将错误返回
})
}
}
用法:在config.default.js 配置
module.exports = {
// 配置需要的中间件,数组顺序即为中间件的加载顺序
middleware: [ 'log' ],
// 配置 log 中间件的配置
log: {
consoleExecTime: true,
},
};
用法:在src/config/middleware.js
文件 ,数组顺序即为中间件的加载顺序
module.exports = [
{
handle: 'log', // 中间件处理函数
options: { // 当前中间件需要的配置
consoleExecTime: true,
},
}
]
写法基本类似,只不过thinkjs中需要额外添加一个Action
// base_controller.js
// 使用this.success()方法统一对外输出,参考thinkjs返回
'use strict';
const { Controller } = require('egg');
class BaseController extends Controller {
success(data) {
this.ctx.body = {
errno: 0,
errmsg: '',
data,
};
}
fail(msg, no) {
this.ctx.body = {
errno: no || 400,
errmsg: msg || '内部错误',
};
}
}
module.exports = BaseController;
// controller
const Controller = require('./../../core/base.controller.js');
module.exports = class extends Controller {
async getUser() {
this.success({username:"hou"});
}
};
controller生效还要添加路由,比Thinkjs麻烦。
module.exports = app => {
const { router, controller } = app;
router.get('/v1/user/getUser', controller.v1.user.getUser);
};
const Base = require('./../base');
module.exports = class extends Base {
//比egg.js多了一个Action,让框架自动识别为controller.通多think-router中间件实现
async getUserAction() {
this.success({username:"hou"});
}
};
红红火火恍恍惚惚,让egg.js的controller集成自定义的一个基类。强行使他们写法一致!?
都支持restful。
module.exports = app => {
const { router, controller } = app;
router.get('/v1/user/getUser', controller.v1.user.getUser);
};
对于controller,不用写router。也可以自定义,我没有用到过,就不写了。
// app/router.js
module.exports = app => {
// 注意这里是resources方法。
app.router.resources('topics', '/api/v2/topics', app.controller.topics);
};
映射关系
Method | Path | Route Name | Controller.Action |
---|---|---|---|
GET | /posts | posts | app.controllers.posts.index |
GET | /posts/new | new_post | app.controllers.posts.new |
GET | /posts/:id | post | app.controllers.posts.show |
GET | /posts/:id/edit | edit_post | app.controllers.posts.edit |
POST | /posts | posts | app.controllers.posts.create |
PUT | /posts/:id | post | app.controllers.posts.update |
DELETE | /posts/:id | post | app.controllers.posts.destroy |
// router.js
module.exports = [
['/user/:id?', 'rest']
]
通过自定义路由,将 /user/:id
相关的请求指定为 REST Controller,然后就可以对其访问了。
GET /user
获取用户列表,执行 getAction
GET /user/:id
获取某个用户的详细信息,执行 getAction
POST /user
添加一个用户,执行 postAction
PUT /user/:id
更新一个用户,执行 putAction
DELETE /user/:id
删除一个用户,执行 deleteAction
基本一样
// service/user.js
const Service = require('egg').Service;
module.exports = class extends Service {
async find(id) {
return {username:'hou',id:id}
}
};
// controller.js
this.ctx.service.user.find(11111);
// service/user.js
module.exports = class extends think.Service {
find(id) {
return {username:'hou',id:id}
}
}
// controller
think.service('user').find(11111)
使用npm安装插件
$ npm i egg-mysql --save
使用
// config/plugin.js
exports.mysql = {
enable: true,
package: 'egg-mysql',
};
// config/config.default.js
exports.mysql = {
// 单数据库信息配置
client: {
// host
host: 'mysql.com',
// 端口号
port: '3306',
// 用户名
user: 'test_user',
// 密码
password: 'test_password',
// 数据库名
database: 'test',
},
// 是否加载到 app 上,默认开启
app: true,
// 是否加载到 agent 上,默认关闭
agent: false,
};
使用npm安装插件
$ npm i think-model-mysql --save
使用
// config/adapter.js
exports.model = {
type: 'mysql',
common: {
logConnect: isDev,
logSql: isDev,
logger: msg => think.logger.info(msg)
},
mysql: {
handle: mysql,
database: '',
prefix: 'think_',
encoding: 'utf8',
host: '127.0.0.1',
port: '',
user: 'root',
password: 'root',
dateStrings: true
}
};
每个文件写一个定时任务,存放在app/schedule
module.exports = {
schedule: {
interval: '1m', // 1 分钟间隔
type: 'all', // 指定所有的 worker 都需要执行
},
async task(ctx) {
console.log('触发定时任务')
},
};;
所有定时任务存在一个文件中
// src/config/crontab.js
module.exports = [{
interval: '10s',
type:'all',
handle: () => {
console.log('触发定时任务')
}
}]
两者都是有一个master进程和若干worker进程。不同的是Egg.js多一个agent进程。
在这方面Egg.js比Thinkjs要方便许多。一些脏活累活都可以交给agent进程去做。比如长连接,监听消息队列等。
worker间通信都是通过master进程中转
// agent.js 监听消息队列
'use strict';
const mqtt = require('mqtt');
module.exports = agent => {
const client = mqtt.connect('mqtt://test.mosquitto.org');
client.on('connect', function() {
console.log('链接成功');
client.subscribe('asdasdasd');
});
client.on('message', function(topic, message) {
console.log(message.toString());
agent.messenger.sendToApp('shoudaoxiaoxi', message.toString());// 发给所有app进程
agent.messenger.sendRandom('shoudaoxiaoxi', message.toString());// 发给随机一个app进程
});
};
//app.js 接收agent进程发送的消息
module.exports = app => {
app.messenger.on('shoudaoxiaoxi', msg => {
app.logger.info(msg);
// 创建一个匿名上下文来访问服务
const ctx = app.createAnonymousContext();
ctx.runInBackground(async () => {
await ctx.service.source.update(); //直接访问service
app.lastUpdateBy = msg;
});
});
};
在Thinkjs中监听消息队列只能在master中了。如果有好的办法实现上边功能,请教我。
完整的单元测试框架使用Mocha
官网上没有提到单元测试内容,但是好像也支持,脚手架生成项目有test文件夹,使用的框架是ava
使用egg-scripts
模块部署。脚手架自带。
npm run start ## 启动
npm run stop ## 停止
与阿里的alinode快速集成。
转译npm run compile
node production.js