HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

5+App和uni-app在App开发上的对比

评测

抛开uni-app可以开发多端,而5+App只能开发App不谈。本文只谈仅做App的情况下,uni-app的App和5+App有什么区别。

5+App是DCloud上一代产品,基于webview扩展的混合开发技术。
它的每个页面都是一个webview加载一个html页面,调用原生扩展能力时通过webview的桥通信实现。
5+App可以满足常规App的开发,并且基于html开发比较简单。但距离第一流互联网体验要求的App而言,5+App难以满足这种要求。

于是DCloud新出了uni-app。
uni-app引入了独立的jscore,逻辑层和视图层分离,并且视图层支持Webview渲染和nvue原生渲染的双渲染引擎,开发者可以做出更高性能和体验的App。

uni-app比5+App的改进具体包括:

引用独立的js引擎,将逻辑层和视图层分离,用多线程解决卡顿问题

webview是单线程的,不管多少个webview,它们的js跑在同一个线程里,会互相影响。js即运行逻辑,又操作页面渲染,经常会阻塞。

引入独立的js引擎(jscore)后,由单线程变成双线程。将逻辑层和视图层分离,逻辑层运行在jscore里,视图层仍然在webview里,可以大幅提升页面加载体验。

比如新页面动画进入时,在5+App里,需要先打开一个新webview,然后这个webview里的js代码去联网下载数据,下载数据后填充到页面,这样等待时间会很长。
但在uni-app里,打开新页面时,联网是在始终都存在的jscore里进行了,同时另一个线程打开了视图层的webview页面,它们双线程并行工作而不是串行,所以新页面内容加载非常快。

5+App要想加快新页面速度,往往需要做预载。这增加了内存占用和代码复杂度。uni-app无需预载,可以快速加载新页面。

当然引入独立的jscore也会有代价,就是Android包体积增加,一个Android的js引擎大约7M的体积。iOS由于系统自带jscore,不需要新增js引擎体积。

双线程还有一个负面问题是两个线程之间通信是需要时间的,高频通信不能像在webview里那样自由写代码,需要依靠uni-app提供的renderjs方案来解决这类问题。(react native也有这类阻塞问题,但它没有解决方案,不如uni-app)

支持原生渲染

5+App是webview渲染的,而uni-app是双渲染引擎可选。视图层可以用webview渲染,也可以用原生渲染。nvue原生渲染支持带来大量的好处,由于内容较多,本文不列举,请另行参考:nvue使用指南

原生插件系统改进和生态区别

5+App是基于Webview的js桥通信和原生能力对接的,这种做法,需要在原生层和js层编写大量代码才能互相对接起来。
uni-app是jscore和原生能力对接的,此时原生插件编写,主要是写原生代码,插件开发工作量大幅减少、插件使用也变的更加方便。
DCloud已经淘汰了5+App的原生插件扩展模式,并且为uni-app建立了插件市场,有几千款插件。
所以uni-app有丰富的原生插件生态,这对开发者很重要。选择5+App将无法享受这种生态。

底部选项卡的逻辑

5+App的启动逻辑,是先打开lauchWebview,然后由这个Webview加载页面内容和逻辑,包括创建底部选项卡。每个选项卡都是一个Webview,以父子Webview嵌套方式排版。整个应用的启动速度和内存占用都比较高。
在uni-app里,App启动时是原生先根据pages.json描述绘制底部选项卡,这个速度是非常快的。并同时打开jscore,jscore的启动时间也比Webview快很多。不同的选项卡的Webview不是父子嵌套,渲染压力和内存占用都要好很多。

软键盘优化

  • Android软键盘底部灰一下的问题
    5+App的软键盘,在Android上,弹出和收起时,底部会灰一下,因为键盘移动的速度很快,而Webview的大小调节速度没那么快。
    uni-app里则没有这个问题。
  • 软键盘右下角按钮自定义问题
    Webview里的软键盘,右下角按钮无法自定义为“发送”。uni-app通过提供nvue原生渲染解决了这个问题。

区域滚动长列表的体验和自定义下拉刷新问题

Webview的页面超长滚动没有问题,但区域想要长列表滚动就性能非常差了。
这需要uni-app的nvue里的list组件或recycle-list组件来解决。没有这种技术,很多优秀的交互做不出来,比如可吸顶、可左右滚动的区域长列表。
包括区域长列表的自定义下拉刷新,也无法在5+App的框架下给出高性能的方案,还是需要nvue的refresh组件。

同屏多Webview的内存消耗

5+App里经常需要做Webview的嵌套或同屏覆盖:

  1. 比如要盖住map等原生组件,就需要再创建一个webview去盖住它。
  2. 比如左右拖动的顶部选项卡+长列表,需要创建多个webview形成webview group才能保证体验。
    这些做法非常占用内存,并且屏幕渲染时要处理的逻辑非常多,导致渲染变慢。
    uni-app里完全没有同屏多webview并存的现象,它通过nvue来解决上述问题,一个nvue页面的内存占用要比一个webview的内存占用小太多了。

WKWebview的坑

由于Apple废弃了灵活的UIWebview,强推WKWebview,这对传统混合开发的应用造成了很大的限制。
WKWebview的跨域问题让人非常烦恼,内存不足时自动单页面崩溃(白屏)问题更是无解。
uni-app的js由于运行在jscore而不是WKWebview里,所以不存在这些问题。

uni统计

uni-app同时配套了一个强大的uni统计系统,https://tongji.dcloud.net.cn/
uni统计可以免打点统计非常多内容,是运维的好帮手。

技术支持和社区生态

uni-app有十几万人的官方QQ群/微信群,各个群都活跃。网络上的视频教程、经验分享也更多。
5+App的使用人数比uni-app少很多,交流群活跃程度、资料丰富度都不及uni-app。

5+App的优势

虽然uni-app优于5+App很多,但5+App也有一些优势:

  1. 入门简单:uni-app要求开发者必须掌握vue。而5+App只需要会html即可。
  2. 包体积小,Android不需要引入7M的独立js引擎。不管Android还是iOS都不需要引入1M的uni-app基础组件框架
  3. 5+app的Android最低版本支持到2.3、iOS最低支持7。而uni-app的Android最低是4.4、iOS最低9。事实上Android4.4是2013年发布的,更低版本的手机在市场上基本没有市占率了。

