在网页与快应用之间如何构造可靠通信通道?

本文作者:空空叶 
出处: bbs.quickapp.cn/forum.php?m…

背景

快应用版本: 1020

在使用web组件进行快应用与网页间的通信的时候,有这几个发现:

1、运行'npm run watch'时会提示web组件不支持message事件,但实际上是支持的(只是提示上的小问题)

2、快应用发信息到网页,与网页发信息到快应用,用的方法名都是postMessage,但数据格式并不一致,使用起来不方便

3、消息会丢失(比如网页端消息处理函数还没执行到就给网页发信息等情况)

4、网页给快应用发信息时,快应用发送给网页的信息才会带回

特点

因此,这里花了点力气,做了个辅助的库,特点:

1、消息有序发送

2、消息不会丢失,保证到达

3、消息不会接收多次(去重)

4、消息分类型,类似事件的监听机制,更加方便

扫描二维码关注公众号,回复: 6562528 查看本文章

快应用端代码

快应用里,新建一个channel.js文件,内容:

<template>
    <web id="web" src="xxx" @message="{{onMessage}}"></web>
</template>

<script>
    import Channel from './channel'

    export default {
        channel: null,
        onInit() {
            this.channel = new Channel(this, 'web')

            //通道监听
            this.channel.on('type1', function (data) {
                //处理收到的数据
            })

            //发送信息
            this.channel.sendMsg('type2', {
                //数据
            })
        },
        onMessage(param) {
            this.channel.onMsg(param)
        },
    }
</script>复制代码

网页端代码

网页端js(可以新建一个js或复制到script脚本里):

/**
 * (在html端,实际上一个页面只能new一个通道)
 *
 * 通道,用来与html进行可靠的通信
 *
 * 1. 有序
 * 2. 回传与ack机制,保证到达
 * 3. id验证机制,去重
 *
 * @param logger {Function} 日志记录器,可为null
 * @param timeout {Number} 超时时间,超时后会重试,单位ms,默认1000
 */
var Channel = function (logger, timeout) {
    timeout = timeout || 1000

    if (!logger) logger = () => {}

    var that = this

    //消息id生成器
    that.idGenerator = 0
    //消息发送队列
    that.sendQueue = [
        // {
        //     data: {},
        //     resolve: Object,
        // },
    ]
    //***列表
    that.listeners = {
        // type: [listener1, listener2],
    }

    //当前接收的消息id
    that.receivedId = 0

    /**
     * @param type {String} 消息类型
     * @param data {Object} 消息内容
     * @return {Promise}
     */
    that.sendMsg = function (type, data) {
        var resolve;
        var promise = new Promise(resolve_ => resolve = resolve_)
        that.sendQueue.push({
            data: {
                pType: 'msg',
                id: ++that.idGenerator,
                type: type,
                data: data,
            },
            resolve
        })
        return promise
    }

    /**
     * 监听
     * @param type 消息类型
     * @param callback 回调
     */
    that.on = function (type, callback) {
        var typeListeners = that.listeners[type] || []
        that.listeners[type] = typeListeners
        typeListeners.push(callback)
    }

    /**
     * 收到消息时调用(设置为onmessage的处理函数)
     */
    that.onMsg = function (message) {
        logger('[收到消息]' + message)
        var {pType, id, type, data} = JSON.parse(message)

        if (pType === 'ack') {//ack
            if (id === that.idGenerator) {//是当前的ack,有效,删除元素
                var nowPackets = that.sendQueue.splice(0, 1);
                that.valid_(nowPackets.length === 1)
                nowPackets[0].resolve()
            }
        } else if (pType === 'msg') {//正常消息
            if (id === that.receivedId + 1) {//是下个准备接收的包,有效
                //更新缓存值
                that.receivedId++
                //处理
                var typeListeners = that.listeners[type] || []
                logger('[处理消息]类型: ' + type + ' 处理器数量: ' + typeListeners.length)
                for (var i = 0; i < typeListeners.length; i++) {
                    try {
                        typeListeners[i](data)
                    } catch (e) {
                        logger('[处理异常]'+JSON.stringify(e))
                    }
                }
                //响应ack
                that.send_({
                    pType: 'ack',
                    id,
                })
            }
        } else {//没有pType,忽略
            return
        }
    }

    /**
     * 发包
     */
    that.send_ = function (packet) {
        var str = JSON.stringify(packet)
        system.postMessage(str)
        logger('[发送消息]' + str)
    }

    /**
     * 下个包
     */
    that.next_ = function () {
        if (that.sendQueue.length > 0) {
            that.send_(that.sendQueue[0].data)
        }
    }

    /**
     * 验证
     */
    that.valid_ = function (bool, errMsg) {
        if (!bool) {
            throw new Error(errMsg || 'Valid Fail!')
        }
    }

    //计时器: 不断重试发送包
    setInterval(function () {
        that.next_()
    }, timeout)

    //计时器: ping
    //(必须不断地ping,因此html不发请求到app,那么app发送给html的消息就不会过来,蛋疼)
    setInterval(function () {
        that.send_({})
    }, 200)

    //对接
    system.onmessage = that.onMsg
}
复制代码


网页端使用方法(参考):

var channel = new Channel()

//通道监听
channel.on('type1', function (data) {
    //处理收到的数据
})

//发送信息
channel.sendMsg('type2', {
    //数据
})
复制代码

最 后

1、快应用发信息到网页,官方文档写的信息格式是messageString,但这个在文档里并没说格式是怎样的,在官方的demo里可以看到注释的代码里格式是{message: 'xxx'}

2、网页端的日志不好显示


相关阅读:

一个前端攻城狮的快应用开发之路

快应用的用法和常见问题解答

快应用开发漫谈-部署与调试

快应用开发新手入门指南


写在最后

在去年的开发者大赛征文中,我们通过多个社区联合活动收集了很多优质文章,有入坑指南、开源项目、开发模板、常见问题总结等多个方面,这些内容为很多开发者提供了参考,感谢大家的支持和参与,今年的我们的征文活动还在继续,感兴趣的开发者可以点阅读原文查看详情哦!

在去年的开发者大赛征文中,我们通过多个社区联合活动收集了很多优质文章,有入坑指南、开源项目、开发模板、常见问题总结等多个方面,这些内容为很多开发者提供了参考,感谢大家的支持和参与,今年的我们的征文活动还在继续,感兴趣的开发者可以到以下查看详情 bbs.quickapp.cn/forum.php?m…


快应用生态平台——赋能开发者 拓展场景未来


转载于:https://juejin.im/post/5d0743a451882569821e5f56

猜你喜欢

转载自blog.csdn.net/weixin_34347651/article/details/93177877