Prerequires
- familiar with
HTML CSS js promise node.js npm - if you don't familiar with Vue site or egg site you can see the official docs then can come back to read this article later
- the most important thing is:
try it by yourself !
What you will lean in this article :
- build a frontend ui with vue-cli vue vuex vue-router
- build a backend server with egg.js and cors plugin template engine plugin (like egg-view-nunjucks) and sequelize(MySQL ORM) plugin
- handle CURD with different categories like(users articles videos etc)
- add egg router.resoure to simplify the router path
- serve a static file in egg
- deploy a backend server(todo later)
- handle cors
- send axios request in frontend
- build a backend cms with elementUI : admin can see update modify some resources in this system.
- build a pc/web page to show a demo website,can show articles videos user can also login logout etc
What is egg.js
Egg is born for building enterprise application and framework, we hope Egg will give birth to more application framework to help developers and developing teams reduce development and maintenance costs.
The fetaures of egg.js
- Provide capability to customizd framework base on Egg
- Highly extensible plugin mechanism
- Built-in cluster
- Based on Koa with high performance
- Stable core framework with high test coverage
- Progressive development
How to init?
npm init egg --type='simple'
if you run this command in a not empty dir . egg will tell you to select a new folder . then you can create a empty folder by yourself.
after install the dependiences. the project structor may like this below :
then cd server && npm run dev to start the server.after doing this . you can start a local server.
when you visit this url http://127.0.0.1:7001/ the egg can return a message for you, the message content is defined in the homeController like this :
//app/controller/home.js
"use strict";
const Controller = require("egg").Controller;
class HomeController extends Controller {
async index() {
const { ctx } = this;
// change the return value
ctx.body = "hello alex from egg";
}
}
serve a static file
you can also create a demo.html file in /app/public folder. then you can see the file content when you enter this url in the browser http://127.0.0.1:7001/public/demo.html
router
- MVC (View --- Model --- Controller)
- View : show data
- Model: handle datas
- Controller : handle user's input
handle api service
add a new Controller then add the controller to router with a path name like/student. then visit this url http://127.0.0.1:7001/student in browser, will see the response is student the code is show in below:
//app/controller/student.js
"use strict";
const Controller = require("egg").Controller;
class StudentController extends Controller {
async index() {
const { ctx } = this;
ctx.body = "student";
}
}
module.exports = StudentController;
//app/router.js
"use strict";
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
router.get("/", controller.home.index);
//add this line below
router.get("/student", controller.student.index);
};
we can konw that controller can also used in router.js to handle different path user visited in the frontend.
get query params and post datas in backend
- get
GETmethod query params usethis.ctx.request.query
class StudentController extends Controller {
async index() {
const { ctx } = this;
console.log(ctx.request.query);
ctx.body = ctx.request.query;
}
}
//in browser you can visit this url to test http://127.0.0.1:7001/student?age=18&sex=man
//the response is
// http://127.0.0.1:7001/student?age=18&sex=man
{
"age": "18",
"sex": "man"
}
- get
GETmethodidusethis.ctx.params
//add new path in router.js
router.get("/student/:id", controller.student.getId);
//add a new method in controller student.js
async getId() {
const { ctx } = this;
let id = ctx.params.id;
ctx.body = `当前请求路径的id是${id}`;
}
//http://127.0.0.1:7001/student/345
//response is 当前请求路径的id是345
- get
POSTmethod usethis.ctx.request.body
"use strict";
const Controller = require("egg").Controller;
let students = ["alex", "tom", "jony"];
class StudentController extends Controller {
async index() {
const { ctx } = this;
const query = ctx.request.query;
ctx.body = query;
}
async getId() {
const { ctx } = this;
let id = ctx.params.id;
ctx.body = `当前请求路径的id是${id}`;
}
// add this two methods
async createStudentPage() {
const { ctx } = this;
const formHtml = `
<form action="/createStduent" method="post">
<input type="text" />
<input type="submit" value="提交">
</form>
`;
ctx.body = formHtml;
}
async addStudent() {
let student = this.ctx.request.body;
this.ctx.body = student;
}
}
module.exports = StudentController;
//router.js
"use strict";
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
router.get("/", controller.home.index);
router.get("/student", controller.student.index);
router.get("/student/:id", controller.student.getId);
//add this two routes
router.get("/createStduent", controller.student.createStudentPage);
router.post("/createStduent", controller.student.addStudent);
};
after we sumit the form we will encounter a error : ForbiddenError in /createStduent invalid csrf token because
egg has a csrf validation for the post mehthod . so we need to edit the config to omit the validation now.
Here we do the config temporarily in config/config.default.js for an example:
config.security = {
csrf: {
enable: false
}
};
egg RESTful Style URL Definition to aviod define multi routes for one resource
before use router.resources:
//app/router.js
//students router logic
router.get("/student", controller.student.index);
router.get("/student/:id", controller.student.getId);
router.get("/createStduent", controller.student.createStudentPage);
router.post("/createStduent", controller.student.addStudent);
after use router.resources : we can handle this just by add one line!
//app/router.js
// router.get("/student", controller.student.index);
// router.get("/student/:id", controller.student.getId);
// router.get("/createStduent", controller.student.createStudentPage);
// router.post("/createStduent", controller.student.addStudent);
router.resources("students", "/students", controller.student);
so we need to change to student controller to match the routers
student CURD demo :
http://127.0.0.1:7002/studentsget students listhttp://127.0.0.1:7002/students/newget add students form page
"use strict";
const Controller = require("egg").Controller;
//define post datas here path : http://127.0.0.1:7002/students
let students = ["alex", "tom", "jony"];
class StudentController extends Controller {
//get method
async index() {
const { ctx } = this;
ctx.body = students;
}
//show add student post page path : http://127.0.0.1:7002/students/new
async new() {
const { ctx } = this;
const formHtml = `
<form action="/students" method="post">
<input type="text" name="studentName" />
<input type="submit" value="提交">
</form>
`;
ctx.body = formHtml;
}
//add a new student post method
// enter the url will send a get request
// click reload will repost
async create() {
let student = this.ctx.request.body;
students.push(student.studentName);
// remote this step
// this.ctx.body = "add stducent success";
//redirect to student list
this.ctx.redirect("/students");
}
// /students/:id delete method
async destory() {}
//update put method /students/:id
async update() {}
}
module.exports = StudentController;
plugins
template engine npm i egg-view-nunjucks --save
npm i egg-view-nunjucks --save
then enable it
// config/plugin.js
'use strict';
/** @type Egg.EggPlugin */
module.exports = {
// had enabled by egg
// static: {
// enable: true,
// }
nunjucks = {
enable: true,
package: 'egg-view-nunjucks'
}
};
//and also config
// config/config.default.js
exports.keys = <YOUR_SECURITY_COOKE_KEYS>;
// add view's configurations
exports.view = {
defaultViewEngine: 'nunjucks',
mapping: {
'.tpl': 'nunjucks',
},
};
then change the home controller
"use strict";
const Controller = require("egg").Controller;
class HomeController extends Controller {
async index() {
const { ctx } = this;
// ctx.body = "hello alex from egg";
//techs is the data the index.html will show
await ctx.render("index", { techs: ["js", "html", "css"] });
}
}
module.exports = HomeController;
crate a new folder `view` in app and a index.html and for loop the datas pass in the home controller
//app/view/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<h1>techs :</h1>
<ul>
{% for tech in techs %}
<li>{{ tech }}</li>
{% endfor %}
</ul>
</body>
</html>
//more detail can reference egg hacknews demo https://sourcegraph.com/github.com/eggjs/examples@master/-/blob/hackernews/app/controller/news.js
the result is http://127.0.0.1:7001/ the router is / to homeController:
//app/router.js
"use strict";
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
// / => home controller
router.get("/", controller.home.index);
router.resources("students", "/students", controller.student);
};
//home.controller app/controller/home.js
"use strict";
const Controller = require("egg").Controller;
class HomeController extends Controller {
async index() {
const { ctx } = this;
// ctx.body = "hello alex from egg";
await ctx.render("index", { techs: ["js", "html", "css"] });
}
}
module.exports = HomeController;
use template result in frontend :
cors
when front end (8080 port) send request to backend (7001) the browser will have a error
- vue cli create a new project call
clintcdclinetnpm run servethen change theapp.vuethe project folder looks like this :
/client (vue.js forentend folder) in 8080 port
/server (egg.js backend server floder) in 7001 port
- add axios in client folder
npm i axios -S
// client/src/app.vue
<template>
<h1>{{message}}</h1>
</template>
<script>
import axios from "axios";
export default {
name: "App",
data() {
return {
message: "hello vue"
};
},
created() {
axios.get("http://127.0.0.1:7001/test").then(
res => {
console.log(res);
this.message = res.data;
},
err => {
console.log(err);
}
);
}
};
</script>
when we visit this url in frontend we will encounter this error Access to XMLHttpRequest at 'http://127.0.0.1:7001/test' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
so we need to config the cors in our backend
how to enable cors in egg.js ?
cd server/ && npm i -S egg-cors
//plugin.js
cors: {
enable: true,
package: "egg-cors"
}
//config.default.js
config.cors = {
//allow all the origin
origin: "*",
allowMethods: "GET,HEAD,PUT,POST,DELETE,PATCH"
};
after configing this we vist the frontend again will see the success result :
build a CURD demo with egg and vue
frontend vue app.vue
<template>
<div>
<h1>{{message}}</h1>
<ul>
<li v-for="(stu, index) of students" :key="index">{{stu}}</li>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
data() {
return {
message: "hello vue",
students: []
};
},
created() {
//test cors
// axios.get("http://127.0.0.1:7001/test").then(
// res => {
// console.log(res);
// this.message = res.data;
// },
// err => {
// console.log(err);
// }
// );
//get students
axios.get("http://127.0.0.1:7001/students").then(
res => {
console.log(res);
//why use res.data because we can check the result data in chrome devtools
this.students = res.data;
},
err => {
console.log(err);
}
);
},
methods: {}
};
</script>
backend student api
js
the result islocalhost:8080 : the frontend show the backend data successfully
- add post method in frontend
//client/src/app.vue
<template>
<div>
<h1>{{message}}</h1>
<form @submit.prevent="postStudent">
<input type="text" v-model="nowInputedSutdentName" />
<button>add a student name</button>
</form>
<ul>
<li v-for="(stu, index) of students" :key="index">{{stu}}</li>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
data() {
return {
message: "hello vue",
students: [],
nowInputedSutdentName: ""
};
},
created() {
//test cors
// axios.get("http://127.0.0.1:7001/test").then(
// res => {
// console.log(res);
// this.message = res.data;
// },
// err => {
// console.log(err);
// }
// );
//get students
this.getStudent();
},
methods: {
getStudent() {
axios.get("http://127.0.0.1:7001/students").then(
res => {
console.log(res);
this.students = res.data;
},
err => {
console.log(err);
}
);
},
postStudent() {
axios
.post("http://127.0.0.1:7001/students", {
studentName: this.nowInputedSutdentName
})
.then(() => {
//reset this.nowInputedSutdentName
this.nowInputedSutdentName = "";
this.getStudent();
});
}
}
};
</script>
//the backend sutdent api is
"use strict";
const Controller = require("egg").Controller;
//define post datas here
let students = ["alex", "tom", "jony"];
class StudentController extends Controller {
async index() {
const { ctx } = this;
ctx.body = students;
}
//add a new student post method
// enter the url will send a get request
// click reload will repost
// post method to add new student
async create() {
// {studentName : "xxx"}
let student = this.ctx.request.body;
students.push(student.studentName);
// remote this step
// this.ctx.body = "add stducent success";
//redirect to student list
this.ctx.redirect("/students");
}
// /students/:id delete method
async destory() {
let { ctx } = this;
let id = ctx.params.id;
students.splice(id, 1);
ctx.body = "delete success";
}
//show add student post page
async new() {
const { ctx } = this;
const formHtml = `
<form action="/students" method="post">
<input type="text" name="studentName" />
<input type="submit" value="提交">
</form>
`;
ctx.body = formHtml;
}
//update put method /students/:id
async update() {}
// async getId() {
// const { ctx } = this;
// let id = ctx.params.id;
// ctx.body = `当前请求路径的id是${id}`;
// }
// async createStudentPage() {
// const { ctx } = this;
// const formHtml = `
// <form action="/createStduent" method="post">
// <input type="text" name="studentName" />
// <input type="submit" value="提交">
// </form>
// `;
// ctx.body = formHtml;
// }
// async addStudent() {
// let student = this.ctx.request.body;
// console.log(student); //{ studentName: 'wewwewe' }
// students.push(student.studentName);
// // this.ctx.body = "add stducent success";
// //redirect to student list
// this.ctx.redirect("/student");
// }
}
module.exports = StudentController;
the result is after user post a new student name. the list will get the students againg to show the newest datas :
next article we will talk about how to use middleware data persistence service deploy etc
middlewares
data persistence
- use `sequelize`(Object Relational Mapping ORM framework) to interact with mysql. you don't need to write pure sql statement by yourself .
service
deploy
最后
- source code fe backend-egg 觉得有帮助,麻烦给个star!
- 前端关宇
- 微信号: wuguanyu1103 加我备注“掘金” 拉你进每日前端早起学习群,和小伙伴一起玩转前端。
- 感谢阅读