所以DCloud的建议是,除非你只会html但不会vue,也不想学vue,且对App的性能体验要求不高,不需要扩展原生能力,或者必须要支持Android4.4以下的手机,那么可以使用5+App。其他场景则应该使用uni-app。

继续阅读 »

抛开uni-app可以开发多端,而5+App只能开发App不谈。本文只谈仅做App的情况下,uni-app的App和5+App有什么区别。

5+App是DCloud上一代产品,基于webview扩展的混合开发技术。
它的每个页面都是一个webview加载一个html页面,调用原生扩展能力时通过webview的桥通信实现。
5+App可以满足常规App的开发,并且基于html开发比较简单。但距离第一流互联网体验要求的App而言,5+App难以满足这种要求。

于是DCloud新出了uni-app。
uni-app引入了独立的jscore,逻辑层和视图层分离,并且视图层支持Webview渲染和nvue原生渲染的双渲染引擎,开发者可以做出更高性能和体验的App。

uni-app比5+App的改进具体包括:

引用独立的js引擎,将逻辑层和视图层分离,用多线程解决卡顿问题

webview是单线程的,不管多少个webview,它们的js跑在同一个线程里,会互相影响。js即运行逻辑,又操作页面渲染,经常会阻塞。

引入独立的js引擎(jscore)后,由单线程变成双线程。将逻辑层和视图层分离,逻辑层运行在jscore里,视图层仍然在webview里,可以大幅提升页面加载体验。

比如新页面动画进入时,在5+App里,需要先打开一个新webview,然后这个webview里的js代码去联网下载数据,下载数据后填充到页面,这样等待时间会很长。
但在uni-app里,打开新页面时,联网是在始终都存在的jscore里进行了,同时另一个线程打开了视图层的webview页面,它们双线程并行工作而不是串行,所以新页面内容加载非常快。

5+App要想加快新页面速度,往往需要做预载。这增加了内存占用和代码复杂度。uni-app无需预载,可以快速加载新页面。

当然引入独立的jscore也会有代价,就是Android包体积增加,一个Android的js引擎大约7M的体积。iOS由于系统自带jscore,不需要新增js引擎体积。

双线程还有一个负面问题是两个线程之间通信是需要时间的,高频通信不能像在webview里那样自由写代码,需要依靠uni-app提供的renderjs方案来解决这类问题。(react native也有这类阻塞问题,但它没有解决方案,不如uni-app)

支持原生渲染

5+App是webview渲染的,而uni-app是双渲染引擎可选。视图层可以用webview渲染,也可以用原生渲染。nvue原生渲染支持带来大量的好处,由于内容较多,本文不列举,请另行参考:nvue使用指南

原生插件系统改进和生态区别

5+App是基于Webview的js桥通信和原生能力对接的,这种做法,需要在原生层和js层编写大量代码才能互相对接起来。
uni-app是jscore和原生能力对接的,此时原生插件编写,主要是写原生代码,插件开发工作量大幅减少、插件使用也变的更加方便。
DCloud已经淘汰了5+App的原生插件扩展模式,并且为uni-app建立了插件市场,有几千款插件。
所以uni-app有丰富的原生插件生态,这对开发者很重要。选择5+App将无法享受这种生态。

底部选项卡的逻辑

5+App的启动逻辑,是先打开lauchWebview,然后由这个Webview加载页面内容和逻辑,包括创建底部选项卡。每个选项卡都是一个Webview,以父子Webview嵌套方式排版。整个应用的启动速度和内存占用都比较高。
在uni-app里,App启动时是原生先根据pages.json描述绘制底部选项卡,这个速度是非常快的。并同时打开jscore,jscore的启动时间也比Webview快很多。不同的选项卡的Webview不是父子嵌套,渲染压力和内存占用都要好很多。

软键盘优化

  • Android软键盘底部灰一下的问题
    5+App的软键盘,在Android上,弹出和收起时,底部会灰一下,因为键盘移动的速度很快,而Webview的大小调节速度没那么快。
    uni-app里则没有这个问题。
  • 软键盘右下角按钮自定义问题
    Webview里的软键盘,右下角按钮无法自定义为“发送”。uni-app通过提供nvue原生渲染解决了这个问题。

区域滚动长列表的体验和自定义下拉刷新问题

Webview的页面超长滚动没有问题,但区域想要长列表滚动就性能非常差了。
这需要uni-app的nvue里的list组件或recycle-list组件来解决。没有这种技术,很多优秀的交互做不出来,比如可吸顶、可左右滚动的区域长列表。
包括区域长列表的自定义下拉刷新,也无法在5+App的框架下给出高性能的方案,还是需要nvue的refresh组件。

同屏多Webview的内存消耗

5+App里经常需要做Webview的嵌套或同屏覆盖:

  1. 比如要盖住map等原生组件,就需要再创建一个webview去盖住它。
  2. 比如左右拖动的顶部选项卡+长列表,需要创建多个webview形成webview group才能保证体验。
    这些做法非常占用内存,并且屏幕渲染时要处理的逻辑非常多,导致渲染变慢。
    uni-app里完全没有同屏多webview并存的现象,它通过nvue来解决上述问题,一个nvue页面的内存占用要比一个webview的内存占用小太多了。

WKWebview的坑

由于Apple废弃了灵活的UIWebview,强推WKWebview,这对传统混合开发的应用造成了很大的限制。
WKWebview的跨域问题让人非常烦恼,内存不足时自动单页面崩溃(白屏)问题更是无解。
uni-app的js由于运行在jscore而不是WKWebview里,所以不存在这些问题。

uni统计

uni-app同时配套了一个强大的uni统计系统,https://tongji.dcloud.net.cn/
uni统计可以免打点统计非常多内容,是运维的好帮手。

技术支持和社区生态

uni-app有十几万人的官方QQ群/微信群,各个群都活跃。网络上的视频教程、经验分享也更多。
5+App的使用人数比uni-app少很多,交流群活跃程度、资料丰富度都不及uni-app。

5+App的优势

虽然uni-app优于5+App很多,但5+App也有一些优势:

  1. 入门简单:uni-app要求开发者必须掌握vue。而5+App只需要会html即可。
  2. 包体积小,Android不需要引入7M的独立js引擎。不管Android还是iOS都不需要引入1M的uni-app基础组件框架
  3. 5+app的Android最低版本支持到2.3、iOS最低支持7。而uni-app的Android最低是4.4、iOS最低9。事实上Android4.4是2013年发布的,更低版本的手机在市场上基本没有市占率了。

