Serverless 实战 —— Serverless + Egg.js 后台管理系统实战
Serverless + Egg.js 后台管理系统实战
作为一名前端开发者,在选择 Nodejs 后端服务框架时,第一时间会想到 Egg.js,不得不说 Egg.js
是一个非常优秀的企业级框架,它的高扩展性和丰富的插件,极大的提高了开发效率。开发者只需要关注业务就好,比如要使用 redis
,引入 egg-redis 插件,然后简单配置就可以了。正因为如此,第一次接触它,我便喜欢上了它,之后也用它开发过不少应用。
有了如此优秀的框架,那么如何将一个 Egg.js
的服务迁移到 Serverless
架构上呢?
背景
我在文章 基于 Serverless Component 的全栈解决方案 中讲述了,如何将一个基于 Vue.js
的前端应用和基于 Express
的后端服务,快速部署到腾讯云上。虽然受到不少开发者的喜爱,但是很多开发者私信问我,这还是一个 Demo
性质的项目而已,有没有更加实用性的解决方案。而且他们实际开发中,很多使用的正是 Egg.js
框架,能不能提供一个 Egg.js
的解决方案?
本文将手把手教你结合 Egg.js
和 Serverless
实现一个后台管理系统。
读完此文你将学到:
- Egg.js 基本使用
- 如何使用 Sequelize ORM 模块进行 Mysql 操作
- 如何使用 Redis
- 如何使用 JWT 进行用户登录验证
- Serverless Framework 的基本使用
- 如何将本地开发好的 Egg.js 应用部署到腾讯云云函数上
- 如何基于云端对象存储快速部署静态网站
Egg.js 入门
初始化 Egg.js 项目:
复制代码
$ mkdir egg-example && cd egg-example
$ npm init egg --type=simple
$ npm i
启动项目:
复制代码
$ npm run dev
然后浏览器访问 http://localhost:7001
,就可以看到亲切的 hi, egg
了。
关于 Egg.js 的框架更多知识,建议阅读 官方文档
准备
对 Egg.js 有了简单了解,接下来我们来初始化我们的后台管理系统,新建一个项目目录 admin-system
:
复制代码
$ mkdir admin-system
将上面创建的 Egg.js 项目复制到 admin-system
目录下,重命名为 backend
。然后将前端模板项目复制到 frontend
文件夹中:
复制代码
$ git clone https://github.com/PanJiaChen/vue-admin-template.git frontend
说明: vue-admin-template 是基于 Vue2.0 的管理系统模板,是一个非常优秀的项目,建议对 Vue.js 感兴趣的开发者可以去学习下,当然如果你对 Vue.js 还不是太了解,这里有个基础入门学习教程 Vuejs 从入门到精通系列文章
之后你的项目目录结构如下:
复制代码
.
├── README.md
├── backend // 创建的 Egg.js 项目
└── frontend // 克隆的 Vue.js 前端项目模板
启动前端项目熟悉下界面:
复制代码
$ cd frontend
$ npm install
$ npm run dev
然后访问 http://localhost:9528
就可以看到登录界面了。
开发后端服务
对于一个后台管理系统服务,我们这里只实现登录鉴权和文章管理功能,剩下的其他功能大同小异,读者可以之后自由补充扩展。
1. 添加 Sequelize 插件
在正式开发之前,我们需要引入数据库插件,这里本人偏向于使用 Sequelize ORM 工具进行数据库操作,正好 Egg.js 提供了 egg-sequelize 插件,于是直接拿来用,需要先安装:
复制代码
$ cd frontend
# 因为需要通过 sequelize 链接 mysql 所以这也同时安装 mysql2 模块
$ npm install egg-sequelize mysql2 --save
然后在 backend/config/plugin.js
中引入该插件:
复制代码
module.exports = {// ....sequelize: {enable: true,package: "egg-sequelize"}// ....
};
在 backend/config/config.default.js
中配置数据库连接参数:
复制代码
// ...
const userConfig = {// ...sequelize: {dialect: "mysql",// 这里也可以通过 .env 文件注入环境变量,然后通过 process.env 获取host: "xxx",port: "xxx",database: "xxx",username: "xxx",password: "xxx"}// ...
};
// ...
2. 添加 JWT 插件
系统将使用 JWT token 方式进行登录鉴权,安装配置参考官方文档,egg-jwt
3. 添加 Redis 插件
系统将使用 redis 来存储和管理用户 token,安装配置参考官方文档,egg-redis
4. 角色 API
定义用户模型,创建 backend/app/model/role.js
文件如下:
复制代码
module.exports = app => {const { STRING, INTEGER, DATE } = app.Sequelize;const Role = app.model.define("role", {id: { type: INTEGER, primaryKey: true, autoIncrement: true },name: STRING(30),created_at: DATE,updated_at: DATE});// 这里定义与 users 表的关系,一个角色可以含有多个用户,外键相关Role.associate = () => {app.model.Role.hasMany(app.model.User, { as: "users" });};return Role;
};
实现 Role 相关服务,创建 backend/app/service/role.js
文件如下:
复制代码
const { Service } = require("egg");class RoleService extends Service {// 获取角色列表async list(options) {const {ctx: { model }} = this;return model.Role.findAndCountAll({...options,order: [["created_at", "desc"],["id", "desc"]]});}// 通过 id 获取角色async find(id) {const {ctx: { model }} = this;const role = await model.Role.findByPk(id);if (!role) {this.ctx.throw(404, "role not found");}return role;}// 创建角色async create(role) {const {ctx: { model }} = this;return model.Role.create(role);}// 更新角色async update({ id, updates }) {const role = await this.ctx.model.Role.findByPk(id);if (!role) {this.ctx.throw(404, "role not found");}return role.update(updates);}// 删除角色async destroy(id) {const role = await this.ctx.model.Role.findByPk(id);if (!role) {this.ctx.throw(404, "role not found");}return role.destroy();}
}module.exports = RoleService;
一个完整的 RESTful API 就该包括以上五个方法,然后实现 RoleController
, 创建 backend/app/controller/role.js
:
复制代码
const { Controller } = require("egg");class RoleController extends Controller {async index() {const { ctx } = this;const { query, service, helper } = ctx;const options = {limit: helper.parseInt(query.limit),offset: helper.parseInt(query.offset)};const data = await service.role.list(options);ctx.body = {code: 0,data: {count: data.count,items: data.rows}};}async show() {const { ctx } = this;const { params, service, helper } = ctx;const id = helper.parseInt(params.id);ctx.body = await service.role.find(id);}async create() {const { ctx } = this;const { service } = ctx;const body = ctx.request.body;const role = await service.role.create(body);ctx.status = 201;ctx.body = role;}async update() {const { ctx } = this;const { params, service, helper } = ctx;const body = ctx.request.body;const id = helper.parseInt(params.id);ctx.body = await service.role.update({id,updates: body});}async destroy() {const { ctx } = this;const { params, service, helper } = ctx;const id = helper.parseInt(params.id);await service.role.destroy(id);ctx.status = 200;}
}module.exports = RoleController;
之后在 backend/app/route.js
路由配置文件中定义 role
的 RESTful API:
复制代码
router.resources("roles", "/roles", controller.role);
通过 router.resources
方法,我们将 roles
这个资源的增删改查接口映射到了 app/controller/roles.js
文件。详细说明参考 官方文档
5. 用户 API
同 Role 一样定义我们的用户 API,这里就不复制粘贴了,可以参考项目实例源码 admin-system。
6. 同步数据库表格
上面只是定义好了 Role
和 User
两个 Schema,那么如何同步到数据库呢?这里先借助 Egg.js 启动的 hooks 来实现,Egg.js 框架提供了统一的入口文件(app.js)进行启动过程自定义,这个文件返回一个 Boot 类,我们可以通过定义 Boot 类中的生命周期方法来执行启动应用过程中的初始化工作。
我们在 backend
目录中创建 app.js
文件,如下:
复制代码
"use strict";class AppBootHook {constructor(app) {this.app = app;}async willReady() {// 这里只能在开发模式下同步数据库表格const isDev = process.env.NODE_ENV === "development";if (isDev) {try {console.log("Start syncing database models...");await this.app.model.sync({ logging: console.log, force: isDev });console.log("Start init database data...");await this.app.model.query("INSERT INTO roles (id, name, created_at, updated_at) VALUES (1, 'admin', '2020-02-04 09:54:25', '2020-02-04 09:54:25'),(2, 'editor', '2020-02-04 09:54:30', '2020-02-04 09:54:30');");await this.app.model.query("INSERT INTO users (id, name, password, age, avatar, introduction, created_at, updated_at, role_id) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 20, 'https://yugasun.com/static/avatar.jpg', 'Fullstack Engineer', '2020-02-04 09:55:23', '2020-02-04 09:55:23', 1);");await this.app.model.query("INSERT INTO posts (id, title, content, created_at, updated_at, user_id) VALUES (2, 'Awesome Egg.js', 'Egg.js is a awesome framework', '2020-02-04 09:57:24', '2020-02-04 09:57:24', 1),(3, 'Awesome Serverless', 'Build web, mobile and IoT applications using Tencent Cloud and API Gateway, Tencent Cloud Functions, and more.', '2020-02-04 10:00:23', '2020-02-04 10:00:23', 1);");console.log("Successfully init database data.");console.log("Successfully sync database models.");} catch (e) {console.log(e);throw new Error("Database migration failed.");}}}
}module.exports = AppBootHook;
通过 willReady
生命周期函数,我们可以执行 this.app.model.sync()
函数来同步数据表,当然这里同时初始化了角色和用户数据记录,用来做为演示用。
注意:这的数据库同步只是本地调试用,如果想要腾讯云的 Mysql 数据库,建议开启远程连接,通过
sequelize db:migrate
实现,而不是每次启动 Egg 应用时同步,示例代码已经完成此功能,参考 Egg Sequelize 文档。 这里本人为了省事,直接开启腾讯云 Mysql 公网连接,然后修改config.default.js
中的sequelize
配置,运行npm run dev
进行开发模式同步。
到这里,我们的用户和角色的 API 都已经定义好了,启动服务 npm run dev
,访问 https://127.0.0.1:7001/users
可以获取所有用户列表了。
7. 用户登录/注销 API
这里登录逻辑比较简单,客户端发送 用户名
和 密码
到 /login
路由,后端通过 login
函数接受,然后从数据库中查询该用户名,同时比对密码是否正确。如果正确则调用 app.jwt.sign()
函数生成 token
,并将 token
存入到 redis
中,同时返回该 token
,之后客户端需要鉴权的请求都会携带 token
,进行鉴权验证。思路很简单,我们就开始实现了。
流程图如下:
首先,在 backend/app/controller/home.js
中新增登录处理 login
方法:
复制代码
class HomeController extends Controller {// ...async login() {const { ctx, app, config } = this;const { service, helper } = ctx;const { username, password } = ctx.request.body;const user = await service.user.findByName(username);if (!user) {ctx.status = 403;ctx.body = {code: 403,message: "Username or password wrong"};} else {if (user.password === helper.encryptPwd(password)) {ctx.status = 200;const token = app.jwt.sign({id: user.id,name: user.name,role: user.role.name,avatar: user.avatar},config.jwt.secret,{expiresIn: "1h"});try {await app.redis.set(`token_${user.id}`, token);ctx.body = {code: 0,message: "Get token success",token};} catch (e) {console.error(e);ctx.body = {code: 500,message: "Server busy, please try again"};}} else {ctx.status = 403;ctx.body = {code: 403,message: "Username or password wrong"};}}}
}
注释:这里有个密码存储逻辑,用户在注册时,密码都是通过
helper
函数encryptPwd()
进行加密的(这里用到最简单的 md5 加密方式,实际开发中建议使用更加高级加密方式),所以在校验密码正确性时,也需要先加密一次。至于如何在 Egg.js 框架中新增helper
函数,只需要在backend/app/extend
文件夹中新增helper.js
文件,然后modole.exports
一个包含该函数的对象就行,参考 Egg 框架扩展文档
然后,在 backend/app/controller/home.js
中新增 userInfo
方法,获取用户信息:
复制代码
async userInfo() {const { ctx } = this;const { user } = ctx.state;ctx.status = 200;ctx.body = {code: 0,data: user,};
}
egg-jwt 插件,在鉴权通过的路由对应 controller 函数中,会将 app.jwt.sign(user, secrete)
加密的用户信息,添加到 ctx.state.user
中,所以 userInfo
函数只需要将它返回就行。
之后,在 backend/app/controller/home.js
中新增 logout
方法:
复制代码
async logout() {const { ctx } = this;ctx.status = 200;ctx.body = {code: 0,message: 'Logout success',};
}
userInfo
和 logout
函数非常简单,重点是路由中间件如何处理。
接下来,我们来定义登录相关路由,修改 backend/app/router.js
文件,新增 /login
, /user-info
, /logout
三个路由:
复制代码
const koajwt = require("koa-jwt2");module.exports = app => {const { router, controller, jwt } = app;router.get("/", controller.home.index);router.post("/login", controller.home.login);router.get("/user-info", jwt, controller.home.userInfo);const isRevokedAsync = function(req, payload) {return new Promise(resolve => {try {const userId = payload.id;const tokenKey = `token_${userId}`;const token = app.redis.get(tokenKey);if (token) {app.redis.del(tokenKey);}resolve(false);} catch (e) {resolve(true);}});};router.post("/logout",koajwt({secret: app.config.jwt.secret,credentialsRequired: false,isRevoked: isRevokedAsync}),controller.home.logout);router.resources("roles", "/roles", controller.role);router.resources("users", "/users", controller.user);router.resources("posts", "/posts", controller.post);
};
Egg.js 框架定义路由时,router.post()
函数可以接受中间件函数,用来处理一些路由相关的特殊逻辑。
比如 /user-info
,路由添加了 app.jwt
作为 JWT 鉴权中间件函数,至于为什么这么用,egg-jwt 插件有明确说明。
这里稍微复杂的是 /logout
路由,因为我们在注销登录时,需要将用户的 token
从 redis
中移除,所以这里借助了 koa-jwt2 的 isRevokded
参数,来进行 token
删除。
后端服务部署
到这里,后端服务的登录和注销逻辑基本完成了。那么如何部署到云函数呢?可以直接使用 tencent-egg 组件,它是专门为 Egg.js 框架打造的 Serverless Component,使用它可以快速将我们的 Egg.js 项目部署到腾讯云云函数上。
1. 准备
我们先创建一个 backend/sls.js
入口文件:
复制代码
const { Application } = require("egg");
const app = new Application();
module.exports = app;
然后修改 backend/config/config.default.js
文件:
复制代码
const config = (exports = {env: "prod", // 推荐云函数的 egg 运行环境变量修改为 prodrundir: "/tmp",logger: {dir: "/tmp"}
});
注释:这里之所有需要修改运行和日志目录,是因为云函数运行时,只有
/tmp
才有写权限。
全局安装 serverless
命令:
复制代码
$ npm install serverless -g
2. 配置 Serverless
在项目根目录下创建 serverless.yml
文件,同时新增 backend
配置:
复制代码
backend:component: "@serverless/tencent-egg"inputs:code: ./backendfunctionName: admin-system# 这里必须指定一个具有操作 mysql 和 redis 的角色,具体角色创建,可访问 https://console.cloud.tencent.com/cam/rolerole: QCS_SCFFullfunctionConf:timeout: 120# 这里的私有网络必须和 mysql、redis 实例一致vpcConfig:vpcId: vpc-xxxsubnetId: subnet-xxxapigatewayConf:protocols:- https
此时你的项目目录结构如下:
复制代码
.
├── README.md // 项目说明文件
├── serverless.yml // serverless yml 配合文件
├── backend // 创建的 Egg.js 项目
└── frontend // 克隆的 Vue.js 前端项目模板
3. 执行部署
执行部署命令:
复制代码
$ serverless --debug
之后控制台需要进行扫码登录验证腾讯云账号,扫码登录就好。等部署成功会发挥如下信息:
复制代码
backend:region: ap-guangzhoufunctionName: admin-systemapiGatewayServiceId: service-f1bhmhk4url: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/
这里输出的 url 就是部署成功的 API 网关接口,可以直接访问测试。
注释:云函数部署时,会自动在腾讯云的 API 网关创建一个服务,同时创建一个 API,通过该 API 就可以触发云函数执行了。
4. 账号配置(可选)
当前默认支持 Serverless cli 扫描二维码登录,如果希望配置持久的环境变量/秘钥信息,也可以在项目根目录创建 .env
文件
在 .env
文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存,密钥可以在 API 密钥管理 中获取或者创建.
复制代码
# .env
TENCENT_SECRET_ID=123
TENCENT_SECRET_KEY=123
5. 文章 API
跟用户 API 类似,只需要复制粘贴上面用户相关模块,修改名称为 posts
, 并修改数据模型就行,这里就不粘贴代码了。
前端开发
本实例直接使用的 vue-admin-template 的前端模板。
我们需要做如下几部分修改:
- 删除接口模拟:更换为真实的后端服务接口
- 修改接口函数:包括用户相关的
frontend/src/api/user.js
和文章相关接口frontend/src/api/post.js
。 - 修改接口工具函数:主要是修改
frontend/src/utils/request.js
文件,包括axios
请求的baseURL
和请求的 header。 - UI 界面修改:主要是新增文章管理页面,包括列表页和新增页。
1. 删除接口模拟
首先删除 frontend/mock
文件夹。然后修改前端入口文件 frontend/src/main.js
:
复制代码
// 1. 引入接口变量文件,这个会依赖 @serverless/tencent-website 组件自动生成
import "./env.js";import Vue from "vue";import "normalize.css/normalize.css";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
import locale from "element-ui/lib/locale/lang/en";
import "@/styles/index.scss";
import App from "./App";
import store from "./store";
import router from "./router";
import "@/icons";
import "@/permission";// 2. 下面这段就是 mock server 引入,删除就好
// if (process.env.NODE_ENV === 'production') {
// const { mockXHR } = require('../mock')
// mockXHR()
// }Vue.use(ElementUI, { locale });
Vue.config.productionTip = false;new Vue({el: "#app",router,store,render: h => h(App)
});
2. 修改接口函数
修改 frontend/src/api/user.js
文件,包括登录、注销、获取用户信息和获取用户列表函数如下:
复制代码
import request from "@/utils/request";// 登录
export function login(data) {return request({url: "/login",method: "post",data});
}// 获取用户信息
export function getInfo(token) {return request({url: "/user-info",method: "get"});
}// 注销登录
export function logout() {return request({url: "/logout",method: "post"});
}// 获取用户列表
export function getList() {return request({url: "/users",method: "get"});
}
新增 frontend/src/api/post.js
文件如下:
复制代码
import request from "@/utils/request";// 获取文章列表
export function getList(params) {return request({url: "/posts",method: "get",params});
}// 创建文章
export function create(data) {return request({url: "/posts",method: "post",data});
}// 删除文章
export function destroy(id) {return request({url: `/posts/${id}`,method: "delete"});
}
3. 修改接口工具函数
因为 @serverless/tencent-website
组件可以定义 env
参数,执行成功后它会在指定 root
目录自动生成 env.js
,然后在 frontend/src/main.js
中引入使用。 它会挂载 env
中定义的接口变量到 window
对象上。比如这生成的 env.js
文件如下:
复制代码
window.env = {};
window.env.apiUrl ="https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/";
根据此文件我们来修改 frontend/src/utils/request.js
文件:
复制代码
import axios from "axios";
import { MessageBox, Message } from "element-ui";
import store from "@/store";
import { getToken } from "@/utils/auth";// 创建 axios 实例
const service = axios.create({// 1. 这里设置为 `env.js` 中的变量 `window.env.apiUrl`baseURL: window.env.apiUrl || "/", // url = base url + request urltimeout: 5000 // request timeout
});// request 注入
service.interceptors.request.use(config => {// 2. 添加鉴权tokenif (store.getters.token) {config.headers["Authorization"] = `Bearer ${getToken()}`;}return config;},error => {console.log(error); // for debugreturn Promise.reject(error);}
);// 请求 response 注入
service.interceptors.response.use(response => {const res = response.data;// 只有请求code为0,才是正常返回,否则需要提示接口错误if (res.code !== 0) {Message({message: res.message || "Error",type: "error",duration: 5 * 1000});if (res.code === 50008 || res.code === 50012 || res.code === 50014) {// to re-loginMessageBox.confirm("You have been logged out, you can cancel to stay on this page, or log in again","Confirm logout",{confirmButtonText: "Re-Login",cancelButtonText: "Cancel",type: "warning"}).then(() => {store.dispatch("user/resetToken").then(() => {location.reload();});});}return Promise.reject(new Error(res.message || "Error"));} else {return res;}},error => {console.log("err" + error);Message({message: error.message,type: "error",duration: 5 * 1000});return Promise.reject(error);}
);export default service;
4. UI 界面修改
关于 UI 界面修改,这里就不做说明了,因为涉及到 Vue.js 的基础使用,如果还不会使用 Vue.js,建议先复制示例代码就好。如果对 Vue.js 感兴趣,可以到 Vue.js 官网 学习。也可以阅读本人的 Vuejs 从入门到精通系列文章,喜欢的话,可以送上您宝贵的 Star (*^▽^*)
这里只需要复制 Demo 源码 的 frontend/router
和 frontend/views
两个文件夹就好。
前端部署
因为前端编译后都是静态文件,我们需要将静态文件上传到腾讯云的 COS(对象存储) 服务,然后开启 COS 的静态网站功能就可以了,这些都不需要你手动操作,使用 @serverless/tencent-website 组件就可以轻松搞定。
1. 修改 Serverless 配置文件
修改项目根目录下 serverless.yml
文件,新增前端相关配置:
复制代码
name: admin-system# 前端配置
frontend:component: "@serverless/tencent-website"inputs:code:src: distroot: frontendenvPath: src # 相对于 root 指定目录,这里实际就是 frontend/srchook: npm run buildenv:# 依赖后端部署成功后生成的 urlapiUrl: ${backend.url}protocol: https# TODO: CDN 配置,请修改!!!hosts:- host: sls-admin.yugasun.com # CDN 加速域名https:certId: abcdedg # 为加速域名在腾讯云平台申请的免费证书 IDhttp2: offhttpsType: 4forceSwitch: -2# 后端配置
backend:component: "@serverless/tencent-egg"inputs:code: ./backendfunctionName: admin-systemrole: QCS_SCFFullfunctionConf:timeout: 120vpcConfig:vpcId: vpc-6n5x55kbsubnetId: subnet-4cvr91jsapigatewayConf:protocols:- https
2. 执行部署
执行部署命令:
复制代码
$ serverless --debug
输出如下成功结果:
复制代码
frontend:url: https://dtnu69vl-470dpfh-1251556596.cos-website.ap-guangzhou.myqcloud.comenv:apiUrl: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/host:- https://sls-admin.yugasun.com (CNAME: sls-admin.yugasun.com.cdn.dnsv1.com)backend:region: ap-guangzhoufunctionName: admin-systemapiGatewayServiceId: service-f1bhmhk4url: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/
注释:这里
frontend
中多输出了host
,是我们的 CDN 加速域名,可以通过配置@serverless/tencent-website
组件的inputs.hosts
来实现。有关 CDN 相关配置说明可以阅读 基于 Serverless Component 的全栈解决方案 - 续集。当然,如果你不想配置 CDN,直接删除,然后访问 COS 生成的静态网站 url。
部署成功后,我们就可以访问 https://sls-admin.yugasun.com
登录体验了。
源码
本篇涉及到所有源码都维护在开源项目 tencent-serverless-demo 中 admin-system
总结
本文基于腾讯云的无服务器框架 Serverless Framework 实现,涉及到内容较多,推荐在阅读时,边看边开发,跟着文章节奏一步一步实现。
如果遇到问题,可以参考本文源码。如果你成功实现了,可以到官网进一步熟悉 Egg.js 框架,以便今后可以实现更加复杂的应用。虽然本文使用的是 Vue.js 前端框架,但是你也可以将 frontend
更换为任何你喜欢的前端框架项目,开发时只需要将接口请求前缀使用 @serverless/tencent-website
组件生成的 env.js
文件就行。
传送门:
- GitHub: github.com/serverless
- 官网:serverless.com
欢迎访问:Serverless 中文网,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发!
Serverless 实战 —— Serverless + Egg.js 后台管理系统实战相关推荐
- Vue实战狗尾草博客后台管理系统
Vue实战狗尾草博客后台管理系统第一章 这里准备采用的技术栈为:vue全家桶+element-ui 这里因为是后台管理系统,没有做SSR的必要.所以这里就采用前后端分离来昨晚这个项目~ 项目搭建 vu ...
- Vue实战狗尾草博客后台管理系统第三章
Vue实战狗尾草博客后台管理系统第三章 Vue实现狗尾草博客后台管理系统第三章 本章节,咱们开发管理系统侧边栏及面包屑功能. 先上一张效果图 样式呢,作者前端初审,关于设计上毫无美感可言,大家可根据自 ...
- [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[JDBC](1)
MIS信息管理系统实战开发之使用MySQL实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有 ...
- [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[文件保存](1)
MIS信息管理系统实战开发之单独使用文件实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有工 ...
- 最新Spring Boot实战项目(权限后台管理系统)详解
Spring Boot实战项目 - 权限后台管理系统 简介 这是一套基于spring boot 2.16.shiro.jwt.redis.swagger2.mybatis .thymeleaf.lay ...
- Vue实战狗尾草博客后台管理系统第七章
Vue实战狗尾草博客后台管理平台第七章 本章内容为借助模块化来阐述Vuex的进阶使用. 在复杂项目的架构中,对于数据的处理是一个非常头疼的问题.处理不当,不仅对维护增加相当的工作负担,也给开发增加巨大 ...
- Vue项目实战之电商后台管理系统(二) 主页模块
前言 目录 前言 一.主页布局 1.1 整体布局 1.2 头部区域布局 1.3 左侧菜单布局 1.3.1 静态布局 1.3.2 通过axios请求拦截器来进行权限验证 1.3.3 通过axios获取左 ...
- Vue项目实战之电商后台管理系统(一) 用户登录模块
目录 一.项目概述 二.项目初始化 2.1 前端项目初始化步骤 2.2 后台项目的环境安装配置 三.用户登录/登出功能实现 3.1 登录功能概述 3.1.1 登录状态保持 3.1.2 登录逻辑: 3. ...
- 微信小程序项目实战+JAVA SSM框架后台管理系统
毕业设计做的是一个阅读微信小程序+后台管理系统 ,最后被评为优秀毕业设计,在此将项目源码及设计思路进行分享(文末含源码下载地址). 效果图如下: 一.系统开发环境 (1)Windows10操作系统 ( ...
最新文章
- uboot中的中断macro宏
- 测试音质好坏的软件,音响音质的好坏简单的两招测出
- 如何从特定位置开始分享YouTube视频
- 三层架构项目如何发布_链客Talk | 优盾首席架构师Alex Yang:如何从0开发区块链项目?...
- Process打开文件
- 项目经理修炼手册,泄露章节 ,项目经理需要注意的习惯
- java执行cmd命令并获取返回结果字符串
- 模拟某个浏览器抓取数据
- MapOnline在线地图插件,ArcGIS的得力助手
- android 小米盒子开发,接小米盒子Android SDK
- 常用国内镜像源地址汇总
- 一道阿姆斯特朗回旋好题( Convex HDU - 5979)
- MySQL 关于时间设置注意事项
- oracle11g 网页账号,oracle11g 创建用户并受权
- CE 开启 DBVM
- Markdown快速入门
- 刷题体验第一天——《录鼎记》第一章
- Shell编程实战范例
- ubuntu 下 uml 工具
- 面试改掉呃呃呃_呃...税
热门文章
- 分享Discuz! X2插件嵌入点列表(包含门户、社区、群组等)
- ClearCase是全球领先的软件配置管理工具
- 实践证明,SQL Server 2000 + 2005 + 2008,完全可以共存
- session和cookie的最深刻理解
- CSS:理解:Before和:After伪元素
- 命令行导出和导入数据库 How to export and import MySQL database using command line Interface...
- 网页检测 AdBlock 的 6 种方法
- Magento 安装教程
- linux下编译安装ACE-6.5.1
- 数据结构学不会?视频+项目+网站大全,还有独家学习方法