**前言: **

  • Egg.js为企业级开发应用,而产生的一门Node.js框架

  • 它使用模块化渐进式开发,内置多进程管理(Node是单进程,无法使用多核CPU的能力),具有高度可扩展的插件机制。

  • 基于Koa开发,性能优异, 框架稳定,测试覆盖率高

1.安装项目

1.安装项目

1.安装模块mkdir egg_demo                              1.建立项目文件夹yarn create egg  --type=simple              2.创建简单类型的项目yarn install(或者yarn)                       3.安装项目依赖的包2.开启项目yarn dev                      4.开启项目(默认7001端口)yarn start                    5.项目正常运行(但有运行时占用cpu非常大)
  • yarn 会自动查找模块,所以不需要install,一般也不需要global。 (安装的比较慢,可能需要等待3-4分钟)

  • yarn start 会在后台开启一个服务,关闭需要使用 yarn stop 结束。 用于项目 上线

  • yarn dev 可以对当前的变化进行更新。

  • 如果程序在开发时出现线程断开的情况,需要首先考虑 plugin.js和config.default.js 的配置正确与否

2.项目结构


egg-example
├── app                       应用程序
│   ├── controller            控制器
│   │   └── home.js
│   ├── public                公有路径
│   ├── service               服务层
│   ├── middleware            中间件
│   ├── view                  视图文件
│   └── router.js             路由设置
├── config                    配置文件
│   └── config.default.js
└── package.json

2.router路由

1.封装router文件