所以DCloud的建议是,除非你只会html但不会vue,也不想学vue,且对App的性能体验要求不高,不需要扩展原生能力,或者必须要支持Android4.4以下的手机,那么可以使用5+App。其他场景则应该使用uni-app。

收起阅读 »

推荐小册课程《前端算法与数据结构面试》

小程序 HBuilder HTML5 uniapp

简明易懂的前端算法面试教学,接地气&说人话&大量图解,带你从编码基本功到手撕大厂真题、构建坚不可摧的算法能力体系。

前端算法与数据结构面试

简明易懂的前端算法面试教学,接地气&说人话&大量图解,带你从编码基本功到手撕大厂真题、构建坚不可摧的算法能力体系。

前端算法与数据结构面试

已存在待跳转页面...请不要连续多次跳转页面...

详细问题描述

页面在onShow中延迟启动扫码框,当延迟时间比较小的时候,bug出现:在开启扫码框后,点击返回,扫码框退出,返回到本页面,再次返回,返回主页面,在这时,无法再次进入页面。
但延迟时间大一点,这个问题就消失了。

async scanQrCode() {  
            let _this = this;  
            let status = await _this.checkPermission();  
            if (status !== 1) {  
                return;  
            }  
            uni.scanCode({  
                onlyFromCamera: true,  
                success: function(res) {  
                }  
            });  
        },  

onShow() {  
        //页面载入执行扫码  
        var _this = this;  
        setTimeout(function() {  
            _this.scanQrCode();  
        }, 300);  
    }
继续阅读 »

详细问题描述

页面在onShow中延迟启动扫码框,当延迟时间比较小的时候,bug出现:在开启扫码框后,点击返回,扫码框退出,返回到本页面,再次返回,返回主页面,在这时,无法再次进入页面。
但延迟时间大一点,这个问题就消失了。

async scanQrCode() {  
            let _this = this;  
            let status = await _this.checkPermission();  
            if (status !== 1) {  
                return;  
            }  
            uni.scanCode({  
                onlyFromCamera: true,  
                success: function(res) {  
                }  
            });  
        },  

onShow() {  
        //页面载入执行扫码  
        var _this = this;  
        setTimeout(function() {  
            _this.scanQrCode();  
        }, 300);  
    }
收起阅读 »

iOS上架被调查相关问题解答

iOS iOS打包

今年以来因为审核的加严,很多iOS开发者账号提交ipa上架审核,直接反馈账号被调查了,也没有具体APP原因。

反馈邮件翻译

Hello,

We are unable to continue this app’s review because your Apple Developer Program account is currently under investigation for not following the App Store Review Guidelines’ Developer Code of Conduct.

Common practices that may lead to an investigation include, but are not limited to:

你好

我们无法继续此应用的审核,因为您的Apple开发者计划帐户目前正在接受调查,因为其未遵循App Store审核指南的“开发人员行为准则”。

可能导致调查的常见做法包括但不限于:

1、 不准确地描述应用程序或服务

2、误导性应用内容

3、参与不真实的评级和评论操作

4、提供误导性的客户支持响应

5、在解决方案中心提供误导性回复

6、从事误导性采购或诱饵开关计划

7、在应用内部或外部参与其他不诚实或欺诈活动

在我们的调查过程中,我们不会审核您提交的任何应用。 完成调查后,我们将通过解决方案中心通知您。

我们目前不要求您提供任何其他信息,也不会分享任何其他详细信息。 我们感您在调查期间继续保持耐心。

最好的祝福

下面列举一些解答,了解具体怎么回事。

1、为什么会账号会调查?

答:可能提交的APP是代码相似,市场上有很多类似App,认为马甲包,或者APP本身有违规,也是因为苹果机器审核越来越严格的原因,比较容易触发!

2、账号调查了会持续多久?

答:时间不确定,被调查多数在一个月内出结果,也有几个月的。如果调查没问题,也可能直接给通过APP审核。

3、账号被调查了怎么办?

答:调查期间,不要操作,申诉和加急都无效,只能等待出结果。在被调查阶段,无论如何提交都是统一回复的,回复你账号被调查,一直到出现2个结果,账号调查才结束。

1)直接过审;

2)被拒绝,有明确拒绝条款;

4.一个App被调查了,会影响同账号下的其他App吗?

答:被调查邮件里虽然说不会审核,但是其实是针对这一个App的,其他 App 项目还是可以提交正常审核了。同一个APP就不要换账号,或者删除再重新提交审核了,等调查结束在操作,调查完可能直接给通过。

5.把被调查的App转移到另一个开发者账号重新提交,会有问题吗?

答:因为是针对App的调查,所以转移App大概率无效,并且可能会被判定重复提交。另外,不建议把正在审核的App删除,很有可能被苹果认为是恶意隐瞒,直接导致封号。

6.此次调查是机审还是人审?

答:基本确定是机审,且是查重审核。因为从整个事件来看,是在清理灰色地带的应用。可能你的各个App的功能完全不同,但很多基础功能都是复用统一代码模块,这样确实会节省很多成本,但现在看很大概率会被误判为重复应用。

  1. 正常通过调查之后,App会有啥影响,会被列为重点审核对象吗?

答:目前来看没啥影响,甚至可能已被标记为安全对象,审核异常顺利。

iOS上架交流社区

http://www.applicationloader.net/

继续阅读 »

今年以来因为审核的加严,很多iOS开发者账号提交ipa上架审核,直接反馈账号被调查了,也没有具体APP原因。

反馈邮件翻译

Hello,

We are unable to continue this app’s review because your Apple Developer Program account is currently under investigation for not following the App Store Review Guidelines’ Developer Code of Conduct.

Common practices that may lead to an investigation include, but are not limited to:

你好

我们无法继续此应用的审核,因为您的Apple开发者计划帐户目前正在接受调查,因为其未遵循App Store审核指南的“开发人员行为准则”。

可能导致调查的常见做法包括但不限于:

1、 不准确地描述应用程序或服务

2、误导性应用内容

3、参与不真实的评级和评论操作

4、提供误导性的客户支持响应

5、在解决方案中心提供误导性回复

6、从事误导性采购或诱饵开关计划

7、在应用内部或外部参与其他不诚实或欺诈活动

在我们的调查过程中,我们不会审核您提交的任何应用。 完成调查后,我们将通过解决方案中心通知您。

