背景
從2000年開始,xml
作為資料交換格式開始流行,伺服器拼接xml
介面,客戶端js獲取xml
內容,動態修改頁面。
幾年後,資料量更小的json
替代了xml
。
移動網際網路到來後,因為客戶端分裂,加劇了介面的泛濫。
一轉眼,介面已經玩了20年了。其他技術飛速發展,而前後端互動卻一直是介面,沒有什麼創新。
js已經有了import
、export
,為什麼呼叫後端介面,不能像呼叫一個前端模組一樣呢?
serverless,讓這一切開始了變化。
亞馬遜lambda
提出了雲函式的概念,不再使用restful
的url
,但仍然是基於json
交換前後端資料。
uniCloud
最初也以支援雲函式為開始,但我們發現這仍不夠優雅、簡潔、直觀、統一。
從HBuilderX 3.4
開始,uniCloud
推出了“雲物件”,讓呼叫雲端服務,真正變成像呼叫前端模組一樣簡單。
什麼是雲物件
雲物件:伺服器編寫API,客戶端呼叫API,不再開發傳輸json的介面。思路更清晰、程式碼更精簡。
比如服務端編寫一個雲物件news,該物件匯出若干方法:add()、getList()、getDetailById()、softDel()、changeContent()、allowPublic()、addComment()、getComments()...等方法。
客戶端的js則可以直接import
這個news
雲物件,然後直接呼叫add
等方法。
伺服器示例程式碼如下:
HBuilderX中在uniCloud/cloudfunctions目錄新建雲函式/雲物件,選擇型別為雲物件,起名為news。開啟雲物件入口index.obj.js
,新增一個add方法。
// 雲物件名:news
module.exports = {
add(title, content) {
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_NEWS',
errMsg: '標題或內容不可為空'
}
}
// ...其他邏輯
return {
errCode: 0,
errMsg: '建立成功'
}
}
}
然後在客戶端的js中,import
這個news
物件,呼叫它的add
方法。
const news = uniCloud.importObject('news') //第一步匯入雲物件
async function add () {
try {
const res = await news.add('title demo', 'content demo') //匯入雲物件後就可以直接呼叫該物件的方法了,注意使用非同步await
uni.showToast({
title: '建立成功'
})
} catch (e) { // 符合uniCloud響應體規範 https://uniapp.dcloud.net.cn/uniCloud/cf-functions?id=resformat,自動丟擲此錯誤
}
}
可以看到雲物件的程式碼非常清晰,程式碼行數也只有27行。
而同樣的邏輯,使用傳統的介面方式則需要更多程式碼,見下:
// 傳統方式呼叫雲函式-雲函式程式碼
// 雲函式名:news
// 雲函式入口index.js內容如下
'use strict';
exports.main = async (event, context) => {
const {
method,
params
} = event
switch(method) {
case 'add': {
let {
title,
content
} = params
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_NEWS',
errMsg: 'NEWS標題或內容不可為空'
}
}
// ...省略其他邏輯
return {
errCode: 0,
errMsg: '建立成功'
}
}
}
return {
errCode: 'METHOD_NOT_FOUND',
errMsg: `Method[${method}] not found`
}
};
傳統方式呼叫雲函式-客戶端程式碼
async function add () {
try {
const res = uniCloud.callFunction({
name: 'news',
data: {
method: 'add',
params: {
title: 'title demo',
content: 'content demo'
}
}
})
const {
errCode,
errMsg
} = res.result
if(errCode) {
uni.showModal({
title: '建立失敗',
content: errMsg,
showCancel: false
})
return
}
uni.showToast({
title: '建立成功'
})
} catch (e) {
uni.showModal({
title: '建立失敗',
content: e.message,
showCancel: false
})
}
}
以上傳統開發需要68行程式碼,對比雲物件的27行程式碼,可以說“又醜又長”
更多強大功能
1. 預處理與後處理
無論請求雲物件的哪個方法,開始前都會經過_before
方法,結束會執行_after
方法。這有助於統一的提前校驗和格式化錯誤。
示例:
// news/index.obj.js
module.exports = {
_before: function(){
this.startTime = Date.now() // 在before內記錄開始時間並在this上掛載,以供後續流程使用
const methodName = this.getMethodName() // 獲取當前請求的雲物件方法名
if(methodName === 'add' && !this.getUniIdToken()) {
throw new Error('token不存在')
}
},
add: function(title = '', content = '') {
if(title === 'abc') {
throw new Error('abc不是一個合法的news標題')
}
return {
errCode: 0,
errMsg: '建立成功'
}
},
_after(error, result) {
if(error) {
throw error // 如果方法丟擲錯誤,也直接丟擲不處理
}
result.timeCost = Date.now() - this.startTime
return result
}
}
2. 雲物件的返回值相容uniCloud響應體規範
雲物件返回值預設為uniCloud響應體規範
,方便客戶端統一攔截錯誤。
無論網路異常,還是token過期,都可以統一攔截提示。
3. 自動顯示互動介面
每次寫客戶端聯網的程式碼時,開發者都免不了重複寫一堆程式碼:先呼叫loading等待框,聯網結束後再關閉loading,如果伺服器異常則彈出提示框。
原來的寫法(22行):
uni.showLoading({
title: '聯網中...'
})
uni.request({
url: "xxxx",
success: (res) => {
uni.showToast({
title: '新增成功',
icon: 'success',
mask: true,
duration: duration
});
},
fail: (err) => {
uni.showModal({
content: err.errMsg,
showCancel: false
});
},
complete: () => {
uni.hideLoading();
}
});
現在,呼叫雲物件的方法時,預設自帶上述功能。
- 在請求聯網開始時顯示loading等待框,
- 結束後隱藏loading
- 如果請求報錯,顯示彈窗(也可配置為顯示Toast)
const news = uniCloud.importObject('news') //第一步匯入雲物件
try {
await news.add('title demo', 'content demo') //匯入雲物件後就可以直接呼叫該物件的方法了,注意使用非同步await
uni.showToast({
title: '新增成功'
})
} catch (e) {}
如上,原來需要23行的程式碼,現在7行就可以搞定!
當然,這些UI策略都可以自定義。
- URL化
為了歷史相容考慮,雲物件同時提供了URL化方案。開發者仍然可以把一個雲物件轉換為一個http
的url
介面。
總結
使用雲物件帶來的諸多好處:
- 更清晰的邏輯
- 更精簡的程式碼
- 更少的協作成本(以及矛盾~)
- 客戶端呼叫時在ide裡有完善的程式碼提示,方法引數均可提示。(傳輸
json
可沒法在ide
裡提示) - 自動支援uniCloud響應體規範,方便錯誤攔截和統一處理
更多雲物件介紹,參見:https://uniapp.dcloud.net.cn/uniCloud/cloud-obj.html