by yugasun from https://yugasun.com/post/serverless-fullstack-vue-practice.html
本文可全文轉(zhuǎn)載昌屉,但需要保留原作者和出處钙蒙。
什么是 Serverless Component
因為 Serverless Component 是基于無服務(wù)框架 (Serverless Framework)的,所以在閱讀這篇實踐文章之前间驮,建議先大概了解下 serverless
命令的使用躬厌,因為下面的案例會使用到。
Serverless Component 的目標(biāo)是磨平不同云服務(wù)平臺之間差異竞帽,你可以將它看作是可以更輕松地構(gòu)建應(yīng)用程序的依賴模塊扛施。目前 Serverless Component ,已經(jīng)形成一個由社區(qū)貢獻驅(qū)動的生態(tài)系統(tǒng)屹篓,你可以瀏覽和使用社區(qū)的所有組件疙渣,快速開發(fā)一款自己想要的應(yīng)用。
Serverless Component 工作原理
基于 Serverless Component 架構(gòu)堆巧,你可以將任何云服務(wù)打包成一個組件妄荔。這個組件將含有一份 serverless.yml
配置文件,并且通過簡單地進行配置就可以使用谍肤。我們拿 @serverless/tencent-express 來舉??啦租。
如果我們要使用它,只需要新建一個項目 express-demo
荒揣,然后修改 serverless.yml
配置如下:
express:
component: '@serverless/tencent-express'
inputs:
region: ap-shanghai
因為 serverless
框架部署到云的鑒權(quán)都是基于 dotenv 注入全局的變量來實現(xiàn)的篷角,所以還得在根目錄下新增 .env
文件,并配置對應(yīng)的鑒權(quán)參數(shù)系任。
之后我們就可以在 app.js
中輕松的編寫基于 express
的接口服務(wù)了:
const express = require('express')
const app = express()
app.get('/', function(req, res) {
res.send('Hello Express')
})
// 不要忘了導(dǎo)出恳蹲,因為該組件會對它進行包裝,輸出成云函數(shù)
module.exports = app
這背后所有的流程邏輯都是組件內(nèi)部實現(xiàn)的,包括:云函數(shù)的部署,API網(wǎng)關(guān)的生成等。
下面是一張簡單的組件依賴圖:
通過此圖可以清晰地查看組件帶來的收益,借助社區(qū)現(xiàn)有的 @serverless/tencent-express 和 @serverless/tencent-website 組件唉窃,我們就可以很快構(gòu)建想要的全棧應(yīng)用。
全棧應(yīng)用實戰(zhàn)
接下來將介紹如何借助 Serverless Component 快速開發(fā)全棧Web應(yīng)用软啼。
在開始所有步驟前榛瓮,需執(zhí)行
npm install -g serverless
命令,全局安裝serverless cli
喉悴。
準(zhǔn)備
新建項目目錄 fullstack-application-vue
棱貌,在該項目目錄下新增 api
和 dashboard
目錄。然后新增 serverless.yml
和 .env
配置文件箕肃,項目目錄結(jié)構(gòu)如下:
├── README.md // 項目說明文檔
├── api // Restful api 后端服務(wù)
├── dashboard // 前端頁面
├── .env // 騰訊云相關(guān)鑒權(quán)參數(shù):TENCENT_APP_ID婚脱,TENCENT_SECRET_ID,TENCENT_SECRET_KEY
└── serverless.yml // serverless 文件
后端服務(wù)開發(fā)
進入目錄 api
,新增 app.js
文件障贸,編寫 express
服務(wù)代碼错森,這里先新增一個路由 /
,并返回當(dāng)前服務(wù)器時間:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/', (req, res) => {
res.send(JSON.stringfy({ message: `Server time: ${new Date().toString()}` }));
});
module.exports = app;
前端頁面開發(fā)
本案例使用的是 Vue.js
+ Parcel
的前端模板篮洁,當(dāng)然你可以使用任何前端項目腳手架涩维,比如 Vue.js 官方推薦的 Vue CLI 生成的項目。進入 dashboard
目錄袁波,靜態(tài)資源你可以直接復(fù)制我準(zhǔn)備好的 項目模板瓦阐,編寫入口文件 src/index.js
:
// 這里初始是沒有 env.js 模塊的,第一次部署后會自動生成
require('../env');
const Vue = require('vue');
module.exports = new Vue({
el: '#root',
data: {
message: 'Click me!',
isVisible: true,
},
methods: {
async queryServer() {
const response = await fetch(window.env.apiUrl);
const result = await response.json();
this.message = result.message;
},
},
});
配置
前后端代碼都準(zhǔn)備好了篷牌,現(xiàn)在我們還需要簡單配置下 serverless.yml
文件了:
name: fullstack-application-vue
frontend:
component: '@serverless/tencent-website'
# inputs 為 @serverless/tencent-website 組件的輸入
# 具體配置說明參考:https://github.com/serverless-components/tencent-website/blob/master/docs/configure.md
inputs:
code:
src: dist
root: frontend
hook: npm run build
env:
# 下面的 API服務(wù)部署后睡蟋,獲取對應(yīng)的 api 請求路徑
apiUrl: ${api.url}
api:
component: '@serverless/tencent-express'
# inputs 為 @serverless/tencent-express 組件的輸入
# 具體配置說明參考:https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md
inputs:
code: ./api
functionName: fullstack-vue-api
apigatewayConf:
protocol: https
簡單的介紹下配置:首先,該文件定義了
frontend
和api
兩個模塊枷颊,分別通過component
屬性指定依賴的 Serverless Component 戳杀。對于一個標(biāo)準(zhǔn)的 Serverless Component ,都會接受一個inputs
屬性參數(shù)夭苗,然后組件會根據(jù)inputs
的配置進行處理和部署豺瘤,具體有關(guān)配置的參數(shù)說明,請參考相關(guān)組件的官方配置說明听诸。
部署
以上所有的步驟都完成后坐求,接下來就是第一次部署了。
為什么不是直接聯(lián)調(diào)開發(fā)呢晌梨?因為后端服務(wù)是云函數(shù)桥嗤,但是到目前為止,所有代碼都是在本地編寫仔蝌,前端頁面接口請求鏈接還不存在泛领。所以需要先將云函數(shù)部署到云端,才能進行前后端調(diào)試敛惊。這個也是本人目前遇到的痛點渊鞋,因為每次修改后端服務(wù)后,都需要重新部署瞧挤,然后進行前端開發(fā)調(diào)試锡宋。如果你有更好的建議,歡迎評論指教~
部署時特恬,只需要運行 serverless
命令就行执俩,當(dāng)然如果你需要查看部署中的 DEBUG
信息,還需要加上 --debug
參數(shù)癌刽,如下:
$ serverless
# or
$ serverless --debug
然后終端會 balabalabala~
, 輸出一大堆 DEBUG
信息役首,最后只需要看到綠色的 done
就行了:
這樣一個基于 Serverless Component 的全棧應(yīng)用就開發(fā)好了尝丐。趕緊點擊你部署好的鏈接體驗一下吧~
數(shù)據(jù)庫連接
既然是全棧,怎么少得了數(shù)據(jù)庫的讀寫呢衡奥?接下來介紹如何添加數(shù)據(jù)庫的讀寫操作爹袁。
準(zhǔn)備
想要操作數(shù)據(jù)庫,必須先擁有一臺數(shù)據(jù)庫實例矮固,騰訊云Mysql云數(shù)據(jù)庫 現(xiàn)在也很便宜呢簸,可以購買一個最基本按量計費 1核1G內(nèi)存
的 1小時收費不到 4 毛錢
,是不是非常劃算乏屯。購買好之后初始化配置根时,然后新增一個 serverless
數(shù)據(jù)庫,同時新增一張 users
表:
CREATE TABLE if not exists `test` ( `name` varchar (32) NOT NULL ,`email` varchar (64) NOT NULL ,`site` varchar (128) NOT NULL ) ENGINE = innodb DEFAULT CHARACTER SET = "utf8mb4" COLLATE = "utf8mb4_general_ci"
前端修改
首先修改前端入口文件 frontend/src/index.js
新增相關(guān)函數(shù)操作:
require('../env');
const Vue = require('vue');
const axios = require('axios');
module.exports = new Vue({
el: '#root',
data: {
// ...
form: {
name: '',
email: '',
site: '',
},
userList: [],
},
methods: {
// ...
// 獲取用戶列表
async getUsers() {
const res = await axios.get(window.env.apiUrl + 'users');
this.userList = res.data && res.data.data || [];
},
// 新增一個用戶
async addUser() {
const data = this.form;
const res = await axios.post(window.env.apiUrl + 'users', data);
console.log(res);
if (res.data) {
this.getUsers();
}
},
},
mounted() {
// 視圖掛在后辰晕,獲取用戶列表
this.getUsers();
}
});
當(dāng)然你還需要修改視圖模板文件 frontend/index.html
蛤迎,在頁面模板中新增用戶列表和用戶表單:
<!-- user form -->
<section class="user-form" action="#">
<div class="form-item">
<label for="name">
Name:
</label>
<input name="name" v-model="form.name" type="text" /><br />
</div>
<div class="form-item">
<label for="email">
Email:
</label>
<input name="email" v-model="form.email" type="email" /><br />
</div>
<div class="form-item">
<label for="site">
Site:
</label>
<input name="site" v-model="form.site" type="text" /><br />
</div>
<button @click="addUser">Submit</button>
</section>
<!-- user list -->
<section class="user-list">
<ul v-if="userList.length > 0">
<li v-for="item in userList" :key="item.id">
<p>
<b>Name: {{ item.name }}</b>
<b>Email: {{ item.email }}</b>
<b>Site: {{ item.site }}</b>
</p>
</li>
</ul>
<span v-else>No Data</span>
</section>
注意:如果還不熟悉 Vue.js 語法,請移至 官方文檔含友,當(dāng)然如果你想快速上手 Vue.js 開發(fā)替裆,也可以閱讀這份 Vue 從入門到精通 教程。
后端修改
這里使用 .env
來進行數(shù)據(jù)庫連接參數(shù)配置窘问,在 api
目錄下新增 .env
文件辆童,將之前的數(shù)據(jù)庫配置填入文件中,參考 api/.env.example
文件惠赫。然后添加并安裝 dotenv
依賴把鉴,同時添加 mysql2
模塊進行數(shù)據(jù)庫操作,body-parser
模塊進行 POST
請求時的 body
解析儿咱。
之后新增后端api庭砍,進行數(shù)據(jù)庫讀寫,修改后的 api/app.js
代碼如下:
'use strict';
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const mysql = require('mysql2');
const bodyParser = require('body-parser');
// init mysql connection
function initMysqlPool() {
const { DB_HOST, DB_PORT, DB_DATABASE, DB_USER, DB_PASSWORD } = process.env;
const promisePool = mysql
.createPool({
host: DB_HOST,
user: DB_USER,
port: DB_PORT,
password: DB_PASSWORD,
database: DB_DATABASE,
connectionLimit: 1,
})
.promise();
return promisePool;
}
const app = express();
app.use(bodyParser.json());
app.use(cors());
if (!app.promisePool) {
app.promisePool = initMysqlPool();
}
app.get('/', (req, res) => {
res.send(JSON.stringify({ message: `Server time: ${new Date().toString()}` }));
});
// get user list
app.get('/users', async (req, res) => {
const [data] = await app.promisePool.query('select * from users');
res.send(
JSON.stringify({
data: data,
}),
);
});
// add new user
app.post('/users', async (req, res) => {
let result = '';
try {
const { name, email, site } = req.body;
const [res] = await app.promisePool.query('INSERT into users SET ?', {
name: name,
email: email,
site: site,
});
result = {
data: res && res.insertId,
message: 'Insert Success',
};
} catch (e) {
result = {
data: e,
message: 'Insert Fail',
};
}
res.send(JSON.stringify(result));
});
module.exports = app;
配置修改
這里數(shù)據(jù)庫訪問需要通過騰訊云私有網(wǎng)絡(luò)混埠,所以還需要為云函數(shù)配置私有網(wǎng)絡(luò)(VPC)怠缸,同時還需要配置能夠操作數(shù)據(jù)庫的角色(關(guān)于角色配置,可以直接到 角色管理頁面)钳宪,這里我新建了一個 QCS_SCFFull
的角色揭北,可以用來訪問數(shù)據(jù)庫。然后修改 serverless.yml
中的配置:
# ...
api:
component: '@serverless/tencent-express'
# more configuration for @serverless/tencent-website,
# refer to: https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md
inputs:
code: ./api
functionName: fullstack-vue-api
role: QCS_SCFFull # 此角色必須具備訪問數(shù)據(jù)庫權(quán)限
functionConf:
# 這個是用來訪問新創(chuàng)建數(shù)據(jù)庫的私有網(wǎng)絡(luò)吏颖,可以在你的數(shù)據(jù)庫實例管理頁面查看
vpcConfig:
vpcId: vpc-6n5x55kb
subnetId: subnet-4cvr91js
apigatewayConf:
protocol: https
最后重新部署一下就行了搔体。
總結(jié)
當(dāng)然全棧方案,并沒有這么簡單侦高,這里只是簡單介紹嫉柴,如何使用 Serverless Component ,快速實現(xiàn)一個全棧應(yīng)用奉呛。如果要應(yīng)用到實際的業(yè)務(wù)場景计螺,我們還需考慮更多的問題。而且目前社區(qū)組件還不夠完善瞧壮,很多功能還需要我們自己去探索發(fā)現(xiàn)登馒。也希望更多牛人加入到 Serverless Component 社區(qū),貢獻更多的優(yōu)秀組件咆槽。