我们目前不要求您提供任何其他信息,也不会分享任何其他详细信息。 我们感您在调查期间继续保持耐心。

最好的祝福

下面列举一些解答,了解具体怎么回事。

1、为什么会账号会调查?

答:可能提交的APP是代码相似,市场上有很多类似App,认为马甲包,或者APP本身有违规,也是因为苹果机器审核越来越严格的原因,比较容易触发!

2、账号调查了会持续多久?

答:时间不确定,被调查多数在一个月内出结果,也有几个月的。如果调查没问题,也可能直接给通过APP审核。

3、账号被调查了怎么办?

答:调查期间,不要操作,申诉和加急都无效,只能等待出结果。在被调查阶段,无论如何提交都是统一回复的,回复你账号被调查,一直到出现2个结果,账号调查才结束。

1)直接过审;

2)被拒绝,有明确拒绝条款;

4.一个App被调查了,会影响同账号下的其他App吗?

答:被调查邮件里虽然说不会审核,但是其实是针对这一个App的,其他 App 项目还是可以提交正常审核了。同一个APP就不要换账号,或者删除再重新提交审核了,等调查结束在操作,调查完可能直接给通过。

5.把被调查的App转移到另一个开发者账号重新提交,会有问题吗?

答:因为是针对App的调查,所以转移App大概率无效,并且可能会被判定重复提交。另外,不建议把正在审核的App删除,很有可能被苹果认为是恶意隐瞒,直接导致封号。

6.此次调查是机审还是人审?

答:基本确定是机审,且是查重审核。因为从整个事件来看,是在清理灰色地带的应用。可能你的各个App的功能完全不同,但很多基础功能都是复用统一代码模块,这样确实会节省很多成本,但现在看很大概率会被误判为重复应用。

  1. 正常通过调查之后,App会有啥影响,会被列为重点审核对象吗?

答:目前来看没啥影响,甚至可能已被标记为安全对象,审核异常顺利。

iOS上架交流社区

http://www.applicationloader.net/

收起阅读 »

uni-app友盟和官方离线多渠道打包

uniapp离线打包 uni_app uniapp 教程 uni统计

公司要求用友盟但是官方不支持友盟多渠道打包,所以必须一个一个打,但是官方又有打包限制而且速度很慢,所以想到了利用反编译重新打包
实现原理:apkTool反编译资源>修改AndroidManifest.xml>重新打包>应用签名
适用平台:Window、依赖JDK
操作步骤:解压压缩包script.vbs、apktool_2.4.1.jar、apktool.bat三个文件
1、将你生成的apk文件放入解呀目录与上述三个文件同目录
2、用记事本打开script.vbs

channelList = array("oppo","baidu","huawei","xiaomi") //渠道列表可以自己按需求添加或者移除  
apkName = "app" //修改app为你apk对应的文件名(不带.apk后缀),如app.apk则填写app  
keystore = "D:\\test.keystore" //修改为你签名文件路径  
keystorePwd = "12345678" //签名文件密码  
keystoreAlias = "testalias" //签名文件别名

3、保存,双击script.vbs
4、等待生成app_oppo.apk等文件则结束
注意事项:在执行后会产生apk对应的文件夹,如果apk发生变化要删除此文件夹不然打包是之前的
链接: https://pan.baidu.com/s/1s6Z4TNlW0vhn1PXq8xk5kw提取码: kf2f

继续阅读 »

公司要求用友盟但是官方不支持友盟多渠道打包,所以必须一个一个打,但是官方又有打包限制而且速度很慢,所以想到了利用反编译重新打包
实现原理:apkTool反编译资源>修改AndroidManifest.xml>重新打包>应用签名
适用平台:Window、依赖JDK
操作步骤:解压压缩包script.vbs、apktool_2.4.1.jar、apktool.bat三个文件
1、将你生成的apk文件放入解呀目录与上述三个文件同目录
2、用记事本打开script.vbs

channelList = array("oppo","baidu","huawei","xiaomi") //渠道列表可以自己按需求添加或者移除  
apkName = "app" //修改app为你apk对应的文件名(不带.apk后缀),如app.apk则填写app  
keystore = "D:\\test.keystore" //修改为你签名文件路径  
keystorePwd = "12345678" //签名文件密码  
keystoreAlias = "testalias" //签名文件别名

3、保存,双击script.vbs
4、等待生成app_oppo.apk等文件则结束
注意事项:在执行后会产生apk对应的文件夹,如果apk发生变化要删除此文件夹不然打包是之前的
链接: https://pan.baidu.com/s/1s6Z4TNlW0vhn1PXq8xk5kw提取码: kf2f

收起阅读 »

图片压缩及转换base64

uni_app 5+App开发 base64

本教程适合APP,利用5+进行图片压缩,然后转换成base64格式文件。

uni.chooseImage({  
    sizeType: ['original'],  
    sourceType: ['camera'],  
    count: 1,  
    success: async (res) => {  
            // 加载框  
            uni.showLoading({  
                title: '图片压缩中。。。'  
            });  
            let path = res.tempFilePaths[0];  
            // 压缩图片  
            plus.zip.compressImage({    
                src:path,    
                dst:path,    
                overwrite:true,    
                quality:20,    
                width: '780px',      
                height:'1040px',      
                format: 'jpg'    
                },    
                function(res) {    
                    console.log("图片压缩完成");    
                    let imgPathUrl = res.target;   
                    let imgPathSize = res.size;  
                    console.log("图片链接:"+imgPathUrl)  
                    console.log("图片尺寸:"+imgPathSize)  
                    // 文件系统中的读取文件对象,用于获取文件的内容  
                    uni.showLoading({  
                        title: '图片转换中。。。'  
                    });  
                    let reader = new plus.io.FileReader();   
                    // 文件读取操作完成时的回调函数  
                    reader.onloadend = (fileData)=> {  
                        uni.hideLoading();  
                        console.log('文件读取完成!');      
                        let speech = fileData.target.result;//base64图片   
                        // 去除base64文件头  
                        let imgData = speech.replace(/^data:image\/\w+;base64,/, "");  

                    };      
                    reader.readAsDataURL(res.target);  
                },    
                function(error) {    
                    console.log("Compress error!",error);   
                    return;   

            });  
        },  
        fail:(err) => {  
            // 加载框  
            console.log(err)  
            uni.showToast({  
                icon: 'none',  
                duration:3000,  
                title: '拍照失败'  
            });  
        }  
    });
