作为一名前端开发者,在选择 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 项目:
1$ mkdir egg-example && cd egg-example 2$ npm init egg --type=simple 3$ 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 从入门到精通系列文章
之后你的项目目录结构如下:
1. 2├── README.md 3├── backend // 创建的 Egg.js 项目 4└── frontend // 克隆的 Vue.js 前端项目模板
启动前端项目熟悉下界面:
1$ cd frontend 2$ npm install 3$ npm run dev
然后访问 http://localhost:9528 就可以看到登录界面了。
开发后端服务
对于一个后台管理系统服务,我们这里只实现登录鉴权和文章管理功能,剩下的其他功能大同小异,读者可以之后自由补充扩展。
1. 添加 Sequelize 插件
在正式开发之前,我们需要引入数据库插件,这里本人偏向于使用 Sequelize ORM 工具进行数据库操作,正好 Egg.js 提供了 egg-sequelize 插件,于是直接拿来用,需要先安装:
1$ cd frontend 2# 因为需要通过 sequelize 链接 mysql 所以这也同时安装 mysql2 模块 3$ npm install egg-sequelize mysql2 --save
然后在 backend/config/plugin.js 中引入该插件:
1module.exports = { 2 // .... 3 sequelize: { 4 enable: true, 5 package: "egg-sequelize" 6 } 7 // .... 8};
在 backend/config/config.default.js 中配置数据库连接参数:
1// ... 2const userConfig = { 3 // ... 4 sequelize: { 5 dialect: "mysql", 6 7 // 这里也可以通过 .env 文件注入环境变量,然后通过 process.env 获取 8 host: "xxx", 9 port: "xxx", 10 database: "xxx", 11 username: "xxx", 12 password: "xxx" 13 } 14 // ... 15}; 16// ...
2. 添加 JWT 插件
系统将使用 JWT token 方式进行登录鉴权,安装配置参考官方文档,egg-jwt
3. 添加 Redis 插件
系统将使用 redis 来存储和管理用户 token,安装配置参考官方文档,egg-redis
4. 角色 API
定义用户模型,创建 backend/app/model/role.js 文件如下:
1module.exports = app => { 2 const { STRING, INTEGER, DATE } = app.Sequelize; 3 4 const Role = app.model.define("role", { 5 id: { type: INTEGER, primaryKey: true, autoIncrement: true }, 6 name: STRING(30), 7 created_at: DATE, 8 updated_at: DATE 9 }); 10 11 // 这里定义与 users 表的关系,一个角色可以含有多个用户,外键相关 12 Role.associate = () => { 13 app.model.Role.hasMany(app.model.User, { as: "users" }); 14 }; 15 16 return Role; 17};
实现 Role 相关服务,创建 backend/app/service/role.js 文件如下:
1const { Service } = require("egg"); 2 3class RoleService extends Service { 4 // 获取角色列表 5 async list(options) { 6 const { 7 ctx: { model } 8 } = this; 9 return model.Role.findAndCountAll({ 10 ...options, 11 order: [ 12 ["created_at", "desc"], 13 ["id", "desc"] 14 ] 15 }); 16 } 17 18 // 通过 id 获取角色 19 async find(id) { 20 const { 21 ctx: { model } 22 } = this; 23 const role = await model.Role.findByPk(id); 24 if (!role) { 25 this.ctx.throw(404, "role not found"); 26 } 27 return role; 28 } 29 30 // 创建角色 31 async create(role) { 32 const { 33 ctx: { model } 34 } = this; 35 return model.Role.create(role); 36 } 37 38 // 更新角色 39 async update({ id, updates }) { 40 const role = await this.ctx.model.Role.findByPk(id); 41 if (!role) { 42 this.ctx.throw(404, "role not found"); 43 } 44 return role.update(updates); 45 } 46 47 // 删除角色 48 async destroy(id) { 49 const role = await this.ctx.model.Role.findByPk(id); 50 if (!role) { 51 this.ctx.throw(404, "role not found"); 52 } 53 return role.destroy(); 54 } 55} 56 57module.exports = RoleService;
一个完整的 RESTful API 就该包括以上五个方法,然后实现 RoleController, 创建 backend/app/controller/role.js:
1const { Controller } = require("egg"); 2 3class RoleController extends Controller { 4 async index() { 5 const { ctx } = this; 6 const { query, service, helper } = ctx; 7 const options = { 8 limit: helper.parseInt(query.limit), 9 offset: helper.parseInt(query.offset) 10 }; 11 const data = await service.role.list(options); 12 ctx.body = { 13 code: 0, 14 data: { 15 count: data.count, 16 items: data.rows 17 } 18 }; 19 } 20 21 async show() { 22 const { ctx } = this; 23 const { params, service, helper } = ctx; 24 const id = helper.parseInt(params.id); 25 ctx.body = await service.role.find(id); 26 } 27 28 async create() { 29 const { ctx } = this; 30 const { service } = ctx; 31 const body = ctx.request.body; 32 const role = await service.role.create(body); 33 ctx.status = 201; 34 ctx.body = role; 35 } 36 37 async update() { 38 const { ctx } = this; 39 const { params, service, helper } = ctx; 40 const body = ctx.request.body; 41 const id = helper.parseInt(params.id); 42 ctx.body = await service.role.update({ 43 id, 44 updates: body 45 }); 46 } 47 48 async destroy() { 49 const { ctx } = this; 50 const { params, service, helper } = ctx; 51 const id = helper.parseInt(params.id); 52 await service.role.destroy(id); 53 ctx.status = 200; 54 } 55} 56 57module.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 文件,如下:
1"use strict"; 2 3class AppBootHook { 4 constructor(app) { 5 this.app = app; 6 } 7 8 async willReady() { 9 // 这里只能在开发模式下同步数据库表格 10 const isDev = process.env.NODE_ENV === "development"; 11 if (isDev) { 12 try { 13 console.log("Start syncing database models..."); 14 await this.app.model.sync({ logging: console.log, force: isDev }); 15 console.log("Start init database data..."); 16 await this.app.model.query( 17 "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');" 18 ); 19 await this.app.model.query( 20 "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);" 21 ); 22 await this.app.model.query( 23 "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);" 24 ); 25 console.log("Successfully init database data."); 26 console.log("Successfully sync database models."); 27 } catch (e) { 28 console.log(e); 29 throw new Error("Database migration failed."); 30 } 31 } 32 } 33} 34 35module.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,进行鉴权验证。思路很简单,我们就开始实现了。
流程图如下:
<center> <img src="https://static.yugasun.com/serverless/login-process.jpg" width="300" alt="Login Process"/> </center>首先,在 backend/app/controller/home.js 中新增登录处理 login 方法:
1class HomeController extends Controller { 2 // ... 3 async login() { 4 const { ctx, app, config } = this; 5 const { service, helper } = ctx; 6 const { username, password } = ctx.request.body; 7 const user = await service.user.findByName(username); 8 if (!user) { 9 ctx.status = 403; 10 ctx.body = { 11 code: 403, 12 message: "Username or password wrong" 13 }; 14 } else { 15 if (user.password === helper.encryptPwd(password)) { 16 ctx.status = 200; 17 const token = app.jwt.sign( 18 { 19 id: user.id, 20 name: user.name, 21 role: user.role.name, 22 avatar: user.avatar 23 }, 24 config.jwt.secret, 25 { 26 expiresIn: "1h" 27 } 28 ); 29 try { 30 await app.redis.set(`token_${user.id}`, token); 31 ctx.body = { 32 code: 0, 33 message: "Get token success", 34 token 35 }; 36 } catch (e) { 37 console.error(e); 38 ctx.body = { 39 code: 500, 40 message: "Server busy, please try again" 41 }; 42 } 43 } else { 44 ctx.status = 403; 45 ctx.body = { 46 code: 403, 47 message: "Username or password wrong" 48 }; 49 } 50 } 51 } 52}
注释:这里有个密码存储逻辑,用户在注册时,密码都是通过
helper函数encryptPwd()进行加密的(这里用到最简单的 md5 加密方式,实际开发中建议使用更加高级加密方式),所以在校验密码正确性时,也需要先加密一次。至于如何在 Egg.js 框架中新增helper函数,只需要在backend/app/extend文件夹中新增helper.js文件,然后modole.exports一个包含该函数的对象就行,参考 Egg 框架扩展文档
然后,在 backend/app/controller/home.js 中新增 userInfo 方法,获取用户信息:
1async userInfo() { 2 const { ctx } = this; 3 const { user } = ctx.state; 4 ctx.status = 200; 5 ctx.body = { 6 code: 0, 7 data: user, 8 }; 9}
egg-jwt 插件,在鉴权通过的路由对应 controller 函数中,会将 app.jwt.sign(user, secrete) 加密的用户信息,添加到 ctx.state.user 中,所以 userInfo 函数只需要将它返回就行。
之后,在 backend/app/controller/home.js 中新增 logout 方法:
1async logout() { 2 const { ctx } = this; 3 ctx.status = 200; 4 ctx.body = { 5 code: 0, 6 message: 'Logout success', 7 }; 8}
userInfo 和 logout 函数非常简单,重点是路由中间件如何处理。
接下来,我们来定义登录相关路由,修改 backend/app/router.js 文件,新增 /login, /user-info, /logout 三个路由:
1const koajwt = require("koa-jwt2"); 2 3module.exports = app => { 4 const { router, controller, jwt } = app; 5 router.get("/", controller.home.index); 6 7 router.post("/login", controller.home.login); 8 router.get("/user-info", jwt, controller.home.userInfo); 9 const isRevokedAsync = function(req, payload) { 10 return new Promise(resolve => { 11 try { 12 const userId = payload.id; 13 const tokenKey = `token_${userId}`; 14 const token = app.redis.get(tokenKey); 15 if (token) { 16 app.redis.del(tokenKey); 17 } 18 resolve(false); 19 } catch (e) { 20 resolve(true); 21 } 22 }); 23 }; 24 router.post( 25 "/logout", 26 koajwt({ 27 secret: app.config.jwt.secret, 28 credentialsRequired: false, 29 isRevoked: isRevokedAsync 30 }), 31 controller.home.logout 32 ); 33 34 router.resources("roles", "/roles", controller.role); 35 router.resources("users", "/users", controller.user); 36 router.resources("posts", "/posts", controller.post); 37};
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 入口文件:
1const { Application } = require("egg"); 2const app = new Application(); 3module.exports = app;
然后修改 backend/config/config.default.js 文件:
1const config = (exports = { 2 env: "prod", // 推荐云函数的 egg 运行环境变量修改为 prod 3 rundir: "/tmp", 4 logger: { 5 dir: "/tmp" 6 } 7});
注释:这里之所有需要修改运行和日志目录,是因为云函数运行时,只有
/tmp才有写权限。
全局安装 serverless 命令:
$ npm install serverless -g
2. 配置 Serverless
在项目根目录下创建 serverless.yml 文件,同时新增 backend 配置:
1backend: 2 component: "@serverless/tencent-egg" 3 inputs: 4 code: ./backend 5 functionName: admin-system 6 # 这里必须指定一个具有操作 mysql 和 redis 的角色,具体角色创建,可访问 https://console.cloud.tencent.com/cam/role 7 role: QCS_SCFFull 8 functionConf: 9 timeout: 120 10 # 这里的私有网络必须和 mysql、redis 实例一致 11 vpcConfig: 12 vpcId: vpc-xxx 13 subnetId: subnet-xxx 14 apigatewayConf: 15 protocols: 16 - https
此时你的项目目录结构如下:
1. 2├── README.md // 项目说明文件 3├── serverless.yml // serverless yml 配合文件 4├── backend // 创建的 Egg.js 项目 5└── frontend // 克隆的 Vue.js 前端项目模板
3. 执行部署
执行部署命令:
$ serverless --debug
之后控制台需要进行扫码登录验证腾讯云账号,扫码登录就好。等部署成功会发挥如下信息:
1 backend: 2 region: ap-guangzhou 3 functionName: admin-system 4 apiGatewayServiceId: service-f1bhmhk4 5 url: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/
这里输出的 url 就是部署成功的 API 网关接口,可以直接访问测试。
注释:云函数部署时,会自动在腾讯云的 API 网关创建一个服务,同时创建一个 API,通过该 API 就可以触发云函数执行了。
4. 账号配置(可选)
当前默认支持 Serverless cli 扫描二维码登录,如果希望配置持久的环境变量/秘钥信息,也可以在项目根目录创建 .env 文件
在 .env 文件中配置腾讯云的 SecretId 和 SecretKey 信息并保存,密钥可以在 API 密钥管理 中获取或者创建.
1# .env 2TENCENT_SECRET_ID=123 3TENCENT_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// 1. 引入接口变量文件,这个会依赖 @serverless/tencent-website 组件自动生成 2import "./env.js"; 3 4import Vue from "vue"; 5 6import "normalize.css/normalize.css"; 7import ElementUI from "element-ui"; 8import "element-ui/lib/theme-chalk/index.css"; 9import locale from "element-ui/lib/locale/lang/en"; 10import "@/styles/index.scss"; 11import App from "./App"; 12import store from "./store"; 13import router from "./router"; 14import "@/icons"; 15import "@/permission"; 16 17// 2. 下面这段就是 mock server 引入,删除就好 18// if (process.env.NODE_ENV === 'production') { 19// const { mockXHR } = require('../mock') 20// mockXHR() 21// } 22 23Vue.use(ElementUI, { locale }); 24Vue.config.productionTip = false; 25 26new Vue({ 27 el: "#app", 28 router, 29 store, 30 render: h => h(App) 31});
2. 修改接口函数
修改 frontend/src/api/user.js 文件,包括登录、注销、获取用户信息和获取用户列表函数如下:
1import request from "@/utils/request"; 2 3// 登录 4export function login(data) { 5 return request({ 6 url: "/login", 7 method: "post", 8 data 9 }); 10} 11 12// 获取用户信息 13export function getInfo(token) { 14 return request({ 15 url: "/user-info", 16 method: "get" 17 }); 18} 19 20// 注销登录 21export function logout() { 22 return request({ 23 url: "/logout", 24 method: "post" 25 }); 26} 27 28// 获取用户列表 29export function getList() { 30 return request({ 31 url: "/users", 32 method: "get" 33 }); 34}
新增 frontend/src/api/post.js 文件如下:
1import request from "@/utils/request"; 2 3// 获取文章列表 4export function getList(params) { 5 return request({ 6 url: "/posts", 7 method: "get", 8 params 9 }); 10} 11 12// 创建文章 13export function create(data) { 14 return request({ 15 url: "/posts", 16 method: "post", 17 data 18 }); 19} 20 21// 删除文章 22export function destroy(id) { 23 return request({ 24 url: `/posts/${id}`, 25 method: "delete" 26 }); 27}
3. 修改接口工具函数
因为 @serverless/tencent-website 组件可以定义 env 参数,执行成功后它会在指定 root 目录自动生成 env.js,然后在 frontend/src/main.js 中引入使用。 它会挂载 env 中定义的接口变量到 window 对象上。比如这生成的 env.js 文件如下:
1window.env = {}; 2window.env.apiUrl = 3 "https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/";
根据此文件我们来修改 frontend/src/utils/request.js 文件:
1import axios from "axios"; 2import { MessageBox, Message } from "element-ui"; 3import store from "@/store"; 4import { getToken } from "@/utils/auth"; 5 6// 创建 axios 实例 7const service = axios.create({ 8 // 1. 这里设置为 `env.js` 中的变量 `window.env.apiUrl` 9 baseURL: window.env.apiUrl || "/", // url = base url + request url 10 timeout: 5000 // request timeout 11}); 12 13// request 注入 14service.interceptors.request.use( 15 config => { 16 // 2. 添加鉴权token 17 if (store.getters.token) { 18 config.headers["Authorization"] = `Bearer ${getToken()}`; 19 } 20 return config; 21 }, 22 error => { 23 console.log(error); // for debug 24 return Promise.reject(error); 25 } 26); 27 28// 请求 response 注入 29service.interceptors.response.use( 30 response => { 31 const res = response.data; 32 33 // 只有请求code为0,才是正常返回,否则需要提示接口错误 34 if (res.code !== 0) { 35 Message({ 36 message: res.message || "Error", 37 type: "error", 38 duration: 5 * 1000 39 }); 40 41 if (res.code === 50008 || res.code === 50012 || res.code === 50014) { 42 // to re-login 43 MessageBox.confirm( 44 "You have been logged out, you can cancel to stay on this page, or log in again", 45 "Confirm logout", 46 { 47 confirmButtonText: "Re-Login", 48 cancelButtonText: "Cancel", 49 type: "warning" 50 } 51 ).then(() => { 52 store.dispatch("user/resetToken").then(() => { 53 location.reload(); 54 }); 55 }); 56 } 57 return Promise.reject(new Error(res.message || "Error")); 58 } else { 59 return res; 60 } 61 }, 62 error => { 63 console.log("err" + error); 64 Message({ 65 message: error.message, 66 type: "error", 67 duration: 5 * 1000 68 }); 69 return Promise.reject(error); 70 } 71); 72 73export 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 文件,新增前端相关配置:
1name: admin-system 2 3# 前端配置 4frontend: 5 component: "@serverless/tencent-website" 6 inputs: 7 code: 8 src: dist 9 root: frontend 10 envPath: src # 相对于 root 指定目录,这里实际就是 frontend/src 11 hook: npm run build 12 env: 13 # 依赖后端部署成功后生成的 url 14 apiUrl: ${backend.url} 15 protocol: https 16 # TODO: CDN 配置,请修改!!! 17 hosts: 18 - host: sls-admin.yugasun.com # CDN 加速域名 19 https: 20 certId: abcdedg # 为加速域名在腾讯云平台申请的免费证书 ID 21 http2: off 22 httpsType: 4 23 forceSwitch: -2 24 25# 后端配置 26backend: 27 component: "@serverless/tencent-egg" 28 inputs: 29 code: ./backend 30 functionName: admin-system 31 role: QCS_SCFFull 32 functionConf: 33 timeout: 120 34 vpcConfig: 35 vpcId: vpc-6n5x55kb 36 subnetId: subnet-4cvr91js 37 apigatewayConf: 38 protocols: 39 - https
2. 执行部署
执行部署命令:
$ serverless --debug
输出如下成功结果:
1 frontend: 2 url: https://dtnu69vl-470dpfh-1251556596.cos-website.ap-guangzhou.myqcloud.com 3 env: 4 apiUrl: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/ 5 host: 6 - https://sls-admin.yugasun.com (CNAME: sls-admin.yugasun.com.cdn.dnsv1.com) 7 backend: 8 region: ap-guangzhou 9 functionName: admin-system 10 apiGatewayServiceId: service-f1bhmhk4 11 url: 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 应用的开发!