写点什么

云对象 - 重新定义前后端交互

作者:Java-fenn
  • 2022 年 9 月 09 日
    湖南
  • 本文字数:3275 字

    阅读完需:约 11 分钟

背景

从 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 方法。

// 云对象名:newsmodule.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.jsmodule.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 化方案。开发者仍然可以把一个云对象转换为一个 http 的 url 接口。

总结

使用云对象带来的诸多好处:

  1. 更清晰的逻辑

  2. 更精简的代码

  3. 更少的协作成本(以及矛盾~)

  4. 客户端调用时在 ide 里有完善的代码提示,方法参数均可提示。(传输 json 可没法在 ide 里提示)

  5. 自动支持 uniCloud 响应体规范,方便错误拦截和统一处理

更多云对象介绍,参见: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj.html

用户头像

Java-fenn

关注

需要Java资料或者咨询可加我v : Jimbye 2022.08.16 加入

还未添加个人简介

评论

发布
暂无评论
云对象 - 重新定义前后端交互_Java_Java-fenn_InfoQ写作社区