继续阅读 »

本教程适合APP,利用5+进行图片压缩,然后转换成base64格式文件。

uni.chooseImage({  
    sizeType: ['original'],  
    sourceType: ['camera'],  
    count: 1,  
    success: async (res) => {  
            // 加载框  
            uni.showLoading({  
                title: '图片压缩中。。。'  
            });  
            let path = res.tempFilePaths[0];  
            // 压缩图片  
            plus.zip.compressImage({    
                src:path,    
                dst:path,    
                overwrite:true,    
                quality:20,    
                width: '780px',      
                height:'1040px',      
                format: 'jpg'    
                },    
                function(res) {    
                    console.log("图片压缩完成");    
                    let imgPathUrl = res.target;   
                    let imgPathSize = res.size;  
                    console.log("图片链接:"+imgPathUrl)  
                    console.log("图片尺寸:"+imgPathSize)  
                    // 文件系统中的读取文件对象,用于获取文件的内容  
                    uni.showLoading({  
                        title: '图片转换中。。。'  
                    });  
                    let reader = new plus.io.FileReader();   
                    // 文件读取操作完成时的回调函数  
                    reader.onloadend = (fileData)=> {  
                        uni.hideLoading();  
                        console.log('文件读取完成!');      
                        let speech = fileData.target.result;//base64图片   
                        // 去除base64文件头  
                        let imgData = speech.replace(/^data:image\/\w+;base64,/, "");  

                    };      
                    reader.readAsDataURL(res.target);  
                },    
                function(error) {    
                    console.log("Compress error!",error);   
                    return;   

            });  
        },  
        fail:(err) => {  
            // 加载框  
            console.log(err)  
            uni.showToast({  
                icon: 'none',  
                duration:3000,  
                title: '拍照失败'  
            });  
        }  
    });
收起阅读 »

uniapp是准备走向哪里

吐槽一下,最近打算用uniapp开发一个Android pad + window(NWjs)的项目。
在开发过程中,遇到的坑不要太多了,感觉官方仅考虑支持手机+小程序,发现问题如下:

  1. 官方组件:input、button等多端不一致:mobile、pad、h5
  2. 编译环境问题频繁:没有提示任何错误,但Android端调试模式下,pages中配置的页面加载不了,没做任何修改,突然又可以了。
  3. dev模式下,访问 局域网地址(编译好后,会提供本地+局域网两个地址),该地址下的sockjs-node 访问的地址确是 localhost,意味着如果你是一台开发,一台预览,那么恩,热更新无效了
    我的建议
  • 丰富生态:插件市场中需要有人去跟进维护
  • 插件质量:优化现有组件,提高多端一致性(毕竟也会有人开发pad版本的),这部分甚至可以外包给第三方团队。
  • 专注于底层环境,放弃hbuilderX或若降低hbuilderX在公司的比重,加强cli模式下的开发体验,丰富cli模式下的更多的配置指导、说明文档,以便提供更多的灵活性。
继续阅读 »

吐槽一下,最近打算用uniapp开发一个Android pad + window(NWjs)的项目。
在开发过程中,遇到的坑不要太多了,感觉官方仅考虑支持手机+小程序,发现问题如下:

  1. 官方组件:input、button等多端不一致:mobile、pad、h5
  2. 编译环境问题频繁:没有提示任何错误,但Android端调试模式下,pages中配置的页面加载不了,没做任何修改,突然又可以了。
  3. dev模式下,访问 局域网地址(编译好后,会提供本地+局域网两个地址),该地址下的sockjs-node 访问的地址确是 localhost,意味着如果你是一台开发,一台预览,那么恩,热更新无效了
    我的建议
  • 丰富生态:插件市场中需要有人去跟进维护
  • 插件质量:优化现有组件,提高多端一致性(毕竟也会有人开发pad版本的),这部分甚至可以外包给第三方团队。
  • 专注于底层环境,放弃hbuilderX或若降低hbuilderX在公司的比重,加强cli模式下的开发体验,丰富cli模式下的更多的配置指导、说明文档,以便提供更多的灵活性。
收起阅读 »

承接uni-app开发、后端PHP开发

外包

PHP开发经验:6年
uni-app经验:2年
uni-app项目包括:搞笑社区、资讯、工具、商城、教育

说明:全职开发,长期接外包,速度与质量有保证

联系方式
微信:shalibaji_ge

PHP开发经验:6年
uni-app经验:2年
uni-app项目包括:搞笑社区、资讯、工具、商城、教育

说明:全职开发,长期接外包,速度与质量有保证

联系方式
微信:shalibaji_ge

2.6.11 V 3

