本文作者:空空叶
背 景
快应用版本: 1020
在使用web组件进行快应用与网页间的通信的时候,有这几个发现:
1、运行'npm run watch'时会提示web组件不支持message事件,但实际上是支持的(只是提示上的小问题)
2、快应用发信息到网页,与网页发信息到快应用,用的方法名都是postMessage,但数据格式并不一致,使用起来不方便
3、消息会丢失(比如网页端消息处理函数还没执行到就给网页发信息等情况)
4、网页给快应用发信息时,快应用发送给网页的信息才会带回
特 点
因此,这里花了点力气,做了个辅助的库,特点:
1、消息有序发送
2、消息不会丢失,保证到达
3、消息不会接收多次(去重)
4、消息分类型,类似事件的监听机制,更加方便
快应用端代码
快应用里,新建一个channel.js文件,内容:
/**
* 通道,用来与html进行可靠的通信
*
* 1. 有序
* 2. 回传与ack机制,保证到达
* 3. id验证机制,去重
*
* @param thisObj {Object} this对象
* @param webId {String} web的id
* @param timeout {Number} 超时时间,超时后会重试,单位ms,默认1000
*/
var Channel = function (thisObj, webId, timeout) {
timeout = timeout || 1000
var that = this
var debug = (...msgs) => console.debug.apply(null, ['[通道]', ...msgs])
var log = (...msgs) => console.log.apply(null, ['[通道]', ...msgs])
var error = (...msgs) => console.error.apply(null, ['[通道]', ...msgs])
//消息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)
}
/**
* 收到消息时调用(设置web组件的message事件的处理函数)
*/
that.onMsg = function ({message, url}) {
debug('[收到消息]', message, '('+url+')')
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] || []
log('[处理消息]', '类型: '+type, '处理器数量: '+typeListeners.length)
for (var i=0;i<typeListeners.length;i++) {
try {
typeListeners[i](data)
} catch (e) {
error('[处理器错误]', e)
}
}
//响应ack
that.send_({
pType: 'ack',
id,
})
}
}else {//没有pType,当作ping包处理,忽略
return
}
}
/**
* 发包
*/
that.send_ = function (packet) {
var str = JSON.stringify(packet)
thisObj.$element(webId).postMessage({
message: str
})
debug('[发送消息]', 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!')
}
}
//计时器: 不断重试发送包
var timerId = setInterval(function () {
if (thisObj.$valid) {//仍然有效
that.next_()
}else {//取消计时器
log('已经失效,取消计时器')
clearInterval(timerId)
}
}, timeout)
}
module.exports = Channel
快应用端使用方式(参考):
<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、网页端的日志不好显示
相关阅读:
写在最后
在去年的开发者大赛征文中,我们通过多个社区联合活动收集了很多优质文章,有入坑指南、开源项目、开发模板、常见问题总结等多个方面,这些内容为很多开发者提供了参考,感谢大家的支持和参与,今年的我们的征文活动还在继续,感兴趣的开发者可以点阅读原文查看详情哦!
快应用生态平台
赋能开发者
拓展场景未来

快来关注我们吧

本文介绍了一种解决快应用与网页通信中消息丢失、格式不一致问题的方法,通过创建辅助库实现有序发送、确保消息到达且不重复。快应用端和网页端的代码示例分别展示如何使用此通信机制。
219





