Egg安装
Egg.js属于MVC类型的NodeJS框架
$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm i
$ npm run dev
$ open localhost:7001
配置文件
1、config
|- config.default.js // 默认配置文件, 所有环境都会加载这个配置文件, 一般用于开发环境的默认配置文件
|- config.test.js // 开发环境配置
|- config.prod.js // 生产环境配置
|- config.unittest.js
|- plugin.js // 控制插件
`- config.local.js
module.exports = {
mysql: {
enable: true, // 开启或关闭插件
package: 'egg-mysql' // package为一个npm模块,package.json中的dependencies中,框架会在node_modules目录中找到这个插件入口
},
};
也可以指定path 来替代 package
const path = require('path');
module.exports = {
mysql: {
enable: true,
path: path.join(__dirname, '../app/plugin/egg-mysql'),
},
};
2、app.js文件
app.js是入口文件,需要手动创建在根目录下,包括了生命周期函数
module.exports = app => {
console.log('start')
}
Router 路由
用来接收请求URL,在交给controller处理
app.get(路由名,路由URL路由,middleware(可以配置多个),controller)
一、controller的两个写法
# router
module.exports = app => {
// 1、controller目录的home.js进行处理
app.get('/', 'home');
// 2、payTool目录、billquery.js文件、getLoanUser方法
app.post('/getLoanUser', 'payTool.billquery.getLoanUser');
};
# controller/home.js
module.exports = function* () {
this.body = 'Hello World'; // 响应给客户端
};
# controller/payTool/billquery.js
module.exports = app => {
class BillqueryController extends app.Controller {
async getLoanUser(){
const { ctx, app, service } = this;
const response = await service.payTool.billquery.getRepayPlan(ctx.request.body);
ctx.body = response;
}
}
}
二、路由中间件 - 在访问路由之前通过中间件来对路由进行一些处理
app.get('router-name', 'middleware1', 'middleware2', 'controlll');
1、创建中间件 checkAuth.js middleware
module.exports = app => {
return async function checkAuth(ctx, next){
const user = ctx.session.user;
console.log(`UserSession ------ ${JSON.stringify(user)}`)
if(user){
console.log(`visited true`);
await next();
}
else{
console.log(`visited false`);
ctx.body = {
data:{},
status: 10001,
message: 'Session失效'
}
}
}
}
2、路由中调用
注意如果路由中使用中间件分为全局和和单个路由第一次两种,如果配置到config.default.js中的就为全局
// 引用中间件
const checkAuth = app.middleware.checkAuth();
// 不需要验证
app.get('/', 'home.index');
app.get('/query', 'query.query.queryPage');
// 需要验证是否登录的
app.post('/web/getUserList', checkAuth, 'user.user.getUserList'); // 获取用户列表
三、参数获取
1、获取url参数 ctx.query
http://127.0.0.1:7001/search?name=egg 取出name的值
// app/router.js
module.exports = app => {
app.get('/search', app.controller.search);
};
// app/controller/search.js
module.exports = function* (ctx) {
tx.body = `search: ${this.query.name}`;
};
2、路由参数 ctx.params
http://127.0.0.1:7001/user/123/xiaoming 取出 123、xiaoming
// app/router.js
module.exports = app => {
app.get('/user/:id/:name', app.controller.user.info);
};
// app/controller/user.js
exports.info = function* (ctx) {
ctx.body = `user: ${ctx.params.id}, ${ctx.params.name}`;
};
四、重定向 - 当路由不存在时会走一个默认的路由
1、内部重定向
// app/router.js
module.exports = app => {
app.get('index', '/home/index', 'home.index');
app.redirect('/', '/home/index', 302);
};
2、外部重定义 controller中来执行路由的跳转 redirect
// app/router.js
module.exports = app => {
app.get('/search', 'search');
};
// app/controller/search.js
module.exports = function* () {
const type = this.query.type;
const q = this.query.q || 'nodejs';
// 这里跳转
ctx.redirect('/admin/home');
};
五、多路由映射 - 将路由分类并用一个路由主文件加载其它的路由文件
// router.js 通过一个主文件将两个路由加载进来
module.exports = app => {
require('./router/news')(app);
require('./router/admin')(app);
};
// router/news.js
module.exports = app => {
app.get('/news/list', app.controller.news.list);
app.get('/news/detail', app.controller.news.detail);
};
// router/admin.js
module.exports = app => {
app.get('/admin/user', app.controller.admin.user);
app.get('/admin/log', app.controller.admin.log);
};
六、路由规则
// app/router.js
module.exports = app => {
app.get(/^\/package\/([\w-.]+\/[\w-.]+)$/, app.controller.package.detail);
};
Controller
controller继承于app.controller, 负责解析用户输入,处理后返回相应的结果, 框架推荐Controller层主要对用户请求参数进行处理(校验、转换),然后用service处理业务结果并返回
用户HTTP请求(路由处理) <=> 校验组装(controller处理)<=> 业务处理(service)
一、Controller的两种写法
1、函数写法
// app/controller/posts.js
exports.index = function* () {};
exports.new = function* () {};
exports.create = function* () {};
// router 调用
app.get('/index', 'posts/index');
app.get('/new', 'posts/new');
app.get('/create', 'posts/create');
2、类写法
// app/controller/post.js
module.exports = app => {
class PostController extends app.Controller { // 定义一个PostController的类,继承了app.Controller
* 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 = yield service.post.create(req);
// 设置响应内容和响应状态码
ctx.body = { id: res.id };
ctx.status = 201;
}
}
return PostController;
}
// router 调用 app/router.js
module.exports = app => {
app.post('createPost', '/api/posts', 'post.create');
}
二、app.Controller下this上挂载的几个属性
1、this.ctx: 当前请求的上下文Context对象的实例
2、this.app: Application对象的实例,可以取到框架的全局对象和方法
3、this.service: 取到service下的方法
4、this.config: 获取或添加修改config的配置
5、this.logger: 日志打印
三、获取请求
ctx.method: 请求方法
ctx.path: 请求路径
ctx.host: 请求IP
1、query 获取GET请求中传递参数
url: /posts?category=egg&language=node 通过context.query拿到url的参数
class QueryController extends app.Controller{
async getQuery(app){
const { ctx, app, service } = this;
const queryName = ctx.query; // {category: 'egg', language: 'node'}
ctx.body = queryName;
}
}
2、queries 能取到重复的参数key
// posts?category=egg&id=1&id=2&id=3 参数解析成对象,id有多个
class QueryController extends app.Controller{
async getQuery(app){
const { ctx, app, service } = this;
const queryName = ctx.queries; // {category: 'egg', id: [1,2,3]}
ctx.body = queryName;
}
}
3、params 参数
路由: app.get('/projects/:projectId/app/:appId', 'app.listApp');
// 请求: GET /projects/1/app/2
class QueryController extends app.Controller{
async getParams(app){
const { ctx, app, service } = this;
const params = {
id: ctx.params.projectId,
appid: ctx.params.appId
};
ctx.body = queryName;
}
}
4、body 获取post传的数据
框架内置bodyParser中间件,挂载到 context.request.body上,来取post的内容
ctx.request.body 来获取post传过来的参数 {"username":"","password":"1111"}
exports.listPosts = function* (ctx) {
assert.equal(ctx.request.body.title, 'controller');
assert.equal(ctx.request.body.content, 'what is controller');
};
<input type="text" name="title" value="" />
<input type="text" name="content" value="" />
config.default.js设置, 默认body最大长度为100kb
bodyParser: {
jsonLimit: '1mb',
formLimit: '1mb',
}
5、获取上传文件
浏览器上都是通过 Multipart/form-data 格式发送文件的,框架通过内置 Multipart 插件来支持获取用户上传的文件
<form method="POST" action="/upload?_csrf={{ ctx.csrf | safe }}" enctype="multipart/form-data">
title: <input name="title" />
file: <input name="file" type="file" />
<button type="submit">上传</button>
</form>
const path = require('path');
const sendToWormhole = require('stream-wormhole');
module.exports = function* (ctx) {
const stream = yield ctx.getFileStream();
const name = 'egg-multipart-test/' + path.basename(stream.filename);
// 文件处理,上传到云存储等等
let result;
try {
result = yield ctx.oss.put(name, stream);
} catch (err) {
// 必须将上传的文件流消费掉,要不然浏览器响应会卡死
yield sendToWormhole(stream);
throw err;
}
ctx.body = {
url: result.url,
// 所有表单字段都能通过 `stream.fields` 获取到
fields: stream.fields,
};
};
在config/config.default.js 中配置来新增支持的文件扩展名,或者重写整个白名单
新增支持的文件扩展名
module.exports = {
multipart: {
fileExtensions: [ '.apk' ], // 增加对 .apk 扩展名的支持
},
};
覆盖整个白名单
module.exports = {
multipart: {
whitelist: [ '.png' ], // 覆盖整个白名单,只允许上传 '.png' 格式
},
};
6、发送HTTP响应
1)设置status
exports.create = function* (ctx) {
// 设置状态码为 201
ctx.status = 201;
};
2)设置body 响应给请求方
exports.show = function* (ctx) {
ctx.body = {
name: 'egg',
category: 'framework',
language: 'Node.js',
};
};
exports.page = function* (ctx) {
ctx.body = '<html><h1>Hello</h1></html>';
};
获取整个 header 对象的方法: context.headers、context.header、context.request.headers、context.request.header
get()方法获取请求 header 中的一个字段的值、字段不存在返回null: context.get(name)、context.request.get(name)
一、获取header
1、ctx.header 取出头信息
async from(ctx){
console.log(ctx.header);
}
返回结果
{
host: '127.0.0.1:7001',
connection: 'keep-alive',
'content-length': '21',
'postman-token': 'a7e094b5-21ce-eb94-e403-3d517c832f30',
'cache-control': 'no-cache',
origin: 'chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'content-type': 'application/json',
accept: '*/*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.8,en;q=0.6',
cookie: 'csrfToken=RNHUjeOf0Zsl9jNl2f5BhreT'
}
2、ctx.protocol 协议
3、ctx.ips 返回ip 一个数组
4、ctx.ip 返回请求方的ip
5)、ctx.host
二、设置header
context.set(key, value) 方法可以设置一个响应头,context.set(headers) 设置多个 Header。
exports.show = function* (ctx) {
const start = Date.now();
ctx.body = yield ctx.service.post.get();
const used = Date.now() - start;
// 设置一个响应头
ctx.set('show-response-time', userd.toString());
};
Cookie
可以通过context.cookies来获取和设置cookie值
一、cookie操作
获取 ctx.cookies.get('count');
设置 ctx.cookies.set('count', ++count);
删除 ctx.cookies.set('count', null);
二、设置options
context.cookies.set(key, value, options)
1、maxAge: nunber - 在浏览器保存最长时间
2、expires: date - 设置这个键值的失效时间
3、path: string - 设置生效的URL路径
4、domain: string - 设置生效的域名
5、httpOnly: boolean - 设置是否可以被js访问
6、secure: boolean - 设置键值只在HTTPS连接上传输
7、overwrite: boolean - 设置key相当键值如何处理,true为后设置的覆盖前面设置的,false发送两个
8、sign: boolean - 对cookie是否进行签名,true防止前端对这个值进行篡改
9、encrypt: boolean - 对cookie进行加密
ctx.cookies.set(key, value, {
httpOnly: false,
sign: false,
});
三、Cookie 密钥
config/config.default.js
module.exports = {
keys: 'key1,key2',
};
Session
context.session 来访问或者修改当前用户 Session
1、读取和设置 Session
exports.fetchPosts = function* (ctx) {
// 获取 Session 上的内容
const user = ctx.session.user;
const posts = yield ctx.service.post.fetch(userId);
// 修改 Session 的值
ctx.session.user = ctx.session.user ? ctx.session.user++ : 1;
ctx.body = {
success: true,
posts,
};
};
2、删除session
exports.deleteSession = function* (ctx) {
ctx.session = null;
};
3、设置 session配置
// config.default.js
exports.session = {
key: 'EGG_SESS',
maxAge: 24 * 3600 * 1000, // 1 天
httpOnly: true,
encrypt: true,
};
service 处理业务逻辑
service 需要继承于 app.Service,service在复杂业务场景下做业务逻辑封装的抽象层
一、service的ctx
1、this.ctx.curl - 发起网络调用
2、this.ctx.service.otherService - 调用service
3、this.ctx.db - 发起数据库调用
二、Example
// 调用服务 app/controller/user.js
module.exports = app => {
class User extends app.controller {
async addUser(){
let userData = {
name: 'siguang',
password: 'xxxxxx'
}
let isAddUser = yield ctx.service.user.createUser(userData)
if(setUser){
ctx.body = '添加成功'
}
ctx.body = isAddUser ? '添加成功' : '添加失败';
}
}
}
// 定义一个Service app/service/user.js
module.exports = app => { // 中间件支持两个参数 options中间件配置,app当前应用Application的实例
class User extends app.Service {
async createUser(params){
const url = '/account/blance/createUser';
let result = await this.ctx.curl(url, {
method: 'POST',
dataType: 'json', // 自动解析 JSON response
data: params,
timeout: 15000, // 15 秒超时
}
this.ctx.logger.info('响应结果:', result.data);
return result.data;
}
}
return User;
};
Middleware 中间件
一、config.default.js 配置全局
module.exports = {
// 配置需要的中间件,数组顺序即为中间件的加载顺序
middleware: [ 'gzip' ],
// 配置 gzip 中间件的配置
gzip: {
threshold: 1024, // 小于 1k 的响应体不压缩
},
};
// 框架中默认的中间件
module.exports = {
bodyParser: {
jsonLimit: '10mb',
},
};
二、通用配置
enable:控制中间件是否开启。
match:设置只有符合某些规则的请求才会经过这个中间件。
ignore:设置符合某些规则的请求不经过这个中间件。
bodyParser: {
enable: false,
jsonLimit: '10mb',
},
三、Example:
1、创建中间件 checkAuth.js middleware
module.exports = app => {
return async function checkAuth(ctx, next){
const user = ctx.session.user;
console.log(`UserSession ------ ${JSON.stringify(user)}`)
if(user){
console.log(`visited true`);
await next();
}
else{
console.log(`visited false`);
ctx.body = {
data:{},
status: 10001,
message: 'Session失效'
}
}
}
}
2、路由中调用
注意如果路由中使用中间件分为全局和和单个路由第一次两种,如果配置到config.default.js中的就为全局
// 引用中间件
const checkAuth = app.middleware.checkAuth();
// 不需要验证
app.get('/', 'home.index');
app.get('/query', 'query.query.queryPage');
// 需要验证是否登录的
app.post('/web/getUserList', checkAuth, 'user.user.getUserList'); // 获取用户列表
JSONP
jsonp只能get请求
// config/config.default.js jsonp的配置
exports.jsonp = {
callback: 'callback', // 识别 query 中的 `callback` 参数
limit: 100, // 函数名最长为 100 个字符
};
// app/router.js 路由加jsonp中间件
module.exports = app => {
const jsonp = app.jsonp();
app.get('/api/posts/:id', jsonp, 'posts.show'); // 通过jsonp中间件来让路由支付一个中间件
app.get('/api/posts', jsonp, 'posts.list');
};
// app/controller/posts.js
exports.show = function* (ctx) {
ctx.body = {
name: 'egg',
category: 'framework',
language: 'Node.js',
};
};
用户请求 /api/posts/1?callback=fn,响应为 JSONP 格式,如果用户请求 /api/posts/1
HttpClient
Node模拟客户端请求,curl请求默认 content-type: application/x-www-form-urlencoded,
app.curl(url, options) 和 app.httpclient.request(url, options) 两种方法相同
一、Example:
module.exports = app => {
class HttpClientController extends app.Controller{
async getHttpClient(){
const {ctx, app, service} = this;
let result = await this.ctx.curl('http://goucai.diyicai.com/lottery/getissue.action?lotteryId=001&issueLen=100&d=1502966960306', {
methods: 'get'
});
let phones = [];
let resultObj = JSON.parse(result.data.toString());
resultObj.forEach((data, idx, arr)=>{
console.log('data------', data);
phones.push(data.endTime);
})
ctx.body = phones.join(',')
}
}
return HttpClientController;
}
二、config配置 // config/config.default.js
exports.httpclient = {
// 默认开启 http/https KeepAlive 功能
keepAlive: true,
// 空闲的 KeepAlive socket 最长可以存活 4 秒
freeSocketKeepAliveTimeout: 4000,
// 当 socket 超过 30 秒都没有任何活动,就会被当作超时处理掉
timeout: 30000,
// 允许创建的最大 socket 数
maxSockets: Infinity,
// 最大空闲 socket 数
maxFreeSockets: 256,
// 是否开启本地 DNS 缓存,默认关闭
// 一旦设置开启,则每个域名的 DNS 查询结果将在进程内缓存 10 秒
enableDNSCache: false,
};
三、options对象
let options = {};
const result = yield ctx.curl('https://httpbin.org/get?foo=bar', options);
1、mothod: 请求方法
2、data: 需要发送的数据 { foo: 'bar' }
3、dataAsQueryString: Boolean 如果为 true 即使在post情况下,也会强制将options.data以 querystringstringify处理后拼接到url的query参数上
4、content(String|Buffer): 发送请求的正文,如果设置了此参数会忽略data参数
ctx.curl(url, {
method: 'POST',
// 直接发送原始 xml 数据,不需要 HttpClient 做特殊处理
content: '<xml><hello>world</hello></xml>',
headers: {
'content-type': 'text/html',
},
});
5、stream(ReadStream): 发送请求正文的可读数据流
ctx.curl(url, {
method: 'POST',
stream: fs.createReadStream('/path/to/read'),
});
6、writeStream: 接受响应数据的可写数据流
ctx.curl(url, {
writeStream: fs.createWriteStream('/path/to/store'),
});
7、consumeWriteStream: Boolean 是否等待 writeStream 完全写完才算响应全部接收完毕,默认是 true
8、contentType: 请求数据的格式,默认undefined
9、dataType: 响应数据格式
10、headers: 自定义请求头
11、timeout: 请求超时时间 默认是 [ 5000, 5000 ],即创建连接超时是 5 秒,接收响应超时是 5 秒。
12、agent:
13、httpsAgent
14、auth、digestAuth
15、followRedirect:Boolean 是否自动跟进3xx的跳转响应
16、maxRedirects: number 最大自动跳转次数
17、formatRedirectUrl: 自定义实现302、301
18、beforeRequest: 请求发送前会调用beforeRequest钩子
ctx.curl(url, {
beforeRequest: options => {
// 例如我们可以设置全局请求 id,方便日志跟踪
options.headers['x-request-id'] = uuid.v1();
}
});
19、streaming:是否直接返回响应流
20、gzip: Boolean是否开始gzip
21、timing: Boolean 是否开启请求各阶段的时间没是
四、Get请求
1、options.method = 'post' 设置请求参数,默认是get请求可以不用加
const result = yield ctx.curl('https://httpbin.org/get?foo=bar', {method: post});
2、status: 响应状态码
3、headers: 响应头信息 有{'conent-type': 'text/html'}
4、data: 响应body, 返回的是Buffer类型, 如果设置了options.dataType会根据参数来处理
五、Post请求
module.exports = function* post(ctx) {
const result = yield ctx.curl('https://httpbin.org/post', {
// 必须指定 method
method: 'POST',
// 通过 contentType 告诉 HttpClient 以 JSON 格式发送
contentType: 'json',
// 要传的数据
data: {
hello: 'world',
now: Date.now(),
},
// 明确告诉 HttpClient 以 JSON 格式处理返回的响应 body
dataType: 'json',
});
ctx.body = result.data;
};
六、From表单提交
以 ontent-type: application/x-www-form-urlencoded 的格式提交请求数据
// app/controller/form.js
module.exports = function* form(ctx) {
const result = yield ctx.curl('https://httpbin.org/post', {
// 必须指定 method,支持 POST,PUT 和 DELETE
method: 'POST',
// 不需要设置 contentType,HttpClient 会默认以 application/x-www-form-urlencoded 格式发送请求
data: {
now: Date.now(),
foo: 'bar',
},
// 明确告诉 HttpClient 以 JSON 格式处理响应 body
dataType: 'json',
});
ctx.body = result.data.form;
// 响应最终会是类似以下的结果:
// {
// "foo": "bar",
// "now": "1483864184348"
// }
};
七、Multipart 方式上传
From 表单提交时包含文件的时候,需要用到 multipart/form-data 进行提交了
引用fromstream 第三方模块
// app/controller/multipart.js
const FormStream = require('formstream');
module.exports = function* multipart(ctx) {
const form = new FormStream();
// 设置普通的 key value
form.field('foo', 'bar');
// 上传当前文件本身用于测试
form.file('file', __filename);
const result = yield ctx.curl('https://httpbin.org/post', {
// 必须指定 method,支持 POST,PUT
method: 'POST',
// 生成符合 multipart/form-data 要求的请求 headers
headers: form.headers(),
// 以 stream 模式提交
stream: form,
// 明确告诉 HttpClient 以 JSON 格式处理响应 body
dataType: 'json',
});
ctx.body = result.data.files;
// 响应最终会是类似以下的结果:
// {
// "file": "'use strict';\n\nconst For...."
// }
};
// 添加更多文件
form.file('file1', file1);
form.file('file2', file2);
八、以Stream 方式上传文件
Stream 实际会以 Transfer-Encoding: chunked 传输编码格式发送
// app/controller/stream.js
const fs = require('fs');
module.exports = function* stream(ctx) {
// 上传当前文件本身用于测试
const fileStream = fs.createReadStream(__filename);
// httpbin.org 不支持 stream 模式,使用本地 stream 接口代替
const url = `${ctx.protocol}://${ctx.host}/stream`;
const result = yield ctx.curl(url, {
// 必须指定 method,支持 POST,PUT
method: 'POST',
// 以 stream 模式提交
stream: fileStream,
});
ctx.status = result.status;
ctx.set(result.headers);
ctx.body = result.data;
// 响应最终会是类似以下的结果:
// {"streamSize":574}
};
服务端模板渲染
读取数据渲染模板,呈现给用户
$ npm i egg-view-ejs --save // 安装
$ 配置
// config/plugin.js
exports.nunjucks = {
enable: true,
package: 'egg-view-nunjucks' // 开启插件使用numjucks模板
};
// config/config.default.js
exports.view = {
defaultViewEngine: 'nunjucks',
mapping: {
'.tpl': 'nunjucks',
},
};
render、renderString两个方法
render(fileName, locals, viewOptions): filenName: 文件路径、locals: 渲染的数据、viewOptions: 用户传入的配置
render(name, locals) 渲染模板文件, 并赋值给 ctx.body
renderView(name, locals) 渲染模板文件, 仅返回不赋值
renderString(tpl, locals) 渲染模板字符串, 仅返回不赋值
- numjucks模板语法: http://mozilla.github.io/nunjucks/cn/templating.html 见模板md -
静态资源
Egg内置static插件,static默认映射到 public目录
app/public
├── css
│ └── news.css
└── js
├── lib.js
└── news.js
框架扩展
一、application 全局应用对象扩展
访问 ctx.app, Controller,Middleware,Helper,Service 中都可以通过 this.app 访问到 Application 对象
// app.js
module.exports = app => {
app.config
};
// 扩展 app.foo() 方法 app/extend/application.js
module.exports = {
foo(param) {
// this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
}
};
二、context 扩展,可以在ctx中调用扩展方法
extend/content.js
module.export = {
async isIOS() {
const iosReg = /iphone|ipad|ipod/i;
return iosReg.test(this.get('user-agent'));
},
}
引用 await this.ctx.isIOS();
三、helper 用来编写一些实用函数
// app/extend/helper.js
const moment = require('moment');
exports.relativeTime = time => moment(new Date(time * 1000)).fromNow();
// 在controller里使用
async newsList(){
const { ctx } = this;
const time = ctx.helper.relativeTime();
this.ctx.logger.info('data: %j', time)
}
// 也可以在模板里面使用:
<!-- app/views/news/list.tpl -->
{{ helper.relativeTime(item.time) }}
四、request
五、response
Schedule 定时任务
有些任务需要定时来完成,如定时上报,定时远程更新本地缓存,定时任务都统一存放在 app/schedule 目录下
创建一个定时任务 app/schedule/update_cache.js
module.exports = {
// 通过 schedule 属性来设置定时任务的执行间隔等配置
schedule: {
interval: '1m', // 1 分钟间隔
type: 'all', // 指定所有的 worker 都需要执行
immediate: false,
disable: !app.config.enableWarningSchedule,
},
// task 是真正定时任务执行时被运行的函数,第一个参数是一个匿名的 Context 实例
* task(ctx) {
const res = yield ctx.curl('http://www.api.com/cache', {
dataType: 'json',
});
ctx.app.cache = res.data;
}
};
1、type: 两种类型 worker 或 all,worker是每台机器只有一个worker执行定时任务, all是每台机器上的每个worker都会执行这个定时任务
2、interval:定时执行的时间
3、cron:指定一个执行的时间 ss mm hh dd mm dd
// 每三小时准点执行一次
cron: '0 0 */3 * * *',
4、immediate:如果为true,定时任务会在启动并ready后立即执行一次任务
5、disable:为true,这个定时任务不会被执行
启动自定义
用来进行应用启动时进行初始化工作
// 通过入口文件app.js
module.exports = app => {
app.beforeStart(function* () {
// 应用会等待这个函数执行完成才启动
app.cities = yield app.curl('http://example.com/city.json', {
method: 'GET',
dataType: 'json',
});
});
};
logger 日志
this.ctx.logger.info('xxxxxx');
logger.debug()、logger.info()、logger.warn()、logger.error()
config.deubgger 文件配置
logger: {
level: 'DEBUG',
dir: '../app/logger',
}
本地开发
egg-bin模块(用于本地开发和单元测试)
package.json:
{
"scripts": {
"dev": "egg-bin dev --port 7001"
}
}
插件的开发
自定义插件存储目录: lib/plugin/...
egg-ua插件
1、创建package.json
{
"eggPlugin": {
"name": "ua"
}
}
2、egg-ua/us.js插件文件
module.exports = {
get isIOS() {
const iosReg = /iphone|ipad|ipod/i;
return iosReg.test(this.get('user-agent'));
},
};
3、config/plugin.js 通过path来挂载插件
const path = require('path');
exports.ua = {
enable: true,
path: path.join(__dirname, '../lib/plugin/egg-ua'),
};
项目目录
├── package.json
├── app.js (可选) // 启动初始化
├── agent.js (可选)
├── app
| ├── router.js // 路由规则
│ ├── controller // 处理用户的输入,返回相应的结果
│ | └── home.js
│ ├── service (可选) // 服务,编写业务逻辑
│ | └── 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 (可选) // ctx扩展
│ ├── 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
积累
一、egg启动时指定端口
在package.json中
script: {
"dev-fundmgmt": "PLATFORM=fundmgmt egg-bin dev --port 20101", // --port 20101
}
二、vue-cli中webpack是否使用热更新
script: {
"dev-fundmgmt": "HOT_ENV=true node build/dev-server.js FUNDMGMT",
}
HOT_ENV=true 为false
二、vue中取启动里的变量
script: {
"dev-fundmgmt": "HOT_ENV=true node build/dev-server.js FUNDMGMT",
}
vue-cli中的dev.env.js
var PLAT = process.argv; // 取出命令中返回数组 ['HOT_ENV=true', 'node', 'build/dev-server.js', 'FUNDMGMT'];
if (PLAT.toUpperCase().trim() === 'FUND') {
PLAT = 'fund'
}
else if(PLAT.toUpperCase().trim() === 'FUNDMGMT'){
PLAT = 'fundmgmt';
}
else {
PLAT = 'portal'
}
| http://koa.bootcss.com/
国内查看评论需要代理~