更新到2.6.11, 会暴露很多以前没有的问题, 是不是规范语法要求了!!!! 不知道是新版本和v3编译器哪个的问题,降了版本2.6.9还是会有错 报错: [-1]$r[4][t0-0] is undefined at view.umd.min.js:1; [-1]$r[8][t0-0] is undefined at view.umd.min.js:1; 之前可以是undefined和null的地方 现在不可以; TypeError: undefined is not an object (evaluating ; 项目升级后用2.6.11的版本打包后, 运行时好多样式都错乱了, 晚上检查到凌晨呀,各种改; 用老版本打包一点问题没有;

继续阅读 »

更新到2.6.11, 会暴露很多以前没有的问题, 是不是规范语法要求了!!!! 不知道是新版本和v3编译器哪个的问题,降了版本2.6.9还是会有错 报错: [-1]$r[4][t0-0] is undefined at view.umd.min.js:1; [-1]$r[8][t0-0] is undefined at view.umd.min.js:1; 之前可以是undefined和null的地方 现在不可以; TypeError: undefined is not an object (evaluating ; 项目升级后用2.6.11的版本打包后, 运行时好多样式都错乱了, 晚上检查到凌晨呀,各种改; 用老版本打包一点问题没有;

收起阅读 »

Websocket直播间聊天室教程 - GoEasy快速实现聊天室

直播 WEBSOCKET

最近两年直播那个火啊,真的是无法形容!经常有朋友问起,我想实现一个直播间聊天或者我想开发一个聊天室, 要如何开始呢?
image

今天小编就手把手的教你用GoEasy做一个聊天室,当然也可以用于直播间内的互动。全套源码已经开源。

uniapp websocket聊天室体验demo:https://ext.dcloud.net.cn/plugin?id=5212

本教程主要目的是为大家介绍实现思路,为了确保本教程能帮助到使用不同前端技术的朋友,采用了HTML + JQuery的方式,后续还会推出Uniapp(vue/nvue)和小程序版本,大家可以持续关注。

我们这次要实现的聊天室,有两个界面,分别是:

  • 登录界面
  • 聊天室界面

    登录

    image

对于登录界面,我们期望:

  • 用户可以输入自己的昵称
  • 用户可以选择自己喜欢的头像
  • 用户可以选择进入不同的聊天室(直播间)

实现步骤

登录界面的实现,不用多说,因为真的是So Easy! 一个简单的界面,只包含三个简单的逻辑:

  • 验证是否输入昵称
  • 验证是否选择一个头像
  • 根据选择进入相应的聊天室

下边重点讲一下聊天室的实现。

聊天室(直播间)

image

当我们进入一个聊天室后,我们期望:

  • 用户能看到当前有多少用户在线,这个数字能够实时的更新
  • 用户能看到当前在线用户们的头像,而且能够实时的更新
  • 如果有用户进入或离开聊天室
    a. 聊天室会有“XXX进来了"或"XXX离开了"的提示
    b. 在线用户的数字和用户的头像列表会随之自动更新
  • 用户可以在聊天里发言
  • 用户可以发送道具:火箭或者比心

实现步骤

第一步:聊天室界面显示

1. 初始化:

当用户选择了一个聊天室,显示聊天室界面之前,我们首先要进行以下初始化工作:

  • 初始化当前用户currentUser,用户id,昵称,头像
  • 初始化当前聊天室ID: currentRoomId
  • 初始化GoEasy对象,注意一定要加上userId参数(可以是该用户的uuid或id等唯一标识,只有设置了userId的客户端在上下线时,才会触发上下线提醒)。同时需要将头像和昵称放入userData,当我们收到一个用户上线提醒的时候,我们需要知道这个用户的头像和昵称。
  • 初始化onlineUsers,onlineUsers是用来存放当前聊天室在线用户数和在线用户列表。 将当前聊天室Id (currentRoomId)作为channel,执行goEasy.hereNow查询此刻聊天室在线用户数和用户列表,赋值给onlineUsers。除了在进入聊天室的时候初始化onlineUsers,当有用户进入或离开时,也会动态的更新onlineUsers。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriber方法监听和接收聊天室新消息。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriberPresence监听用户进入和离开事件。

参考代码:service.js

//初始化聊天室  
this.joinRoom = function(userId,nickName, avatar, roomID) {  
        //初始化当前用户  
        this.currentUser = new User(userId, nickName, avatar);  
        //初始化当前聊天室id  
        this.currentRoomId = roomID;  
        //初始化goeasy,建立长连接  
        this.goeasy = new GoEasy({  
            host: "hangzhou.goeasy.io",  
            appkey: "您的appkey",  
            userId: this.currentUser.id,  
            userData: '{"nickname":"' + this.currentUser.nickname + '","avatar":"' + this.currentUser.avatar + '"}',  
            onConnected: function () {  
                console.log( "GoEasy connect successfully.")  
            },  
            onDisconnected: function () {  
                console.log("GoEasy disconnected.")  
            }  
        });  
        //查询当前在线用户列表,初始化onlineUsers对象  
        this.initialOnlineUsers();  
        //监听用户上下线提醒,实时更新onlineUsers对象  
        this.subscriberPresence();  
        //监听和接收新消息  
        this.subscriberNewMessage();  
};

2. 页面展示:

完成初始化之后,就跳转到直播间界面,在页面上显示以下数据:

  • 当前聊天室的名称
  • 聊天记录,并且显示聊天室界面
  • 展示聊天室界面

参考代码:controller.js

//页面切换到聊天室界面  
function showChatRoom() {  
    //更新房间名  
    $("#chatRoom-header").find(".current-chatRoom-name").text(loginCommand.roomName);  

    //加载聊天历史  
    var chatHistory = service.loadChatHistory();  
    chatHistory.forEach(function (item) {  
        //展示发送的消息  
        var otherPerson = createCurrentChatRoomPerson(item.senderNickname + ":", item.content)  
        $(".chatRoom-content-box").append($(otherPerson));  
    });  

    //隐藏登录界面  
    $(".chat-login-box").hide();  
    // //显示聊天界面  
    $(".chatRoom-box").show();  
    // //滑动到最后一行  
    scrollBottom();  
}

至此,我们已经完成了goeasy长连接的初始化,和一个聊天室静态展示。接下来,我们一起来看看如何让这个聊天室能够动起来。

第二步:聊天室互动

1. 实时更新在线用户数和头像列表

之前在service.initialOnlineUsers方法已经初始化onlineUsers对象,但聊天室随时都有用户进进出出,所以我们接下来还需要能够在有用户上线或下线的时候能够实时的更新onlineUsers,并且实时显示在页面上。
当我们收到一个用户上线提醒,我们将新上线的用户的信息存入在线用户对象onlineUsers里,当有用户离开时,在本地在线用户列表里删除。

参考代码:service.js

//监听用户上下线时间,维护onlineUsers对象  
this.subscriberPresence = function() {  
    var self = this;  
    this.goeasy.subscribePresence({  
        channel: this.currentRoomId,  
        onPresence: function(presenceEvents) {  
            presenceEvents.events.forEach(function(event) {  
                var userId = event.userId;  
                var count = presenceEvents.clientAmount;  
                //更新onlineUsers在线用户数  
                self.onlineUsers.count = count;  
                //如果有用户进入聊天室  
                if (event.action == "join" || event.action == "online") {  
                    var userData = JSON.parse(event.userData);  
                    var nickName = userData.nickname;  
                    var avatar = userData.avatar;  
                    var user = new User(userId, nickName, avatar);  
                    //将新用户加入onlineUsers列表  
                    self.onlineUsers.users.push(user);  
                    //触发界面的更新  
                    self.onJoinRoom(user.nickname, user.avatar);  
                } else {  
                    for (var i = 0; i < self.onlineUsers.users.length; i++) {  
                        var leavingUser = self.onlineUsers.users[i];  
                        if (leavingUser.id == userId) {  
                            var nickName = leavingUser.nickname;  
                            var avatar = leavingUser.avatar;  
                            //将离开的用户从onlineUsers中删掉  
                            self.onlineUsers.users.splice(i, 1);  
                            //触发界面的更新  
                            self.onLeaveRoom(nickName, avatar);  
                        }  
                    }  
                }  
            });  
        },  
        onSuccess : function () {  
            console.log("监听成功")  
        },  
        onFailed : function () {  
            console.log("监听失败")  
        }  
    });  
};

2. 发送消息

  • 初始化一个chatMessage对象,包含发送方id,昵称,消息内容,消息类型为chat
  • 将chatMessage转换为一个Json格式的字符串
  • 调用GoEasy的Publish方法,完成消息的发送

参考代码(service.js)

this.sendMessage = function(content) {  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.CHAT, content);  
    var self = this;  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("消息发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("消息发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

3. 接收和显示新消息/道具

之前我们已经在初始化页面的时候执行了service.subscriberNewMessage(),当我们收到一条消息时:

  • 根据消息类型判断是一条聊天消息,还是一个道具
  • 如果收到的是一条聊天消息,直接显示到界面
  • 如果是道具,就播放动画

参考代码(service.js)

//监听消息或道具  
this.subscriberNewMessage = function() {  
    var self = this;  
    this.goeasy.subscribe({  
        channel: this.currentRoomId, //替换为您自己的channel  
        onMessage: function(message) {  
            var chatMessage = JSON.parse(message.content);  
            //todo:事实上不推荐在前端收到时保存, 一个用户开多个窗口,会导致重复保存, 建议所有消息都是都在发送时在服务器端保存,这里只是为了演示  
            self.restapi.saveChatMessage(self.currentRoomId, chatMessage);  
            //如果收到的是一个消息,就显示为消息  
            if (chatMessage.type == MessageType.CHAT) {  
                var selfSent = chatMessage.senderUserId == self.currentUser.id;  
                var content = JSON.parse(message.content);  
                self.onNewMessage(chatMessage.senderNickname, content, selfSent);  
            }  
            //如果收到的是一个道具,就播放道具动画  
            if (chatMessage.type == MessageType.PROP) {  
                if (chatMessage.content == Prop.ROCKET) {  
                    self.onNewRocket(chatMessage.senderNickname);  
                }  
                if (chatMessage.content == Prop.HEART) {  
                    self.onNewHeart(chatMessage.senderNickname);  
                }  
            }  
        }  
    });  
};

4. 发送和接收并展示道具

其实和发送消息的实现几乎是一样的,具体代码请参考service.js的sendProp方法,controller.js的onNewHeart()方法。动画的播放,使用了TweenMax这个库,主要是为了展示一个实现思路,小编也不知道这个库是否有很好的兼容性,以及是否能够用在Uniapp和小程序下,知道的朋友可以留言分享给大家。

this.sendProp = function(prop) {  
    var self = this;  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.PROP, prop);  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("道具发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("道具发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

至此,一个聊天室就搞定了,是不是很简单?

如果阅读本文或开发中有任何问题,也欢迎在GoEasy官网(https://www.goeasy.io)添加GoEasy为好友,来获得更多技术支持。

GoEasy系列教程:

继续阅读 »

最近两年直播那个火啊,真的是无法形容!经常有朋友问起,我想实现一个直播间聊天或者我想开发一个聊天室, 要如何开始呢?
image

今天小编就手把手的教你用GoEasy做一个聊天室,当然也可以用于直播间内的互动。全套源码已经开源。

uniapp websocket聊天室体验demo:https://ext.dcloud.net.cn/plugin?id=5212

本教程主要目的是为大家介绍实现思路,为了确保本教程能帮助到使用不同前端技术的朋友,采用了HTML + JQuery的方式,后续还会推出Uniapp(vue/nvue)和小程序版本,大家可以持续关注。

我们这次要实现的聊天室,有两个界面,分别是:

  • 登录界面
  • 聊天室界面

    登录

    image

对于登录界面,我们期望:

  • 用户可以输入自己的昵称
  • 用户可以选择自己喜欢的头像
  • 用户可以选择进入不同的聊天室(直播间)

实现步骤

登录界面的实现,不用多说,因为真的是So Easy! 一个简单的界面,只包含三个简单的逻辑:

  • 验证是否输入昵称
  • 验证是否选择一个头像
  • 根据选择进入相应的聊天室

下边重点讲一下聊天室的实现。

聊天室(直播间)

image

当我们进入一个聊天室后,我们期望:

  • 用户能看到当前有多少用户在线,这个数字能够实时的更新
  • 用户能看到当前在线用户们的头像,而且能够实时的更新
  • 如果有用户进入或离开聊天室
    a. 聊天室会有“XXX进来了"或"XXX离开了"的提示
    b. 在线用户的数字和用户的头像列表会随之自动更新
  • 用户可以在聊天里发言
  • 用户可以发送道具:火箭或者比心

实现步骤

第一步:聊天室界面显示

1. 初始化:

当用户选择了一个聊天室,显示聊天室界面之前,我们首先要进行以下初始化工作:

  • 初始化当前用户currentUser,用户id,昵称,头像
  • 初始化当前聊天室ID: currentRoomId
  • 初始化GoEasy对象,注意一定要加上userId参数(可以是该用户的uuid或id等唯一标识,只有设置了userId的客户端在上下线时,才会触发上下线提醒)。同时需要将头像和昵称放入userData,当我们收到一个用户上线提醒的时候,我们需要知道这个用户的头像和昵称。
  • 初始化onlineUsers,onlineUsers是用来存放当前聊天室在线用户数和在线用户列表。 将当前聊天室Id (currentRoomId)作为channel,执行goEasy.hereNow查询此刻聊天室在线用户数和用户列表,赋值给onlineUsers。除了在进入聊天室的时候初始化onlineUsers,当有用户进入或离开时,也会动态的更新onlineUsers。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriber方法监听和接收聊天室新消息。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriberPresence监听用户进入和离开事件。

参考代码:service.js

//初始化聊天室  
this.joinRoom = function(userId,nickName, avatar, roomID) {  
        //初始化当前用户  
        this.currentUser = new User(userId, nickName, avatar);  
        //初始化当前聊天室id  
        this.currentRoomId = roomID;  
        //初始化goeasy,建立长连接  
        this.goeasy = new GoEasy({  
            host: "hangzhou.goeasy.io",  
            appkey: "您的appkey",  
            userId: this.currentUser.id,  
            userData: '{"nickname":"' + this.currentUser.nickname + '","avatar":"' + this.currentUser.avatar + '"}',  
            onConnected: function () {  
                console.log( "GoEasy connect successfully.")  
            },  
            onDisconnected: function () {  
                console.log("GoEasy disconnected.")  
            }  
        });  
        //查询当前在线用户列表,初始化onlineUsers对象  
        this.initialOnlineUsers();  
        //监听用户上下线提醒,实时更新onlineUsers对象  
        this.subscriberPresence();  
        //监听和接收新消息  
        this.subscriberNewMessage();  
};

2. 页面展示:

完成初始化之后,就跳转到直播间界面,在页面上显示以下数据:

  • 当前聊天室的名称
  • 聊天记录,并且显示聊天室界面
  • 展示聊天室界面

参考代码:controller.js

//页面切换到聊天室界面  
function showChatRoom() {  
    //更新房间名  
    $("#chatRoom-header").find(".current-chatRoom-name").text(loginCommand.roomName);  

    //加载聊天历史  
    var chatHistory = service.loadChatHistory();  
    chatHistory.forEach(function (item) {  
        //展示发送的消息  
        var otherPerson = createCurrentChatRoomPerson(item.senderNickname + ":", item.content)  
        $(".chatRoom-content-box").append($(otherPerson));  
    });  

    //隐藏登录界面  
    $(".chat-login-box").hide();  
    // //显示聊天界面  
    $(".chatRoom-box").show();  
    // //滑动到最后一行  
    scrollBottom();  
}

至此,我们已经完成了goeasy长连接的初始化,和一个聊天室静态展示。接下来,我们一起来看看如何让这个聊天室能够动起来。

第二步:聊天室互动

1. 实时更新在线用户数和头像列表

之前在service.initialOnlineUsers方法已经初始化onlineUsers对象,但聊天室随时都有用户进进出出,所以我们接下来还需要能够在有用户上线或下线的时候能够实时的更新onlineUsers,并且实时显示在页面上。
当我们收到一个用户上线提醒,我们将新上线的用户的信息存入在线用户对象onlineUsers里,当有用户离开时,在本地在线用户列表里删除。

参考代码:service.js

//监听用户上下线时间,维护onlineUsers对象  
this.subscriberPresence = function() {  
    var self = this;  
    this.goeasy.subscribePresence({  
        channel: this.currentRoomId,  
        onPresence: function(presenceEvents) {  
            presenceEvents.events.forEach(function(event) {  
                var userId = event.userId;  
                var count = presenceEvents.clientAmount;  
                //更新onlineUsers在线用户数  
                self.onlineUsers.count = count;  
                //如果有用户进入聊天室  
                if (event.action == "join" || event.action == "online") {  
                    var userData = JSON.parse(event.userData);  
                    var nickName = userData.nickname;  
                    var avatar = userData.avatar;  
                    var user = new User(userId, nickName, avatar);  
                    //将新用户加入onlineUsers列表  
                    self.onlineUsers.users.push(user);  
                    //触发界面的更新  
                    self.onJoinRoom(user.nickname, user.avatar);  
                } else {  
                    for (var i = 0; i < self.onlineUsers.users.length; i++) {  
                        var leavingUser = self.onlineUsers.users[i];  
                        if (leavingUser.id == userId) {  
                            var nickName = leavingUser.nickname;  
                            var avatar = leavingUser.avatar;  
                            //将离开的用户从onlineUsers中删掉  
                            self.onlineUsers.users.splice(i, 1);  
                            //触发界面的更新  
                            self.onLeaveRoom(nickName, avatar);  
                        }  
                    }  
                }  
            });  
        },  
        onSuccess : function () {  
            console.log("监听成功")  
        },  
        onFailed : function () {  
            console.log("监听失败")  
        }  
    });  
};

2. 发送消息

  • 初始化一个chatMessage对象,包含发送方id,昵称,消息内容,消息类型为chat
  • 将chatMessage转换为一个Json格式的字符串
  • 调用GoEasy的Publish方法,完成消息的发送

参考代码(service.js)

this.sendMessage = function(content) {  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.CHAT, content);  
    var self = this;  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("消息发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("消息发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

3. 接收和显示新消息/道具

之前我们已经在初始化页面的时候执行了service.subscriberNewMessage(),当我们收到一条消息时:

  • 根据消息类型判断是一条聊天消息,还是一个道具
  • 如果收到的是一条聊天消息,直接显示到界面
  • 如果是道具,就播放动画

参考代码(service.js)

//监听消息或道具  
this.subscriberNewMessage = function() {  
    var self = this;  
    this.goeasy.subscribe({  
        channel: this.currentRoomId, //替换为您自己的channel  
        onMessage: function(message) {  
            var chatMessage = JSON.parse(message.content);  
            //todo:事实上不推荐在前端收到时保存, 一个用户开多个窗口,会导致重复保存, 建议所有消息都是都在发送时在服务器端保存,这里只是为了演示  
            self.restapi.saveChatMessage(self.currentRoomId, chatMessage);  
            //如果收到的是一个消息,就显示为消息  
            if (chatMessage.type == MessageType.CHAT) {  
                var selfSent = chatMessage.senderUserId == self.currentUser.id;  
                var content = JSON.parse(message.content);  
                self.onNewMessage(chatMessage.senderNickname, content, selfSent);  
            }  
            //如果收到的是一个道具,就播放道具动画  
            if (chatMessage.type == MessageType.PROP) {  
                if (chatMessage.content == Prop.ROCKET) {  
                    self.onNewRocket(chatMessage.senderNickname);  
                }  
                if (chatMessage.content == Prop.HEART) {  
                    self.onNewHeart(chatMessage.senderNickname);  
                }  
            }  
        }  
    });  
};

4. 发送和接收并展示道具

其实和发送消息的实现几乎是一样的,具体代码请参考service.js的sendProp方法,controller.js的onNewHeart()方法。动画的播放,使用了TweenMax这个库,主要是为了展示一个实现思路,小编也不知道这个库是否有很好的兼容性,以及是否能够用在Uniapp和小程序下,知道的朋友可以留言分享给大家。

this.sendProp = function(prop) {  
    var self = this;  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.PROP, prop);  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("道具发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("道具发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

至此,一个聊天室就搞定了,是不是很简单?

如果阅读本文或开发中有任何问题,也欢迎在GoEasy官网(https://www.goeasy.io)添加GoEasy为好友,来获得更多技术支持。

GoEasy系列教程:

收起阅读 »