(1. 在当前位置引入全局应用对象, app/router.js 的对象内添加以下内容

 require('./router/default')(app)        // 导入模块, 需要添加全局应用对象

(2.新建立router路由文件夹, 创建default.js

module.exports = app=>{ // 单独分离出的路由const {router,controller} = approuter.get('/default/index',controller.default.home.index)
}

3.controller控制器

1.创建控制器

yarn dev 功能可以对当前的请示结果进行分析。

函数内部必须使用async修饰, 在controller文件夹下新建“mini.js”文件,并填写以下内容。

'use strict';const Controller = require('egg').Controller;class MinicatController extends Controller {async index() {const { ctx } = this; // 获取当前的上下文对象ctx.body = '<h1>I am a MiniCat</h1>';}async getGirls() {const { ctx } = this;await new Promise(resolve => { // 异步单元测试, 等待异步请求结果setTimeout(() => {resolve(ctx.body = '<h1>小妹向你走过去啦!</h1>');});});}
}module.exports = MinicatController;

2.测试控制器

向router添加路由

  router.get('/mini', controller.minicat.index); // 添加路由

在test/app/controller 添加 minicat.test.js

'use strict';const { app } = require('egg-mock/bootstrap');describe('minicat test', () => {it('minicat index', () => {return app.httpRequest().get('/mini').expect(200).expect('<h1>I am a MiniCat</h1>');});it('minicat getGirls', () => {return app.httpRequest().get('/getGirls').expect(200).expect('<h1>小妹向你走过去啦!</h1>');});
});

4.service 服务层

Service就是在复杂业务场景下用于做业务逻辑封装的一个抽象层。就是把业务逻辑代码进一步细化和分类,所以和数据库交互的代码都放到Service中。这样作有三个明显的好处。

  • 保持Controller中的逻辑更加简介。
  • 保持业务逻辑的独立性,抽象出来的Service可以被多个Controller调用。
  • 将逻辑和展现分离,更容易编写测试用例。

1.创建service服务层

在app下新建立minicat.js文件,并填写以下内容

'use strict';
const Service = require("egg").Service;class MinicatService extends Service{// Service方法也是异步方法async getGirl3(id){//因为没有真实连接数据库,所以模拟数据return {id:id,name:'小红',age:18}}
}module.exports =MinicatService;

2.控制器与服务的交互

// // service数据层async getGirls3(){const {ctx} = this;      // 向服务层传入数据const res = await ctx.service.minicat.getGirl3(ctx.query.id);ctx.body = res }

5.middleware中间件

中间件的作用域是全局,访问不同的页面, 只要添加ctx.session.xxx都会有效 。

1.配置中间件

在config/config.default.js 添加下面的内容

  /* 配置中间件 */config.middleware = [ 'counter' ];

2.创建中间件

官方要求中间件保存在 app/middleware 文件夹下。 其中,middleware是新创建的。

module.exports = options =>{return async (ctx,next) =>{if(ctx.session.counter){ctx.session.counter++;}else{ctx.session.counter=1;}await next();      // 异步等待下一个方法或者页面,防止页面卡死}
}

3.路由使用的中间件

在router.js中定义 counter 中间件。

每次访问路由都会执行一次添加,可以认为它是路由访问前,进行一定计数。

只要是调用ctx.session.counter, 次数都会加 1. 但如果路由也使用了这个中间件,则会加 2。

 //  启动router中间件const counter = app.middleware.counter() router.post('/add1', controller.minicat.add1);router.post('/show1',counter, controller.minicat.show1);

6.schedule定时任务

官方要求 ,定时任务需要按照Egg的约定,/app目录下,新建shedule文件夹。然后再创建getTime.js文件

作用: 在一定的时间间隔内,对当前的配置进行设置。

创建后定时任务,重启项目后, 即可正常运行

const Subscription = require('egg').Subscriptionclass GetTime extends Subscription{static get schedule(){return {// interval:'3s',          // 间隔时间3scron: '*/3 * * * * *',    // 间隔时间3stype:'worker'     // 类型}}async subscribe(){         // 方法会自动执行, 一般会去检查配置console.log(Date.now())}
}module.exports = GetTime;    // 暴露这个类中的所有方法// module.exports = {         // 只是暴露这个类。
//     GetTime:GetTime
// }; // export default GetTime;    // es6 暴露方法无效
*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    |
│    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
│    │    │    │    └───── month (1 - 12)
│    │    │    └────────── day of month (1 - 31)
│    │    └─────────────── hour (0 - 23)
│    └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, optional)

7.public静态资源文件

在public目录下添加 default.css 样式文件

则可以在view模板文件中直接找到


<link rel="stylesheet" href="/public/default.css"> 

直接访问public下的静态资源:(这是需要添加public路径的框架,但django的static也是需要添加到访问路径当中)


http://localhost:7001/public/assets/file/38e25517-3981-4987-aaf1-8c6c830ea097.jpg

控制器中使用视图文件

async getPage(){ render('hello.html')
}

8.获取数据方法

1.获取Request数据

__get方法// 自由传参const username = ctx.query.username;// 严格传参(path路径)const params = ctx.params.name__post方法>>>>>  注意:egg.js的post方法只遵循标准的x-www-form-urlencoded方式。 form-data不可以// post请求数据,是保存的请求包体中。使用ctx.request.bodyconst title  = ctx.request.body.title,__headers请求头数据// 获取tokenconst token = ctx.request.headers.token__url, method请求数据const url = ctx.request.url /method__file文件数据const req = ctx.request.files; let temp  = req[0].filepath  //临时文件路径__response响应数据 ctx.body = { msg:"hello"}ctx.body = "测试成功"  // ctx.body响应结果

2.获取MySQL数据

1.基本操作


// 1.插入数据
const data = {title: 'Hello World',username:"xxx",password:"xxxx"
}
res = await app.mysql.insert(tTable,data);    // res2.insertId  可以查看插入的数据// 2.删除数据
const data = {id:123   // 根据目标id删除
}
res = await  app.mysql.delete(tTable,data );// 3.更新数据
const data = {// id:123  // 根据目标id更新username:'xxx',password:'xxxx' //新数据
}
const options = {where: {custom_id: 456}
};
res = await  app.mysql.update(tTable,data, options); // 4.查询数据
const  options = { where: { id: '23',name:'小明'  }, columns: ['author', 'title'],  orders: [['created_at','desc'], ['id','desc']], limit: 10,  offset: 0,
})
// select默认会获取所有数据
res = await  app.mysql.select(tTable, options);
// get 查询第一条符合条件的数据
const post = await this.app.mysql.get(tTable, options);
// query方法执行原生sql语句
res = await app.mysql.query(`select * from ${tTable} where  ${new_params} `) 

2.高级操作


1.事务处理:
// 一般用于两种关联事务的处理。
const conn = await this.app.mysql.beginTransaction(); try {await conn.insert('user', { 'username': 'hahahh','password':'xxx' }); const row = { id: 8,username: '王麻子'};await conn.update('user', row);await conn.commit();
} catch (err) {await conn.rollback(); // rollback call won't throw errthrow err;
}      

**注意: **

1.查询的表不能是有 ‘-'的表,否则查询失败,

2.eggjs支持post请求中的x-www-form-urlencoded格式,form-data格式 不支持

3.编码规范,注意在使用query方法前,对sql语句用unescape进行解码。

4.where, set , values 中的参数值需要是使用引号包含

**5.where 中的参数用and连接,set中的参数用逗号’,'连接 , values中的参数逗号‘,’连接,并用括号“()”包裹 **

6.目前eggjs的mysql中query方法,使用js还是无法生成符合这个方法的日期格式数据。

mysql语句专用的日期格式的 SQL函数: current_timestamp() , 获取当前时间,不需要引号包裹

7. 插入数据的日期格式就是字符串,使用SQL函数没有效果

// 保存到数据库的时间
// (1.mysql语句专用的日期格式的 SQL函数:
current_timestamp()                  // 获取当前时间,不需要引号包裹
// (2.获取当前日期的格式化数据
const mysqlDateType = (isvalues=false)=>{// return (new Date()).toLocaleString()// 下午会出现异常.replace(/[\上\午\下\午]+?/gi,'')if(isvalues){// 插入数据的日期格式(自定义的格式化日期函数)return DateFormat(new Date(),'YYYY-MM-DD HH:mm:ss');   }else{ return 'current_timestamp()';                     // 获取当前日期的SQL函数}
}

3.封装Models模型层

egg-mysql详情

-------- controller控制器中 // 初始化,获取服务层的路径  AdminDataindex = this.ctx.service.admin.dataindexHomeDataindex = this.ctx.service.home.dataindex -------- services服务层中// 执行函数,返回一个模型对象const User = require("service/models/User")()// 1.高级获取所有的字段// sql查询语句, 选择的条件可以是一个数组,但要经过模板字符串转换const sql = `select ${[...User.fillable,...User.hidden]} from ${User.table}`// 获取单独的字段const sql = `select ${User.fillable} from ${User.table}`// 2.更新部分字段const sql = `update user_msg set sex='女',token='${token}' where id='${user.id}'`const res = await ctx.app.mysql.query(sql)// 3.插入新数据 // 插入默认排序数据 const sql = `insert into user_msg values(NULL,"11","222")`// 插入部分数据const sql = `insert into user_msg(id,username,token) values(NULL,"11","222")`const res = await ctx.app.mysql.query(sql)// 数据 + 模板字符串,可以生成元组数据,//(即数据展开状态,但模板字符串是不支持直接使用扩展运算符,将数组展开)const sql =  `insert into ${table}(${keys}) values(${t_values})`; ------ User.js模型层中'use strict';function User(){return { // 使用的表table : 'user_msg',// 主键primaryKey : 'id', // 排除的字段hidden : ['password','token'] ,// 可使用的字段fillable :['username', 'sex','address', 'registerTime','updateTime','deleteTime','isDelete',]}}module.exports = User;

4.get,post请求方法

(1.get方法

控制器中编写方法

  // 自由传参模式, query// 则可以选择性添加参数 async getGirls() {const { ctx } = this;ctx.body = ctx.query.username;}// 严格传参模式  parmas// 则必需添加相应的参数async getGirls2(){const {ctx} = this; ctx.body = ctx.params.name}

设置路由

  router.get('/getGirls/', controller.minicat.getGirls); // 添加路由router.get('/getGirls2/:name', controller.minicat.getGirls2); // 添加路由

请求方式

http://127.0.0.1:7001/getGirls?username=111http://127.0.0.1:7001/getGirls2/20

(2.post方法

CSRF的全名为 Cross-site request forgery, 它的中文名为 伪造跨站请求。

注意:一定要x-www-form-urlencoded方式,form-data格式不可以

  1. 使用Rest-client插件 , 在项目下添加一个 test.http
POST http://127.0.0.1:7001/add
Content-Type: application/x-www-form-urlencodedname = minicat或者使用JSon格式,注意空行
{"name":"hello"
}
  1. 关闭csrf 安全策略

    在config/config.default.js 下添加 以下配置项, 然后点击

  2. 接收post请求参数 和路由配置

  // POSt请求async add(){const {ctx} = this;let  data = ctx.request.body        // 获取数据包中的参数ctx.body = { satus:200,  data:data }}
 // post方法router.post('/add', controller.minicat.add); 

9.模块的使用

1.egg-mysql数据库

安装egg-mysql数据库

yarn add egg-mysql

添加插件配置

在config/plugins.js添加以下内容,

/* egg-mysql */
exports.mysql = {enable:true,package:'egg-mysql'
}

在config/config.default.js 添加以下内容

 /* 安装mysql插件 */config.mysql = {app:true,      // 是否挂载到app下agent:false,   // 是否挂载到代理下client:{host:'127.0.0.1',   // 主机地址port:'3306',user:'root',password:'xxxx',database:'test-egg'   // 数据库名称}}

然后创建 utf8mb4 编码格式的数据库 , 名称为test-egg, 建立grils的表

const res = await app.mysql.insert('girls',data);           // 添加
const res= await app.mysql.select('girls',option)           // 查询 ,会获取所有数据
const res= await app.mysql.update('girls',data,option)      // 更新
const res= await app.mysql.delete('girls',data)             // 删除
const res = app.mysql.query('select * from girls where id= ?',[params.id])   // 执行sql语句

注意:

  • service服务中的数据库操作,必须使用异常捕获,以保证程序的正常运行。

  • 它们也都是在严格环境下,使用CommonJS 引入包的形式开发。

    ‘use strict’; require (“egg”)

  • 自定义创建的类是从egg 包中继承相应 类, 并把类中的方法全部暴露。 module.exports = XXXService

  • service和controller中,类里面的方法都是异步方法。即使用async修饰方法, await异步等待。

控制器从service获取数据

注意: 实际上表单上的数据没有Number数字类型,都是字符串。因此,使用字符串查询对应的数据,也可以得到结果。

const params = {id:3 , name:'小白', age:20, skill: '唱歌'}
const res = await ctx.service.testdb.getGirl({id:ctx.query.id}  )
const res = await ctx.service.testdb.addGril(params)
const res = await ctx.service.testdb.delGril({id:ctx.query.id}  )      

2.egg-cors跨域

安装模块

yarn add egg-cors

配置信息 在config/plugins.js添加以下内容

exports.cors = {enable:true,package:'egg-cors'
}

在 config/config.default.js 添加以下内容

/* cors跨域 */
config.security = {csrf: {enable: true},domainWhiteList: [ 'http://localhost:3000','http://localhost:3001' ]  // 白名单
};
config.cors = {origin: ctx => ctx.get('origin'), //这种方式是允许所有的IP+端口跨域访问// origin: 'http://localhost:3000',  // credentials: true,  // 开启认证allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS'
};

3.egg-jwt身份认证

安装模块

yarn add egg-jwt

配置信息 在config/plugins.js添加以下内容

exports.cors = {enable:true,package:'egg-jwt'
}

在 config/config.default.js 添加以下内容

 /*jwt认证*/config.jwt = {secret: 'AB12XY',  // 不能设置为123456enable: true, // default is falsematch: '/jwt', // optionalexpiresIn: '24h',}

middleware中间件使用方法:

'use strict';module.exports = () => {return async function jwtErr(ctx, next) {const headerStr = ctx.request.header.access_token;if (headerStr) {try {console.log(headerStr,ctx.app.config.jwt.secret)const options = {secret: ctx.app.config.jwt.secret}// 解码token,需传加密时的 secretconst decode = await ctx.app.jwt.verify(headerStr, ctx.app.config.jwt.secret);ctx.state.user = decode; // 信息存一下,这步很重要,业务里要用await next();} catch (error) {ctx.status = 401;// 翻译错误码let message = error.message;ctx.body = {code: -1,msg: message,};return;}} else {ctx.status = 401;ctx.body = {code: -1,msg: '没有token,无法访问',};return;}};
};

controller控制器中使用方法:

  // 登录帐号,(返回token)async login() {const {ctx, app} = this;const req = ctx.request.body;// 空值验证const errArr = app.validator.validate({username: 'string',password: 'string',}, req);if (errArr) {ctx.body = { error:JSON.stringify(errArr), code:403   }return;}let responseData = null;try {const params = {where:{username:req.username,password:req.password,}}// 查询是否存在当前用户const data =await app.mysql.select('user-msg',params);console.log(data,data.length,data.length > 0)if (data.length > 0) {const user = data[0];const {jwt: {secret, expiresIn}} = app.config;// 需传 secret 过期时间const token = app.jwt.sign({data:{username: user.username,password: user.password,},}, secret, {expiresIn,});// 更新tokenconst params = {  id:user.id, token:token }const res = await app.mysql.update('user-msg',params)responseData = {  msg:'登录成功!',  token:token, userinfo:user, code:200 }} else {responseData = { msg:'登录失败', error:'用户名或密码不正确',  code:403 }}} catch (err) {responseData = { msg:"出现异常!",  error:err, code:500  }}ctx.body = responseData}

4.egg-validate 验证器

安装模块

yarn add egg-validate

配置信息 在config/plugins.js添加以下内容

exports.cors = {enable:true,package:'egg-validate'
}

在 config/config.default.js 添加以下内容

  /**验证参数 */exports.validate = {// convert: false,// validateRoot: false,};

controller中验证使用

// 空值验证
const errArr = app.validator.validate({username: 'string',password: 'string',
}, req);
if (errArr) {ctx.body = { error:JSON.stringify(errArr), code:403   }return;
}

5.bcryptjs密码加密

安装模块

yarn add bcryptjs

配置信息 在config/plugins.js添加以下内容

exports.cors = {enable:true,package:'bcryptjs'
}

使用方法

引入模块
var bcrypt = require("bcryptjs");
var key = bcrypt.genSaltSync(10);  // 设置加密密钥var hashPwd = bcrypt.hashSync("123",key)
var res = bcrypt.compareSync("123",hashPwd)   // true

6.file文件保存(框架)

egg接收文件上传_魔笛Love的博客-CSDN博客_ctx.multipart()

首先在config.default.js中配置file文件接收设置,注意在assets下创建一个temp用户保存临时图片的文件夹

  /* file文件接收配置 */config.multipart = { mode: 'file',tmpdir:  './app/public/assets/temp',cleanSchedule: {cron: '0 0 4 * * *',},fileSize: '100mb',whitelist() {return true;}, }; 

接收上传的文件

  async avatarUpdate() {const {ctx, app} = this;const req = ctx.request.files; // 临时文件的位置let res = utils.saveFile(req[0].filepath)  let responseData = null;if(res)responseData = { msg:'提交成功!',data:res,  code:200 } else responseData = { msg:'提交失败!',  code:403 } ctx.body = responseData}

保存文件的模块


// (9.保存file文件
const saveFile = (file)=>{try{ let date = Date.now() let last = Math.floor(Math.random()*(99999-10000+1)+10000)let filename = date+'.'+last+'.jpg'let dirname = `/assets/file/${filename}` // 注意把临时文件的“\”反斜杠转换成"/"下斜杠let fileData = file.replace(/\\/gi,'\/')// 当前的运行路径是项目的根目录 let saveDirname = 'app/public'+dirnameconsole.log("当前写入的位置是",saveDirname)// // 方法一:分块写入(错误)// var ws = fs.createWriteStream (saveDirname);// ws.on('open', ()=> {//     const blockSize = 128;//     const nbBlocks = Math.ceil(fileData.length / (blockSize));//     for (let i = 0; i < nbBlocks; i += 1) {//         const currentBlock = fileData.slice(blockSize * i,//         Math.min(blockSize * (i + 1), fileData.length),);//         ws.write(currentBlock);//     } //     ws.end();// });  // ws.on('error',(err)=>{console.log(err);reject(null);})// ws.on('finish',()=>{resolve(dirname)})// // 方法二:边读边写 (读写只能读一半)// var rs = fs.createReadStream (fileData );              //创建一个可读流// var ws =fs.createWriteStream( saveDirname); // //如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件,data事件绑定完毕,它会自动开始读取数据//     rs.once ( "open" ,function () {});//     rs. once ( "close" , function (){});//     ws.once ( "open" ,function () {   });//     ws. once ( "close" , function (){//         rs.end();//         ws.end();//     });// rs.on ( "data",function (data) {//     console.log (data) ;//     ws.write(data);// }); // // pipe ( )可以将可读流中的内容,直接输出到可写流中// rs.pipe (ws) ; // 方法三:复制临时文件fs.copyFile(fileData, saveDirname,(err)=>{if(!err){console.log('复制成功');}}) return dirname   }catch(e){console.log("error is",e)return null}
}

7.jsonwebtoken认证(?)

express中使用jsonwebtoken

在service目录下,新建token.js文件:'use strict';const Service = require('egg').Service;
const jwt = require('jsonwebtoken');class TokenService extends Service {async signJwt(_id) {return jwt.sign({data: {_id,},exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 7),}, 'this is secert key, you can replace it!');}async decodeJwt(token) {try {return jwt.verify(token, 'this is secert key, you can replace it!');} catch (err) {return err;}}
}module.exports = TokenService;

10. view视图EJS模板引擎

服务端渲染的好处

  • 对SEO非常友好,单页应用,比如Vue是到客户端才生成的。这种应用对于国内的搜索引擎是没办法爬取的,这样SEO就不会有好的结果。所以如果是官网、新闻网站、博客这些展示类、宣传类的网址,必须要使用服务端渲染技术。
  • 后端渲染是老牌开发模式,渲染性能也是得到一致认可的。在PHP时代,这种后端渲染的技术达到了顶峰。
  • 对前后端分离开发模式的补充,并不是所有的功能都可以实现前后端分离的。特别现在流行的中台系统,有很多一次登录,处处可用的原则。这时候就需要服务端渲染来帮忙。

EJS是什么

EJS是基于javascript的一种历史悠久的模板引擎,生态良好,性能稳定,得到普遍的认可以及广泛的使用。在php时代就已经是达到了顶峰。

安装与配置环境

yarn add egg-view-ejs

配置环境

config/config.default.js下添加以下内容

/*配置ejs */config.view = {mapping:{     /* 匹配文件 */'.html':'ejs'}}config.ejs = {}

config/plugins.js添加

exports.ejs = {enable:true,package:"egg-view-ejs"
}

注意: 首先需要把module.exports 注释, 否则无法暴露这信exports.ejs模块

1.render渲染模板

 // ejs模板引擎 async index1(){const {ctx} = this;await ctx.render('minicat.html',{id:2021,name:'minicat',age:"18",skills:['坚持','努力','沉着']})   // ctx.body = "hello"// render方法返回的是promise对象,所以需要使用异步}

2.<%%>模板语法

模块需要放在app/view下, 创建minicat.html ,并在html插入以下内容

// 1.include组合文件
<% include header.html %>               // 使用include加载文件,文件内只是一个标签// 2.=赋值语句
<h2>现在是                               // 赋值<%=id %><%=name %><%=age %>
</h2>  // 3.for循环遍历 <ul><% for(var i=0;i<skills.length;i++){ %>      // for循环遍历<li><%=skills[i] %></li><% } %>  </ul> // 4.if..else条件判断<%if(islogin){ %<button>已登录</button><%}else { %><button>已登录</button><%}%>// 5.<% %> 变量语句<% var name = "ejs模板" %>

静态css资源

根路径是在app, 只要是默认文件夹,就可以views , controller 直接找到 ”xxx.html 或者 xxx.js, “,

但是静态资源 public目录下,它并非默认文件夹,属于可修改配置。

    /* 修改ejs 配置*/config.ejs = {// delimiter: "$"     // 修改默认分隔符}/* 配置静态文件路径 */config.static = {// prefix:'/assets/'            //  如果使用,则需要把public 改成static}

在public目录下添加 default.css 样式文件

则可以在view模板文件中直接找到


<link rel="stylesheet" href="/public/default.css"> 

11.cookie 和 session存储

1.session的使用

(1.什么是session?

  • session和cookie非常类似 ,但session比cookie更加安全,因此session只允许在服务端进行操作。
  • session用于保存用户登录信息,和用户信息。而cookie用于保存是否登录 。 重要的信息放在session, 公开或者临时信息存在cookie, 。

(2.session的配置

config/config.default.js 下添加

 /* session的配置 */config.session = {key :"PANG_SESS",   // 设置Key的默认值httpOnly:true,      // 设置服务端操作maxAge:1000*60  ,   // 设置最大有效时间renew: true,        // 页面有访问动作自动刷新session, 一般用于是否需要是否登录}

(3.session的操作

注意:与cookie不一样的sessionl默认支持中文, 不需要加密与解密。

ctx.session.username =  "minicat"                 添加session
ctx.session.username = null                       删除session
const se = ctx.session.username                   获取session

2.cookie的使用

(1. 什么是cookie?

HTTP请求是无状态的,但是在开发时,有些情况是需要知道请求的人是谁的。为了解决这个问题,HTTP协议设计了一个特殊的请求头:Cookie。服务端可以通过响应头(set-cookie)将少量数据响应给客户端,浏览器会遵循协议将数据保留,并在下一次请求同一个服务的时候带上。

开发者选项的 ”网络导航项(network)“ 或者 ”应用(Application)“, 可以查看cookie的添加信息

**(2.cookie的配置 **

  1. **ctx.cookies.set() **

    • 第一个是key, 第二个是value, 第三个是配置项 ,这是一个对象的形式

    • maxAge: xxx 以毫秒计数

    • httpOnly:true 服务端专属修改cookie, 客户端不可以通过JS直接使用**document.cookie 获取cookie**,但对开发都选项的控制台无影响。

    • encrypt:true 允许使用中文设置cookie, 注意,使用ctx.cookie.get.获取数值需要添加这个配置项 ctx.cookies.get("user",{encrypt:true})

  2. ctx.cookies.get()

    • 当获取cookie的时候, 如果使用了中文添加到cookie值时,需要解密,否则返回的结果是undefined;

    • 第一个是key,第二是配置项,一般也添加 encrypt: true 来解密。

    • HttpOnly的设置:伪造Cookie来绕过登录是黑客经常使用的一种手段,所以为了安全,Egg.js默认设置只允许服务端来操作Cookie

(3.使用实例

视图页面minicat.html

    <div><button onclick="add()" >增加Cookie</button><button onclick="del()" >删除Cookie</button><button onclick="editor()" >编辑Cookie</button><button onclick="show()" >展示Cookie</button></div><script>function add(){// 原生请求数据的apifetch("/add",{method:'post',headers:{'Content-type':"application/json"}})}function del(){fetch("/del",{method:'post',headers:{'Content-type':"application/json"}})}function editor(){fetch("/editor",{method:'post',headers:{'Content-type':"application/json"}})}function show(){//  fetch类似一个axios,但它是原生的apifetch("/show",{method:'post',headers:{'Content-type':"application/json"}}).then(response=>{return response.json();     // 首先需要返回一个回调,promise对象}).then(data=>{                 // 再去获取响应结果console.log(data);}).catch(err=>{console.log("错误是",err)})}</script>

控制器 minicat.js

 // Cookie的设置async add(){                  // 添加cookieconst ctx = this.ctxctx.cookies.set("user",'加入minicat.com ',{maxAge:1000*60, httpOnly:true,encrypt:true})ctx.body = {status:200,data:"Cookie添加成功"}}async del(){                    // 修改cookieconst ctx = this.ctxctx.cookies.set("user",'')ctx.body = {status:200,data:"Cookie删除成功"}} async editor(){                  // 编辑cookie(略)const ctx = this.ctx....} async show(){                   // 获取cookieconst ctx = this.ctx  ctx.body = {status:200,msg:"Cookie获取成功",data:ctx.cookies.get("user",{encrypt:true      // 返回结果前进行解密})}}

3.fetch的使用

1.什么是fetch?

  • fetch是浏览器提供的原生AJax接口

  • 由于原来 的XMLHttpRequest不符合分离原则,基于事件模型处理异步请求逐渐淘汰。

    Promise的处理具有更强大的优势。

//  fetch类似一个axios,但它是原生的apifetch("/show",{method:'post',headers:{'Content-type':"application/json"}}).then(response=>{return response.json();     // 首先需要返回一个回调,promise对象}).then(data=>{                 // 再去获取响应结果console.log(data);}).catch(err=>{console.log("错误是",err)})

2.原生的ajax

var xhttp = new XMLHttpRequest();           // 创建xml对象
xhttp.onreadystatechange = function() {     // 设置响应函数if (this.readyState === 4 && this.status === 200) {console.log(this.responseText);}
};
xhr.onload = function() {console.log(xhr.response);
};
xhr.onerror = function() {console.log("Oops, error");
};
xhttp.open("GET", "/", true);              // 准备发送请求
xhttp.send();                              // 响应成功后,会触发相应的函数,执行回调函数

12. extend 扩展方法

官方要求扩展必须保存在 app/extend 文件夹下, extend是新创建的文件夹,


application   全局应用对象                              app对象            this.app
context       请求上下文                                ctx对象            this.ctx
request       请求级别的对象,提供了请求相关的属性和方法     ctx.request对象  this.ctx.request
response      请求级别的对象,提供了相应相关的属性和方法     ctx.response对象 this.ctx.response
helper        帮助函数                                 ctx.helper对象   this.ctx.helper

1.创建application扩展

1.创建application.js文件, 并添加以下内容

function getTime(){let  now = new Date();let year = now.getFullYear(); //得到年份let  month = now.getMonth()+1;//得到月份let date = now.getDate();//得到日期let  hour= now.getHours();//得到小时数let minute= now.getMinutes();//得到分钟数let second= now.getSeconds();//得到秒数let nowTime = year+'年'+month+'月'+date+'日 '+hour+':'+minute+':'+second;return nowTime;
}module.exports = {     // 暴露模块// 方法的扩展     app.currentTime()已经成为app下的全局方法currentTime(){     const current = getTime();return current;},// 属性的扩展  app.timeProp  已经成为app下的全局属性、// 加入get,就会默认是一个属性,可以直接以属性的形式在`controller`方法里进行调用。get timeProp(){     return getTime();}// 属性的扩展 app.tokenset token(t){     let t = 'token';}
}

2.控制器调用扩展

async getData(){          const {ctx , app} = this   let data = {nowtime:app.currentTime(),   // 调用自定义扩展的全局方法property:app.timeProp        // 调用自定义扩展的全局属性property2:app.get("timeProp")// 调用自定义扩展的全局属性} app.token = "minicat.com";           //  获取属性, 也可以扩展为属性app.set("token","minicat.com")       //  设置属性, 也可以扩展为属性}

2. 创建context 扩展

1.创建一个context.js文件, 填写以下内容

module.exports = {// 作用: 将get方法的自由传参query和 post请求传参合并,可以自定义获取参数中的数据// 扩展ctx的方法myquery(key){                        // 方法扩展const method = this.request.methodif(method == 'GET'){return key ? this.query[key] : this.query;}else{return key ? this.request.body[key] : this.request.body;}}
}

2.控制器调用扩展

  // 2.contextasync newContext(){const {ctx} = this ;  //   1.get请求//  自由传参   ctx.query.username        为空//  严格传参    ctx.params.name          不允许为空//   2.post请求//  获取数据包      ctx.request.body.name   为空const params = ctx.myquery()  // 默认为{},  使用扩展方法}

3. 创建request扩展

1.创建一个context.js文件, 填写以下内容

module.exports = {// 作用:获取请求头上的数据, 一般用户获取 请求头上的token  // 扩展ctx.request属性get token() {    return this.get("token");},
};

2.控制器调用扩展

  // 3.requestasync newRequest(){const {ctx} = this;const token = ctx.request.token;          //  自定义扩展属性1 const token = ctx.request.get("token");   //  自定义扩展属性2, const method = ctx.request.method;         // 获取默认方法ctx.body = {status:200,body:token,method:method}}

4.创建response扩展

1.创建一个response.js文件, 填写以下内容

module.exports={// 作用: response 可以设置响应token // 扩展属性set token(token){    // 设置响应tokenthis.set('token',token)}};

2.控制器调用扩展

async newResponse(){const {ctx} = this;ctx.response.token = "minicat.com";           // 自定义扩展属性1ctx.response.set("token","minicat.com")       // 自定义扩展属性2ctx.body = { status:200,  msg:'成功访问' }}

5.创建helper扩展

1.创建一个helper.js文件, 填写以下内容

module.exports = {base64Encode(str = '' ){return new Buffer(str).toString('base64');} }

2.控制器调用扩展

async newResponse(){const {ctx} = this; const testBase64 = ctx.helper.base64Encode('jspang.com')  // 扩展方法,方法加密ctx.body = testBase64
}

6.ctx常用的扩展方法

1.请求类的方法
ctx.request                       获取请求的数据ctx.request.header.xxx              获取请求头上的数据2.响应类的方法
ctx.response                       获取请求的数据3.中间件类的方法
ctx.app.mysql.xxx方法                执行数据库语句(异步方法)ctx.app.jwt.xxx 方法                 执行安装的jwt中间件中的方法4.数据库类的方法
ctx.service.home. xxx               获取service服务层文件夹下的文件,调用方法

7.config相关配置项

任何在config.webpack.js中添加的配置项config.xxx=  {   xxx :xxx }都能在所有页面中访问到
const abc = app.config.xxx;或者使用相关的方法
app.xxx(xxxx)

13.项目布署

1.引入模块

使用commonJS语法,只能通过 require 引入包

const qs = require("querystring")

自定义模块

module.exports = {// 音乐列表music_box:"music_box"
}// 引入模块
const table = require("../contant.js")
// 使用模块
table.music_box
  1. 在nodejs中,一个包就相当于一个模块,即使暴露了文件内的模块,依旧还有文件这层。这与 es6不同,并且commonjs不支持es6语法引入模块。

  2. es6不用写后缀,但nodejs自定义的模块,就必需写后缀名, 这很大程度上讲,commonjs采用的是严格格式,对引入的文件格式要求比较高

2.项目根目录

不同的语言,取别名的方法有很大的差异, 当前模块适用于多数的webpack打包类型的框架。

安装模块: yarn add module-alias

(1.简单通用方法


----package.json中配置路径,添加下方内容"_moduleAliases": {"@root": ".","@app": "app"},---全局注册取别名模块, 内容添加以下内容(一般是项目的总配置文件)module.exports = appInfo => {..../* 项目取别名 */require('module-alias/register')}---控制器中使用const func = require("@app/public/utils/func.js")

(2.标准建议方法

修改package.json, 对项目的规范性开发处理的很不好,很容易导致重要文件被不经意间修改,所以建议新建立单独的文件,用于填写重命名规则。


---- 在config项目下添加 config-alias.js文件, 添加以下内容const moduleAlias = require('module-alias')// 路径取别名, 当前路径不是根路径,必需查找到上一级“..”moduleAlias.addAliases({'@root'  : __dirname+"/..",'@app': __dirname + '/../app', })---- 全局注册取别名模块, 内容添加以下内容module.exports = appInfo => {..../* 项目取别名 */require('./config-alias.js')       // 新添加的自定义文件require('module-alias/register')}---- 控制器使用const func = require("@app/public/utils/func.js")

3.项目布署


yarn dev        开发者模式运行
yarn start      生产模式运行
  • 使用yarn start会导致内存占用过高, 对数据库的依赖很大,占用cpu也非常多
  • 所以建议使用 yarn dev 开启 egg.js 项目,再考虑使用 mysql的缓存策略

4.学习总结

1.文件路径引用问题

app是根目录, config是插件配置目录, test是测试目录

关联链接: 技术胖-Egg.js快速入门 200分钟掌握企业级框架开发和应用

  • **controller**是官方创建目录, 一般被 路由调用 。

    (eg:controller.minicat.index)

  • **public**是官方创建目录 , 一般保存css,js等静态文件,但被view内的文件引用,需要采用根目录的形式。 (eg: /public/css/index.css

  • router.js 是官方创建文件, 一般是定义路由规则,以及与控制器、中间件的关系 。

    (eg: router.get("/index",controller.minicat.index), 使用 app.middleware.counter() )

  • service 是自定义官方建议目录, 一般被控制器调用,通过ctx 执行上下文获取路径和函数信息

    (eg: ctx.service.testdb.addGirl(params))

  • view是自定义官方建议目录, 一般由控制调用,用于返回一个html页面。 这个页面使用EJS模板引擎,将模板语法加入到html页面中。

    (eg: ctx.render('minicat.html',{ id:2021, name:'minicat' }

  • middleware是自定义官方建议目录, 一般由内部依赖的属性决定, 因为使用的是箭头函数,所以可以被整体引入和调用依赖属性 使用。

    (eg: ctx.session.counter

  • extend是自定义官方建议目录, 根据扩展目录, 只可以 被相应的对象调用。

    (eg: app.currentTime() 和 app.timeProp )

  • schedule是自定义官方建议目录, 重启服务,自动调用。

2.学习总结:

egg.js是一门具有弹性的框架,可以根据建设网站需要,自由选择是否使用内置配置以及使用其它扩展插件。丰富的生态圈,强大的性能值得我们去学习。

因为采用的是渐近式开发,它的体积虽然比其它node.js框架更加庞大,但它是以轻量化的形式面向开发者使用。基于内置多进程管理,对于系统的处理事件效率和运行的稳定性都是极大的提升。

web前端学习(四):基于koa的EggJs框架,优雅而又完美的Nodejs框架相关推荐

  1. WEB前端学习四 js什么是原始类型

    原始值与引用值 在ECMAScript 中,变量可以存放两种类型的值,即原始值和引用值. 原始值(primitive value)是存放在栈(stack)中的简单数据字段,也就是说,它们的值直接存储在 ...

  2. html css 前端实现消息提醒数_自学的福音,web前端学习全套视频教程+最新学习思维导图都在这里...

    1.产品经理.这些是负责策划应用程序的一群人.他们会想出很多新鲜的.奇怪的.甚至是不可能实现的应用.一般来说,产品经理都追求丰富的功能. 2.UI设计师.这些人负责应用程序的视觉设计和交互模拟. 3. ...

  3. 零基础web前端学习路线

    很多同学想学习WEB前端开发,虽然互联网有很多的教程.网站.书籍,可是却又不知从何开始如何选取. 前端开发入门学习有:HTML.CSS.JavaScript(简称JS)这三个部分.所以在学习之前我们需 ...

  4. 2021年web前端开发视频教程,自学web前端开发技术,全套web前端学习路线笔记

    2021年web前端开发视频教程,自学web前端开发技术,全套web前端学习路线笔记 [导读]:初学web前端的小伙伴经常会遇到的问题,1.没方法 2.没资源 3.没经验,不知道从何开始 ,代码哥(D ...

  5. web前端工程师学习路线指南,完整Web前端学习路线图

    有人说:只要有恒心,铁杵磨成针.这不对,学习重在兴趣,而不在恒心.当你通宵达旦的玩游戏,捧着自己喜爱的名著谈天说地时,不是因为有恒心,而是因为兴趣.只有不感兴趣的东西,才需要恒心的妥协. 所以请抛弃恒 ...

  6. html表单的课后心得体会,web前端学习心得体会范文

    <web前端学习心得体会范文>由会员分享,可在线阅读,更多相关<web前端学习心得体会范文(2页珍藏版)>请在装配图网上搜索. 1.web前端学习心得体会范文web前端学习心得 ...

  7. web 前端学习线路图

    web 前端学习线路图 一.HTML 教程 HTML教程 HTML简介 HTML编辑器 HTML基础 HTML元素 HTML属性 HTML标题 HTML段落 HTML样式 HTML格式化 HTML引用 ...

  8. 2019年web前端学习路线图大纲及学习方法,哎呦不错哦

    Web前端是一个入行门槛较低的开发技术,但更是近几年热门的职业,web前端不仅薪资高发展前景好,是很多年轻人向往的一个职业,想学习web前端,那么你得找到好的学习方法,以下就给大家分享一份适合新手小白 ...

  9. 零基础想要学习前端,却无从下手?其实你就差一套这样的web前端学习路线

    优秀的前端工程师无论在深度和广度上都得有自己的一套清晰透明的知识体系,同时更应该具备快速学习的能力. WEB前端工程师除了需要掌握基本的前端的开发技能外,当然,这里的基本技能说的比较宽泛,大致包括HT ...

最新文章

  1. 菲波那切数列php实现,php实现菲波那切数列和杨辉三角
  2. 企消互动广告:网络时代广告活动的创新形式——兼谈杜丽反败为胜对企业的启示...
  3. LC_ALL=C的含义
  4. ARM64的启动过程之(三):为打开MMU而进行的CPU初始化
  5. 今日arXiv精选 | 11篇ICCV 2021最新论文
  6. android r.java 原理,深入理解Android消息处理系统原理
  7. 切换器黑屏_景阳华泰科技高清无缝矩阵切换器高端视频会议运用
  8. 洛谷 P1824 进击的奶牛 【二分答案】(求最大的最小值)
  9. SystemUI之状态栏notification icon加载流程
  10. while语句的使用
  11. java 执行ssis包_在SSIS包中使用CHECKPOINT重新启动包执行
  12. android+创意方案,有创意≠购买欲 - 10款失败的 Android 创意产品
  13. (HDOJ2039)三角形
  14. 如何在iPhone或Mac上自定义共享菜单?
  15. 编程c 语言怎么表示倍数,C语言里怎么表示是3的倍数
  16. CPU使用率100%,如何解决
  17. html轮播图片在线制作,如何制作图片轮播?轮播图在线制作技巧
  18. c语言假币问题的编程,假币问题 (C语言代码)
  19. vim ctrl + s 终端假死?
  20. CDS软件语音测试,cds测试软件

热门文章

  1. thinkphp更新mysql数据库表_ThinkPHP 创建新表、创建数据库讲解
  2. 有史以来,最牛的一段代码......
  3. idea中使用log4j(打开、关闭日志方法)
  4. 最便捷的注册谷歌浏览器账号的方式
  5. 驱动LSM6DS3TR-C实现高效运动检测与数据采集(4)----上报匿名上位机实现可视化
  6. 计算机办公文档制作,办公室“大秘”教你:如何精确制作“红头文件”的“版记”?-红头文件格式...
  7. TeamViewer以科技创新连接世界
  8. 广东省新型数据中心发展白皮书
  9. UFT12无限期试用
  10. Python 下载文件获取文件名request.get(...,stream=True)