雲物件 - 重新定義前後端互動

崔紅保發表於2022-11-24

背景

從2000年開始,xml作為資料交換格式開始流行,伺服器拼接xml介面,客戶端js獲取xml內容,動態修改頁面。

幾年後,資料量更小的json替代了xml

移動網際網路到來後,因為客戶端分裂,加劇了介面的泛濫。

一轉眼,介面已經玩了20年了。其他技術飛速發展,而前後端互動卻一直是介面,沒有什麼創新。

js已經有了importexport,為什麼呼叫後端介面,不能像呼叫一個前端模組一樣呢?

serverless,讓這一切開始了變化。

亞馬遜lambda提出了雲函式的概念,不再使用restfulurl,但仍然是基於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過期,都可以統一攔截提示。

詳見uniCloud響應體規範

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策略都可以自定義。

  1. URL化

為了歷史相容考慮,雲物件同時提供了URL化方案。開發者仍然可以把一個雲物件轉換為一個httpurl介面。

總結

使用雲物件帶來的諸多好處:

  1. 更清晰的邏輯
  2. 更精簡的程式碼
  3. 更少的協作成本(以及矛盾~)
  4. 客戶端呼叫時在ide裡有完善的程式碼提示,方法引數均可提示。(傳輸json可沒法在ide裡提示)
  5. 自動支援uniCloud響應體規範,方便錯誤攔截和統一處理

更多雲物件介紹,參見:https://uniapp.dcloud.net.cn/uniCloud/cloud-obj.html

相關文章