uniapp H5端在异步回调中无法调用chooseImage
- uni.chooseImage 在手机浏览器中无法在异步回调中拉起;
- 可以在异步完成后调用uni.showModal 然后在showModal的success回调中再去调用uni.chooseImage可以拉起成功。
chooseImage: async function() {
let that = this;
if (this.imageList.length === 3) {
let isContinue = await this.isFullImg();
if (!isContinue) {
return;
}
}
uni.chooseImage({
sourceType: sourceType[that.sourceTypeIndex],
sizeType: sizeType[that.sizeTypeIndex],
count: that.imageList.length + that.count[that.countIndex] > 3 ? 3 - that.imageList.length : that.count[that.countIndex],
success: (res) => {
that.imageList = that.imageList.concat(res.tempFilePaths);
},
fail: (err) => {
}
})
},
isFullImg: function() {
let that = this;
return new Promise((res) => {
setTimeout(function() {
//如果我把uni.showModal这段代码注释掉替换成 res(true); ,uni.chooseImage 就不会在手机端的浏览器中调起来
uni.showModal({
content: "已经有3张图片了,是否清空现有图片?",
success: (e) => {
if (e.confirm) {
that.imageList = [];
res(true);
} else {
res(false)
}
},
fail: () => {
res(false)
}
})
},2000)
})
}
- uni.chooseImage 在手机浏览器中无法在异步回调中拉起;
- 可以在异步完成后调用uni.showModal 然后在showModal的success回调中再去调用uni.chooseImage可以拉起成功。
chooseImage: async function() {
let that = this;
if (this.imageList.length === 3) {
let isContinue = await this.isFullImg();
if (!isContinue) {
return;
}
}
uni.chooseImage({
sourceType: sourceType[that.sourceTypeIndex],
sizeType: sizeType[that.sizeTypeIndex],
count: that.imageList.length + that.count[that.countIndex] > 3 ? 3 - that.imageList.length : that.count[that.countIndex],
success: (res) => {
that.imageList = that.imageList.concat(res.tempFilePaths);
},
fail: (err) => {
}
})
},
isFullImg: function() {
let that = this;
return new Promise((res) => {
setTimeout(function() {
//如果我把uni.showModal这段代码注释掉替换成 res(true); ,uni.chooseImage 就不会在手机端的浏览器中调起来
uni.showModal({
content: "已经有3张图片了,是否清空现有图片?",
success: (e) => {
if (e.confirm) {
that.imageList = [];
res(true);
} else {
res(false)
}
},
fail: () => {
res(false)
}
})
},2000)
})
}
收起阅读 »
第一次接触uniapp 制作app 总结1
历时三个月 ,基本完成了公司的app项目!下面我我在使用uniapp时遇到的问题做个总结【可能并非全部】(其实只用了两个多月,后面app部分页面,及流程重做了)
1、项目开始时
其实项目开始时,页面已经用vue做了一半了,但是后来老大说可能要做成小程序,然后告诉了我这个框架《uniapp》花了近一个星期,边学边做又将vue上的项目转移到uniapp ,做开始遇到了几个小问题都是一些标签使用上的问题,基本多看几遍文档就能够解决!
2、修改BUG
在整个项目编写过程中,我将几个遇到的问题列举下:
1、注册(上传多张图片)
这个问题一共用了差不多一个多星期。
需求:用户注册需要填写信息,上传图片 1、身份证正反2、生活照(用来做人脸识别)3、权限证书(权限证书又分了三种对应三种权限)
这个需求,这么理解,三个权限可以组合,也就是权限1
单独的权限需要传1、身份证正 2、身份证反 3、生活照 4、权限证书
两个权限需要传1、身份证正 2、身份证反 3、生活照 4、权限证书1 5、权限证书2
三个权限都有的 1、身份证正 2、身份证反 3、生活照 4、权限证书1 5、权限证书2 6、权限证书3
在这里遇到的问题就是关于上传的问题,刚开始我没有使用uniapp内置的api 我直接调用的接口,后来发现传不了,需要用表单,但是页面结构都已经做好了,所以就放弃了这一种,然后就是百度,发现又uploadfile 这个api 然后就开始了 钻坑之旅
1.多张图片传不了?(files)
用了差不多一天多的时间发现多张图片传不了???一头雾水不是写的5+app吗?
因为花了太多的时间果断放弃!
- 将图片转未base64
又去百度搜索图片转base64的方法,最后发现转完图片太大了这个方法肯定不行,转完后的大小几十M到一两百M,好吧又果断放弃
3.然后有回到在页面添加标签form表单的方法
这个方法用了半天去改页面弄东西,后来发现,需要操作dom通过js去创建表单。。嫌弃太麻烦又放弃了!
4.最后还是用回了uploadfile
选来选去最后还是用回了这个内置的api,经过自己不断的学习(问人),发现自己少了个东西 {name:name,uri:图片地址}在一个不起眼的角落里还有一个files 的说明uri 和name 的问题如图
然后果断的加上,通过遍历一个一个的去加,加完以后发现可以了然后调用接口就完事儿了!!oh!mygod!(其实这当中,还有其它的问题,但是都是无关痛痒的,也都是自身的问题就不详细描述了)
2、导航标题
刚学的时候就知道有自定义导航,但是没当回事儿,只是看了一遍没咋注意!觉得就用官方给的就好了呀!后来才知道,还是自己太年轻!
在刚开始用官方给的导航没点问题,感觉也还不错!这个框架真好用!后来一个需求 ,我基本把所有页面的导航都换成了自定义导航!
需求:根据订单不同的状态在导航是否显示二维码跳转页! 在导航上让一个button消失??后来发现可以在上面进行操作(点击执行某个事件onNavigationBarButtonTap(e){})但是无法隐藏,好吧全部改成自定义
3、状态栏
在将导航改成自定义后发现状态栏不见了。果断百度后来知道了height: var(--status-bar-height);去设置状态栏等等。
历史三个月踩了很多坑,还收获了很多,在这里做已分享,可能还有很多的不足之处,希望大家提出宝贵意见!非常感激!又不懂的欢迎来问,直接加我qq就行。虽然我会的不是很多,但是还是希望大家共同进步!
历时三个月 ,基本完成了公司的app项目!下面我我在使用uniapp时遇到的问题做个总结【可能并非全部】(其实只用了两个多月,后面app部分页面,及流程重做了)
1、项目开始时
其实项目开始时,页面已经用vue做了一半了,但是后来老大说可能要做成小程序,然后告诉了我这个框架《uniapp》花了近一个星期,边学边做又将vue上的项目转移到uniapp ,做开始遇到了几个小问题都是一些标签使用上的问题,基本多看几遍文档就能够解决!
2、修改BUG
在整个项目编写过程中,我将几个遇到的问题列举下:
1、注册(上传多张图片)
这个问题一共用了差不多一个多星期。
需求:用户注册需要填写信息,上传图片 1、身份证正反2、生活照(用来做人脸识别)3、权限证书(权限证书又分了三种对应三种权限)
这个需求,这么理解,三个权限可以组合,也就是权限1
单独的权限需要传1、身份证正 2、身份证反 3、生活照 4、权限证书
两个权限需要传1、身份证正 2、身份证反 3、生活照 4、权限证书1 5、权限证书2
三个权限都有的 1、身份证正 2、身份证反 3、生活照 4、权限证书1 5、权限证书2 6、权限证书3
在这里遇到的问题就是关于上传的问题,刚开始我没有使用uniapp内置的api 我直接调用的接口,后来发现传不了,需要用表单,但是页面结构都已经做好了,所以就放弃了这一种,然后就是百度,发现又uploadfile 这个api 然后就开始了 钻坑之旅
1.多张图片传不了?(files)
用了差不多一天多的时间发现多张图片传不了???一头雾水不是写的5+app吗?
因为花了太多的时间果断放弃!
- 将图片转未base64
又去百度搜索图片转base64的方法,最后发现转完图片太大了这个方法肯定不行,转完后的大小几十M到一两百M,好吧又果断放弃
3.然后有回到在页面添加标签form表单的方法
这个方法用了半天去改页面弄东西,后来发现,需要操作dom通过js去创建表单。。嫌弃太麻烦又放弃了!
4.最后还是用回了uploadfile
选来选去最后还是用回了这个内置的api,经过自己不断的学习(问人),发现自己少了个东西 {name:name,uri:图片地址}在一个不起眼的角落里还有一个files 的说明uri 和name 的问题如图
然后果断的加上,通过遍历一个一个的去加,加完以后发现可以了然后调用接口就完事儿了!!oh!mygod!(其实这当中,还有其它的问题,但是都是无关痛痒的,也都是自身的问题就不详细描述了)
2、导航标题
刚学的时候就知道有自定义导航,但是没当回事儿,只是看了一遍没咋注意!觉得就用官方给的就好了呀!后来才知道,还是自己太年轻!
在刚开始用官方给的导航没点问题,感觉也还不错!这个框架真好用!后来一个需求 ,我基本把所有页面的导航都换成了自定义导航!
需求:根据订单不同的状态在导航是否显示二维码跳转页! 在导航上让一个button消失??后来发现可以在上面进行操作(点击执行某个事件onNavigationBarButtonTap(e){})但是无法隐藏,好吧全部改成自定义
3、状态栏
在将导航改成自定义后发现状态栏不见了。果断百度后来知道了height: var(--status-bar-height);去设置状态栏等等。
历史三个月踩了很多坑,还收获了很多,在这里做已分享,可能还有很多的不足之处,希望大家提出宝贵意见!非常感激!又不懂的欢迎来问,直接加我qq就行。虽然我会的不是很多,但是还是希望大家共同进步! 收起阅读 »
windows下生成p12证书的方法,用于ios开发
其实很简单,使用这个工具就可以生成了
https://www.yunedit.com/createcert
使用方法非常简单,也就是登录进去这个工具,创建证书,就可以像使用苹果电脑钥匙串访问那样创建一个csr文件了,还能在线生成p12证书
而且是免费的,非常好用
其实很简单,使用这个工具就可以生成了
https://www.yunedit.com/createcert
使用方法非常简单,也就是登录进去这个工具,创建证书,就可以像使用苹果电脑钥匙串访问那样创建一个csr文件了,还能在线生成p12证书
而且是免费的,非常好用
收起阅读 »阿里小程序IDE官方内嵌uni-app,为开发者提供多端开发服务
随着微信、阿里、百度、头条、QQ纷纷推出小程序,开发者的开发维护成本持续上升,负担过重。这点已经成为共识,现在连小程序平台厂商也充分意识到了。
阿里小程序团队,为了减轻开发者的负担,在官方的小程序开发者工具中整合了多端框架。
经过阿里团队仔细评估,uni-app 在产品完成度、跨平台支持度、开发者社区、可持续发展等多方面优势明显,最终选定 uni-app 内置于阿里小程序开发工具中,为开发者提供多端开发解决方案。
经过之前1个月的公测,10月10日,阿里小程序正式发布0.70版开发者工具,通过 uni-app 实现多端开发,成为本次版本更新的亮点功能!
如下图,在阿里小程序工具左侧主导航选择 uni-app,创建项目,即可开发。
阿里小程序开发工具更新说明详见:https://docs.alipay.com/mini/ide/0.70-stable
集成 uni-app,这对于阿里团队而言,并不是一个容易做出的决定。毕竟 uni-app 是一个三方产品,要经过复杂的评审流程。
这一方面突显出阿里团队以开发者需求为本的优秀价值观,另一方面也证明 uni-app 的产品确实过硬。
很多开发者都有多端需求,但又没有足够精力去了解、评估 uni-app,而处于观望态度。现在大家可以更放心的使用 uni-app 了,它没有让阿里失望,也不会让你失望。
自从 uni-app 推出以来,DCloud也取得了高速的发展,目前拥有370万开发者,框架运行在4.6亿手机用户设备上,月活达到1.35亿(仅包括部分接入DCloud统计平台的数据)。并且数据仍在高速增长中,在市场占有率上处于遥遥领先的位置。
本次阿里小程序工具集成 uni-app,会让 uni-app 继续快速爆发,取得更大的成功。
后续DCloud还将深化与阿里的合作,在serverless等领域给开发者提供更多优质服务。
使用多端框架开发各端应用,是多赢的模式。开发者减轻了负担,获得了更多新流量。而小程序平台厂商,也能保证自己平台上的各种应用可以被及时的更新。
DCloud欢迎更多小程序平台厂商,与我们一起合作,为开发者、平台、用户的多赢而努力。
进一步了解uni-app,详见:https://uniapp.dcloud.io
欢迎扫码关注DCloud公众号,转发消息到朋友圈。
随着微信、阿里、百度、头条、QQ纷纷推出小程序,开发者的开发维护成本持续上升,负担过重。这点已经成为共识,现在连小程序平台厂商也充分意识到了。
阿里小程序团队,为了减轻开发者的负担,在官方的小程序开发者工具中整合了多端框架。
经过阿里团队仔细评估,uni-app 在产品完成度、跨平台支持度、开发者社区、可持续发展等多方面优势明显,最终选定 uni-app 内置于阿里小程序开发工具中,为开发者提供多端开发解决方案。
经过之前1个月的公测,10月10日,阿里小程序正式发布0.70版开发者工具,通过 uni-app 实现多端开发,成为本次版本更新的亮点功能!
如下图,在阿里小程序工具左侧主导航选择 uni-app,创建项目,即可开发。
阿里小程序开发工具更新说明详见:https://docs.alipay.com/mini/ide/0.70-stable
集成 uni-app,这对于阿里团队而言,并不是一个容易做出的决定。毕竟 uni-app 是一个三方产品,要经过复杂的评审流程。
这一方面突显出阿里团队以开发者需求为本的优秀价值观,另一方面也证明 uni-app 的产品确实过硬。
很多开发者都有多端需求,但又没有足够精力去了解、评估 uni-app,而处于观望态度。现在大家可以更放心的使用 uni-app 了,它没有让阿里失望,也不会让你失望。
自从 uni-app 推出以来,DCloud也取得了高速的发展,目前拥有370万开发者,框架运行在4.6亿手机用户设备上,月活达到1.35亿(仅包括部分接入DCloud统计平台的数据)。并且数据仍在高速增长中,在市场占有率上处于遥遥领先的位置。
本次阿里小程序工具集成 uni-app,会让 uni-app 继续快速爆发,取得更大的成功。
后续DCloud还将深化与阿里的合作,在serverless等领域给开发者提供更多优质服务。
使用多端框架开发各端应用,是多赢的模式。开发者减轻了负担,获得了更多新流量。而小程序平台厂商,也能保证自己平台上的各种应用可以被及时的更新。
DCloud欢迎更多小程序平台厂商,与我们一起合作,为开发者、平台、用户的多赢而努力。
进一步了解uni-app,详见:https://uniapp.dcloud.io
欢迎扫码关注DCloud公众号,转发消息到朋友圈。
使用uni-app开发小程序,比直接原生开发小程序好在哪里?
小程序原生开发有不少槽点:
- 原生wxml开发对Node、预编译器、webpack支持不好,影响开发效率和工程构建流程。所以大公司都会用框架开发
- 微信定义的这套语法,wxml、wxs,以及wx:if等语法,私有化太强。不如正经学vue,学会了全端通用,而不是只为微信小程序
- vue生态里有太多周边工具,可以提高开发效率,比如ide、校验器、三方库。。。而微信的开发者工具和专业编辑器相比实在不好用,个性化设置也非常少
作为前端工程师,除了微信小程序,还要开发web、其他小程序甚至App,人们不喜欢来回切换开发工具和变更语法思考方式。
uni-app自然可以解决这些问题,但开发者又经常有些顾虑:
- 怕使用uni-app后,微信小程序里有的功能无法实现,受制于uni-app的更新
- 怕性能不如原生WXML
- 怕框架不成熟,跳到坑里
- 担心社区生态不完善
本文从开发者关心的功能、性能、学习门槛、开发体验、生态、可扩展性等维度,逐个分析对比,给予说明。
1.功能实现
开发者最常问的问题:如果小程序迭代升级,新增了一批API,但uni-app
框架未及时更新,该怎么办?
其实这是误解,uni-app
不限制底层API 调用;在小程序端,uni-app
支持直接编写微信原生代码。
类比传统web开发,如果vue、react等框架的使用,造成开发者无法操作浏览器提供的所有api,那这样的框架肯定是不成熟的。小程序开发也一样,uni-app
框架中,同样可调用微信提供的所有原生API。
故如果存在某些API(平台特有或新增API),uni-app
尚未封装,开发者可直接在uni-app
中编写微信原生API,即wx.开头的各种API。
举个例子,目前uni-app
虽然尚未封装跨平台的广告(ad)组件,但开发者在小程序端依然可以使用微信<ad>
组件来展现广告,代码示例如下:
<view>
<view>微信公众号关注组件</view>
<view>
<!-- uni-app未封装,但可直接使用微信原生的official-account组件-->
<official-account></official-account>
</view>
</view>
包括微信小程序自定义组件、WXS、云开发这些复杂用法,在uni-app里一样全面支持。尤其是wxs,目前在各种小程序开发框架里,也只有uni-app支持。
所以,结论是:使用uni-app
框架开发,在功能上和原生小程序开发没有区别,不会有任何限制。
2. 性能体验
开发者常问的第二个问题:三方框架,内部大多做了层层封装,这些封装是否会增加运行负载,导致性能下降?
同样是多虑了,uni-app
不会导致性能下载,甚至对很多环节做了自动优化,很多场景下性能体验比微信原生开发更好。
类似使用vue.js开发web,不但不会造成性能比原生js差,反而由于虚拟dom和差量更新技术的运用,在大多数场景下,比开发者手动写代码操作dom的性能还好。
小程序中需要频繁的写setData代码来更新数据,这里很重要的就是差量数据更新。如果不做差量,代码性能不好,如果每处逻辑都判断差量数据更新,那代码写起来太麻烦了。
使用uni-app
,底层自动差量数据更新,简单而高性能。
我们从优化理论、实测数据两个维度来仔细说明。
2.1 理论:框架优化方案
为提高性能体验,小程序从架构设计层面做了很多工作:
- 逻辑层、视图层分离,避免JS运算阻塞视图渲染
- 单独定义组件标签(wxml),减少DOM复杂度
- 精简样式(wxss),提升渲染性能
- 复杂组件原生化(video/map等),解决web组件的功能/体验缺失
通过这些规范约束,大幅提升了小程序的整体性能体验,但依然存在不少性能坑点,其中以setData
最为频繁普遍。
这里引用微信官方的描述,简单介绍一下setData
背后的工作原理:
小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的 evaluateJavascript 所实现。
为简化开发,微信将evaluateJavascript
调用封装成了setData
JS方法,实现视图层和逻辑层的数据传输,数据流示意图如下:
setData
的执行会受到很多因素的影响,setData
每次传递数据量过大或频繁被调用(见微信官方介绍),都可能引发性能体验问题。
幸运的是,uni-app
在这两个方面都有优化。
2.1.1 减少 setData 传递数据量
假设当前页面有一个列表(初始值为a,b,c,d
),现在要向列表后追加4个新列表项(e,f,g,h
),我们分别以微信原生、uni-app 两种模式编写代码。
小程序原生代码:
page({
data:{
list:['a','b','c','d']
},
change:function(){
let newData = ['e','f','g','h'];
this.data.list.push(...newData);
this.setData({
list:this.data.list
})
}
})
如上微信原生代码,change
方法执行时,会将list
中的a,b,c,d,e,f,g,h
8个列表项通过setData
全部传输过去。
uni-app 代码:
export default{
data(){
return {
list:['a','b','c','d']
}
},
methods:{
change:function(){
let newData = ['e','f','g','h'];
this.list.push(...newData)
}
}
}
如上uni-app
代码,change
方法执行时,仅会将list
中的e,f,g,h
4个新增列表项传输过去,实现了setData
传输量的极简化。
uni-app
借鉴了 westore JSON Diff库,在调用setData
之前,会先比对历史数据,精确、高效计算出有变化的差量数据,然后再调用setData
,仅传输变化的数据,这样就实现 setData 传递数据量的最小化,大幅提高通讯性能。
Tips:也许有些同学对传递数据从a,b,c,d,e,f,g,h
8个列表项优化为e,f,g,h
4个列表项,不以为然,但我们提醒,不要小看这个机制,上述只是demo示例。
- 在实际列表场景中,每个列表项可能包含缩略图、标题、摘要、时间等各种信息,每个列表项数据都会更大(假设为1k);
- 假设当前页面有20个列表项,连续上拉4次后,页面变成100条记录;如果再次上拉,页面变成120条记录时,情况会有不同
- 上述微信原生的方式,将120条记录数据(120k)全部传输过去
- 上述 uni-app 模式,仅会将新增的20条(101 ~ 120)记录数据(20k)传输过去,数据量是原生方式的1/6!
- 当页面列表项数据越多,这个差别就越大,页面有200条记录时,uni-app传递数据量会变成微信原生数据传递量的1/10!
2.1.2 减少 setData 调用频次
假设我们有更改多个变量值的需求,我们分别以微信原生、uni-app 两种模式编写代码。
小程序原生代码:
change:function(){
this.setData({a:1});
this.setData({b:2});
this.setData({c:3});
this.setData({d:4});
}
如上四次调用setData
,就会引发4次逻辑层、视图层数据通讯
uni-app 代码:
change:function(){
this.a = 1;
this.b = 2;
this.c = 3;
this.d = 4;
}
如上uni-app
的代码,最后会被合并成{"a":1,"b":2,"c":3,"d":4}
一条数据,然后仅调用一次setData
完成所有数据传递,大幅降低了setData
的调用频次。
uni-app
之所以有这样的优势,是因为 uni-app 基于 Vue Runtime 深度定制实现,并借助了 Vue 的 nextTick 机制。
2.2 实测:性能对比数据
有了如上的理论分析,我们接着进行真机实测,用数据来对比。
测试模型如下:
- 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
仿微博的列表是一个包含很多组件的列表,这种复杂列表对性能的压力更大,很适合做性能测试。
-
界面如下:
-
开发版本:使用微信原生、uni-app分别开发两套代码,uni-app使用
cli
方式默认安装。 -
测试代码开源(Github仓库地址:https://github.com/dcloudio/test-framework),
Tips:若有同学觉得测试代码写法欠妥,欢迎提交 PR 或 Issus,本项目下还有其它框架的测试代码,开发者可忽略 -
测试机型:红米 Redmi 6 Pro、MIUI 10.2.2.0 稳定版(最新版)、微信版本 7.0.3(最新版)
-
测试环境:每个框架开始测试前,杀掉各App进程、清空内存,保证测试机环境基本一致;每次从本地读取静态数据,屏蔽网络差异。
从触发上拉加载到数据更新、页面渲染完成,需要准确计时。人眼视觉计时肯定不行,我们采用程序埋点的方式,制定了如下计时时机:
- 计时开始时机:交互事件触发,框架赋值之前,如:上拉加载(onReachBottom)函数开头
- 计时结束时机:页面渲染完毕(微信setData回调函数开头)
Tips:setData
回调函数开头可认为是页面渲染完成的时间,是因为微信setData
定义如下(微信规范):
字段 | 类型 | 必填 | 描述 |
---|---|---|---|
data | Object | 是 | 这次要改变的数据 |
callback | Function | 否 | setData引起的界面更新渲染完毕后的回调函数 |
测试方式:从页面空列表开始,通过程序自动触发上拉加载,每次新增20条列表,记录单次耗时;固定间隔连续触发 N 次上拉加载,使得页面达到 20*N 条列表,计算这 N 次触发上拉到渲染完成的平均耗时。
测试结果如下:
列表条数 | 微信原生 | uni-app |
---|---|---|
200 | 770 | 641 |
400 | 876 | 741 |
600 | 1111 | 910 |
800 | 1406 | 1113 |
1000 | 1690 | 1321 |
说明:以400条微博列表为例,从页面空列表开始,每隔1秒触发一次上拉加载(新增20条微博),记录单次耗时,触发20次后停止(页面达到400条微博),计算这20次的平均耗时,结果微信原生在这20次 触发上拉 -> 渲染完成
的平均耗时为876毫秒,uni-app
是741毫秒。
这个数据,可能违反了很多人的直觉,uni-app 的性能竟然比微信原生还好!
当然,使用微信原生开发,也可以自己单独写代码优化setData。但每处业务都编写太多判断是不现实的,自然是用框架更舒心。
这个结果,和web开发类似,web开发也有原生js开发、vue、react框架等情况。如果不做特殊优化,原生js写的网页,性能经常还不如vue、react框架的性能。
也恰恰是因为Vue
、react
框架的优秀,性能好,开发体验好,所以原生js开发已经逐渐减少使用了。
3.社区生态
3.1 周边轮子
小程序是脱离web自造生态,很多web生态中轮子无法使用。
微信小程序还是有周边生态的,而其他几家小程序平台的生态基本没建起来。
uni-app
的周边生态非常丰富,在插件市场有近数千个插件,详见 ext.dcloud.net.cn。
首先uni-app
兼容小程序的生态,各种自定义组件均可直接引入使用。在此基础上,uni-app
的插件市场,有更多vue组件,同时可跨多端使用,并且性能优秀。
这使得uni-app
的生态成为最丰富的小程序开发生态。
比如富文本解析、图表、自定义下拉刷新等组件,uni-app
的插件性能均超过了wxparse、wx-echart等微信小程序组件。包括 uni ui等ui库的性能也超过了vant、iview等ui库的小程序版。
如果开发者需要丰富和高性能的组件,更应该使用uni-app
,而不是原生小程序开发。
3.2 活跃的QQ/微信群和论坛
uni-app
官方有 70 个开发者QQ/微信交流群(大多2千人群,近10万开发者),三方群更多。
问答社区,每天有数百篇帖子。活跃度与微信小程序官方论坛相同,远超过其他小程序官方论坛。
uni-app
三方培训活跃,腾讯课堂官方都为uni-app制作了课程,各种培训网站到处可见免费或收费的uni-app培训视频教程。
4.学习门槛、开发体验
首先微信原生的开发语法,既像React
,又像Vue
,有点不伦不类,对于开发者来说,等于又要学习一套新的语法,大幅提升了学习成本,这一直被大家所诟病。
uni-app
则对开发者更为友好,简单来说是 vue的语法 + 小程序的js api。
它遵循Vue.js
语法规范,组件和API遵循微信小程序命名
,这些都属于通用技术栈,学习它们是前端必备技能,uni-app
没有太多额外学习成本。
有一定 Vue.js 和微信小程序开发经验的开发者可快速上手 uni-app
。
没学过vue的同学,也不用掌握vue的全部,只需了解vue基础语法、数据绑定、列表渲染、组件等,其他如路由、loader、cli、node.js、webpack并不需要学。
因为HBuilderX工具搭配uni-app
可以免终端开发,可视化创建项目、可视化安装组件和扩展编译器,也就是uni-app
的学习门槛,比web开发的vue.js还低。
开发体验层面,微信原生开发相比uni-app
有较大差距,主要体现在:
- 更为强大的组件化开发能力:vue的组件开发比小程序自定义组件开发的体验要好很多
- 应用状态管理:uni-app支持vuex
- 使用 Sass 等 CSS 预处理器
- 完整的 ES Next 语法支持
- 自定义构建策略
开发工具维度,差距更大:
- 微信开发者工具被吐槽无数
uni-app
的出品公司,同时也是HBuilder的出品公司,DCloud.io。HBuilder/HBuilderX系列是四大主流前端开发工具(可对比百度指数),其为uni-app
做了很多优化,故uni-app
的开发效率、易用性非微信原生开发可及。
这里可以输出一个结论:如果你需要工程化能力,那就直接忘了微信原生开发吧。
5.未来扩展性
虽然当前产品仅要求发布到微信小程序,但阿里、百度、字节跳动、QQ、快应用等众多平台的流量越来越多,覆盖这些平台是迟早要考虑的事情。
此时,uni-ap
的跨端功能将成为程序员的自救神器,基于uni-app
开发的小程序,无需修改,即可同时发布到多家小程序,甚至App、H5平台。这不是梦想,而是现实。大家可依次扫描如下8个二维码,亲自体验最全面的跨平台效果!。
6.结语
uni-app | 微信 | |
---|---|---|
功能 | 相同 | 相同 |
性能 | 常规场景更优 | 需要自己编写复杂代码才能提高性能 |
社区生态 | 丰富,更多高性能组件 | 丰富 |
开发体验 | 纯vue体验,高效、统一;工程化能力强 | 语法私有化;工程化能力弱 |
多端能力 | 同时支持H5、多家小程序、跨平台App | 只能用于微信小程序 |
结论:只开发微信小程序,也应该使用uni-app
小程序原生开发有不少槽点:
- 原生wxml开发对Node、预编译器、webpack支持不好,影响开发效率和工程构建流程。所以大公司都会用框架开发
- 微信定义的这套语法,wxml、wxs,以及wx:if等语法,私有化太强。不如正经学vue,学会了全端通用,而不是只为微信小程序
- vue生态里有太多周边工具,可以提高开发效率,比如ide、校验器、三方库。。。而微信的开发者工具和专业编辑器相比实在不好用,个性化设置也非常少
作为前端工程师,除了微信小程序,还要开发web、其他小程序甚至App,人们不喜欢来回切换开发工具和变更语法思考方式。
uni-app自然可以解决这些问题,但开发者又经常有些顾虑:
- 怕使用uni-app后,微信小程序里有的功能无法实现,受制于uni-app的更新
- 怕性能不如原生WXML
- 怕框架不成熟,跳到坑里
- 担心社区生态不完善
本文从开发者关心的功能、性能、学习门槛、开发体验、生态、可扩展性等维度,逐个分析对比,给予说明。
1.功能实现
开发者最常问的问题:如果小程序迭代升级,新增了一批API,但uni-app
框架未及时更新,该怎么办?
其实这是误解,uni-app
不限制底层API 调用;在小程序端,uni-app
支持直接编写微信原生代码。
类比传统web开发,如果vue、react等框架的使用,造成开发者无法操作浏览器提供的所有api,那这样的框架肯定是不成熟的。小程序开发也一样,uni-app
框架中,同样可调用微信提供的所有原生API。
故如果存在某些API(平台特有或新增API),uni-app
尚未封装,开发者可直接在uni-app
中编写微信原生API,即wx.开头的各种API。
举个例子,目前uni-app
虽然尚未封装跨平台的广告(ad)组件,但开发者在小程序端依然可以使用微信<ad>
组件来展现广告,代码示例如下:
<view>
<view>微信公众号关注组件</view>
<view>
<!-- uni-app未封装,但可直接使用微信原生的official-account组件-->
<official-account></official-account>
</view>
</view>
包括微信小程序自定义组件、WXS、云开发这些复杂用法,在uni-app里一样全面支持。尤其是wxs,目前在各种小程序开发框架里,也只有uni-app支持。
所以,结论是:使用uni-app
框架开发,在功能上和原生小程序开发没有区别,不会有任何限制。
2. 性能体验
开发者常问的第二个问题:三方框架,内部大多做了层层封装,这些封装是否会增加运行负载,导致性能下降?
同样是多虑了,uni-app
不会导致性能下载,甚至对很多环节做了自动优化,很多场景下性能体验比微信原生开发更好。
类似使用vue.js开发web,不但不会造成性能比原生js差,反而由于虚拟dom和差量更新技术的运用,在大多数场景下,比开发者手动写代码操作dom的性能还好。
小程序中需要频繁的写setData代码来更新数据,这里很重要的就是差量数据更新。如果不做差量,代码性能不好,如果每处逻辑都判断差量数据更新,那代码写起来太麻烦了。
使用uni-app
,底层自动差量数据更新,简单而高性能。
我们从优化理论、实测数据两个维度来仔细说明。
2.1 理论:框架优化方案
为提高性能体验,小程序从架构设计层面做了很多工作:
- 逻辑层、视图层分离,避免JS运算阻塞视图渲染
- 单独定义组件标签(wxml),减少DOM复杂度
- 精简样式(wxss),提升渲染性能
- 复杂组件原生化(video/map等),解决web组件的功能/体验缺失
通过这些规范约束,大幅提升了小程序的整体性能体验,但依然存在不少性能坑点,其中以setData
最为频繁普遍。
这里引用微信官方的描述,简单介绍一下setData
背后的工作原理:
小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的 evaluateJavascript 所实现。
为简化开发,微信将evaluateJavascript
调用封装成了setData
JS方法,实现视图层和逻辑层的数据传输,数据流示意图如下:
setData
的执行会受到很多因素的影响,setData
每次传递数据量过大或频繁被调用(见微信官方介绍),都可能引发性能体验问题。
幸运的是,uni-app
在这两个方面都有优化。
2.1.1 减少 setData 传递数据量
假设当前页面有一个列表(初始值为a,b,c,d
),现在要向列表后追加4个新列表项(e,f,g,h
),我们分别以微信原生、uni-app 两种模式编写代码。
小程序原生代码:
page({
data:{
list:['a','b','c','d']
},
change:function(){
let newData = ['e','f','g','h'];
this.data.list.push(...newData);
this.setData({
list:this.data.list
})
}
})
如上微信原生代码,change
方法执行时,会将list
中的a,b,c,d,e,f,g,h
8个列表项通过setData
全部传输过去。
uni-app 代码:
export default{
data(){
return {
list:['a','b','c','d']
}
},
methods:{
change:function(){
let newData = ['e','f','g','h'];
this.list.push(...newData)
}
}
}
如上uni-app
代码,change
方法执行时,仅会将list
中的e,f,g,h
4个新增列表项传输过去,实现了setData
传输量的极简化。
uni-app
借鉴了 westore JSON Diff库,在调用setData
之前,会先比对历史数据,精确、高效计算出有变化的差量数据,然后再调用setData
,仅传输变化的数据,这样就实现 setData 传递数据量的最小化,大幅提高通讯性能。
Tips:也许有些同学对传递数据从a,b,c,d,e,f,g,h
8个列表项优化为e,f,g,h
4个列表项,不以为然,但我们提醒,不要小看这个机制,上述只是demo示例。
- 在实际列表场景中,每个列表项可能包含缩略图、标题、摘要、时间等各种信息,每个列表项数据都会更大(假设为1k);
- 假设当前页面有20个列表项,连续上拉4次后,页面变成100条记录;如果再次上拉,页面变成120条记录时,情况会有不同
- 上述微信原生的方式,将120条记录数据(120k)全部传输过去
- 上述 uni-app 模式,仅会将新增的20条(101 ~ 120)记录数据(20k)传输过去,数据量是原生方式的1/6!
- 当页面列表项数据越多,这个差别就越大,页面有200条记录时,uni-app传递数据量会变成微信原生数据传递量的1/10!
2.1.2 减少 setData 调用频次
假设我们有更改多个变量值的需求,我们分别以微信原生、uni-app 两种模式编写代码。
小程序原生代码:
change:function(){
this.setData({a:1});
this.setData({b:2});
this.setData({c:3});
this.setData({d:4});
}
如上四次调用setData
,就会引发4次逻辑层、视图层数据通讯
uni-app 代码:
change:function(){
this.a = 1;
this.b = 2;
this.c = 3;
this.d = 4;
}
如上uni-app
的代码,最后会被合并成{"a":1,"b":2,"c":3,"d":4}
一条数据,然后仅调用一次setData
完成所有数据传递,大幅降低了setData
的调用频次。
uni-app
之所以有这样的优势,是因为 uni-app 基于 Vue Runtime 深度定制实现,并借助了 Vue 的 nextTick 机制。
2.2 实测:性能对比数据
有了如上的理论分析,我们接着进行真机实测,用数据来对比。
测试模型如下:
- 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。
仿微博的列表是一个包含很多组件的列表,这种复杂列表对性能的压力更大,很适合做性能测试。
-
界面如下:
-
开发版本:使用微信原生、uni-app分别开发两套代码,uni-app使用
cli
方式默认安装。 -
测试代码开源(Github仓库地址:https://github.com/dcloudio/test-framework),
Tips:若有同学觉得测试代码写法欠妥,欢迎提交 PR 或 Issus,本项目下还有其它框架的测试代码,开发者可忽略 -
测试机型:红米 Redmi 6 Pro、MIUI 10.2.2.0 稳定版(最新版)、微信版本 7.0.3(最新版)
-
测试环境:每个框架开始测试前,杀掉各App进程、清空内存,保证测试机环境基本一致;每次从本地读取静态数据,屏蔽网络差异。
从触发上拉加载到数据更新、页面渲染完成,需要准确计时。人眼视觉计时肯定不行,我们采用程序埋点的方式,制定了如下计时时机:
- 计时开始时机:交互事件触发,框架赋值之前,如:上拉加载(onReachBottom)函数开头
- 计时结束时机:页面渲染完毕(微信setData回调函数开头)
Tips:setData
回调函数开头可认为是页面渲染完成的时间,是因为微信setData
定义如下(微信规范):
字段 | 类型 | 必填 | 描述 |
---|---|---|---|
data | Object | 是 | 这次要改变的数据 |
callback | Function | 否 | setData引起的界面更新渲染完毕后的回调函数 |
测试方式:从页面空列表开始,通过程序自动触发上拉加载,每次新增20条列表,记录单次耗时;固定间隔连续触发 N 次上拉加载,使得页面达到 20*N 条列表,计算这 N 次触发上拉到渲染完成的平均耗时。
测试结果如下:
列表条数 | 微信原生 | uni-app |
---|---|---|
200 | 770 | 641 |
400 | 876 | 741 |
600 | 1111 | 910 |
800 | 1406 | 1113 |
1000 | 1690 | 1321 |
说明:以400条微博列表为例,从页面空列表开始,每隔1秒触发一次上拉加载(新增20条微博),记录单次耗时,触发20次后停止(页面达到400条微博),计算这20次的平均耗时,结果微信原生在这20次 触发上拉 -> 渲染完成
的平均耗时为876毫秒,uni-app
是741毫秒。
这个数据,可能违反了很多人的直觉,uni-app 的性能竟然比微信原生还好!
当然,使用微信原生开发,也可以自己单独写代码优化setData。但每处业务都编写太多判断是不现实的,自然是用框架更舒心。
这个结果,和web开发类似,web开发也有原生js开发、vue、react框架等情况。如果不做特殊优化,原生js写的网页,性能经常还不如vue、react框架的性能。
也恰恰是因为Vue
、react
框架的优秀,性能好,开发体验好,所以原生js开发已经逐渐减少使用了。
3.社区生态
3.1 周边轮子
小程序是脱离web自造生态,很多web生态中轮子无法使用。
微信小程序还是有周边生态的,而其他几家小程序平台的生态基本没建起来。
uni-app
的周边生态非常丰富,在插件市场有近数千个插件,详见 ext.dcloud.net.cn。
首先uni-app
兼容小程序的生态,各种自定义组件均可直接引入使用。在此基础上,uni-app
的插件市场,有更多vue组件,同时可跨多端使用,并且性能优秀。
这使得uni-app
的生态成为最丰富的小程序开发生态。
比如富文本解析、图表、自定义下拉刷新等组件,uni-app
的插件性能均超过了wxparse、wx-echart等微信小程序组件。包括 uni ui等ui库的性能也超过了vant、iview等ui库的小程序版。
如果开发者需要丰富和高性能的组件,更应该使用uni-app
,而不是原生小程序开发。
3.2 活跃的QQ/微信群和论坛
uni-app
官方有 70 个开发者QQ/微信交流群(大多2千人群,近10万开发者),三方群更多。
问答社区,每天有数百篇帖子。活跃度与微信小程序官方论坛相同,远超过其他小程序官方论坛。
uni-app
三方培训活跃,腾讯课堂官方都为uni-app制作了课程,各种培训网站到处可见免费或收费的uni-app培训视频教程。
4.学习门槛、开发体验
首先微信原生的开发语法,既像React
,又像Vue
,有点不伦不类,对于开发者来说,等于又要学习一套新的语法,大幅提升了学习成本,这一直被大家所诟病。
uni-app
则对开发者更为友好,简单来说是 vue的语法 + 小程序的js api。
它遵循Vue.js
语法规范,组件和API遵循微信小程序命名
,这些都属于通用技术栈,学习它们是前端必备技能,uni-app
没有太多额外学习成本。
有一定 Vue.js 和微信小程序开发经验的开发者可快速上手 uni-app
。
没学过vue的同学,也不用掌握vue的全部,只需了解vue基础语法、数据绑定、列表渲染、组件等,其他如路由、loader、cli、node.js、webpack并不需要学。
因为HBuilderX工具搭配uni-app
可以免终端开发,可视化创建项目、可视化安装组件和扩展编译器,也就是uni-app
的学习门槛,比web开发的vue.js还低。
开发体验层面,微信原生开发相比uni-app
有较大差距,主要体现在:
- 更为强大的组件化开发能力:vue的组件开发比小程序自定义组件开发的体验要好很多
- 应用状态管理:uni-app支持vuex
- 使用 Sass 等 CSS 预处理器
- 完整的 ES Next 语法支持
- 自定义构建策略
开发工具维度,差距更大:
- 微信开发者工具被吐槽无数
uni-app
的出品公司,同时也是HBuilder的出品公司,DCloud.io。HBuilder/HBuilderX系列是四大主流前端开发工具(可对比百度指数),其为uni-app
做了很多优化,故uni-app
的开发效率、易用性非微信原生开发可及。
这里可以输出一个结论:如果你需要工程化能力,那就直接忘了微信原生开发吧。
5.未来扩展性
虽然当前产品仅要求发布到微信小程序,但阿里、百度、字节跳动、QQ、快应用等众多平台的流量越来越多,覆盖这些平台是迟早要考虑的事情。
此时,uni-ap
的跨端功能将成为程序员的自救神器,基于uni-app
开发的小程序,无需修改,即可同时发布到多家小程序,甚至App、H5平台。这不是梦想,而是现实。大家可依次扫描如下8个二维码,亲自体验最全面的跨平台效果!。
6.结语
uni-app | 微信 | |
---|---|---|
功能 | 相同 | 相同 |
性能 | 常规场景更优 | 需要自己编写复杂代码才能提高性能 |
社区生态 | 丰富,更多高性能组件 | 丰富 |
开发体验 | 纯vue体验,高效、统一;工程化能力强 | 语法私有化;工程化能力弱 |
多端能力 | 同时支持H5、多家小程序、跨平台App | 只能用于微信小程序 |
结论:只开发微信小程序,也应该使用uni-app
HQChart使用教程37 - 如何在uni-app创建k线图(app)
@[TOC](HQChart使用教程37 - 如何在uni-app创建k线图 app)
插件目录
创建步骤
1. 拷贝插件到工程中
创建一个空的uni-app 工程, 把wechathqchart 目录拷贝到工程中。
例子里 我建了一个hqchart.uniapp的目录, 把插件的文件拷贝到这个目录里
2. 创建一个page页 在page页中
import umychart.wechat.3.0.js
创建一个画布元素
绑定手势事件
代码如下:
<template>
<view>
<view>
<canvas id="kline" canvas-id='kline' class='kline' style="width: 400px; height: 600px"
@touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>
</view>
<view class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_DAY_ID)">日线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_WEEK_ID)">周线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_MINUTE_ID)">1分钟</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_15MINUTE_ID)">15分钟</button>
</view>
<view class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(0,'BOLL')">BOLL</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(1,'RSI')">RSI</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(2,'WR')">WR</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(0,'MA')">MA</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(1,'VOL')">VOL</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(1,'MACD')">MACD</button>
</view>
<view class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeSymbol('000001.sz')">000001.sz</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeSymbol('600000.sh')">600000.sh</button>
</view>
</view>
</template>
<script>
import {JSCommon} from '../../umychart.uniapp/umychart.wechat.3.0.js'
function DefaultData() { }
DefaultData.GetKLineOption = function ()
{
let data =
{
Type: '历史K线图',
Windows: //窗口指标
[
{Index:"MA",Modify: false, Change: false},
{Index:"VOL",Modify: false, Change: false},
{Index:"MACD",Modify: false, Change: false},
],
CorssCursorTouchEnd:true,
IsShowRightMenu:false, //是否显示右键菜单
CorssCursorInfo:{ Left:2,Right:2 },
Border: //边框
{
Left: 1,
Right: 1, //右边间距
Top: 1,
Bottom: 25,
},
KLine:
{
Right:1, //复权 0 不复权 1 前复权 2 后复权
Period:0, //周期: 0 日线 1 周线 2 月线 3 年线
PageSize:12,
IsShowTooltip:false,
},
Frame: //子框架设置 (Height 窗口高度比例值)
[
{ SplitCount:3,
//Height:4,
IsShowLeftText:true,
IsShowRightText:false
},
{
SplitCount:2,
//Height:2,
IsShowLeftText:true,
IsShowRightText:false
},
{
SplitCount:2,
//Height:2,
IsShowLeftText:true,
IsShowRightText:false
}
],
ExtendChart:
[
{Name:'KLineTooltip' },
],
};
return data;
}
//周期枚举
var PERIOD_ID=
{
KLINE_DAY_ID:0,
KLINE_WEEK_ID:1,
KLINE_MONTH_ID:2,
KLINE_YEAR_ID:3,
KLINE_MINUTE_ID:4,
KLINE_5MINUTE_ID:5,
KLINE_15MINUTE_ID:6,
KLINE_30MINUTE_ID:7,
KLINE_60MINUTE_ID:8
}
var g_KLine=
{
JSChart:null
};
export default
{
name:'HQChart',
data()
{
let data=
{
Symbol:'600000.sh',
KLine:
{
Option:DefaultData.GetKLineOption(),
IsShow:false,
Display:'none',
Width:400,
Height:600,
},
PERIOD_ID:PERIOD_ID,
};
return data;
},
onLoad()
{
},
onReady()
{
this.ChangeKLinePeriod(PERIOD_ID.KLINE_DAY_ID);
},
methods:
{
//////////////////////////////////////////////////////////////////////////////////////
CreateKLineChart:function()
{
if (this.KLine.JSChart) return;
let element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'kline';
element.Height = this.KLine.Height; //高度宽度需要手动绑定!!
element.Width = this.KLine.Width;
g_KLine.JSChart = JSCommon.JSChart.Init(element);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
this.KLine.Option.Symbol=this.Symbol;
g_KLine.JSChart.SetOption(this.KLine.Option);
},
//K线周期切换
ChangeKLinePeriod:function(period)
{
if (!g_KLine.JSChart) //不存在创建
{
this.KLine.Option.Period=period;
this.CreateKLineChart();
}
else
{
g_KLine.JSChart.ChangePeriod(period);
}
},
//切换指标 windowIndex=窗口索引 0开始, name=指标名字/ID
ChangeKLineIndex:function(windowIndex, name)
{
if (!g_KLine.JSChart) return;
g_KLine.JSChart.ChangeIndex(windowIndex,name);
},
//切换股票
ChangeSymbol:function(symbol)
{
if (!g_KLine.JSChart) return;
g_KLine.JSChart.ChangeSymbol(symbol);
},
NetworkFilter:function(data, callback)
{
console.log('[HQChart:NetworkFilter] data', data.Name);
},
//KLine事件
KLineTouchStart: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchStart(event);
},
KLineTouchMove: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchMove(event);
},
KLineTouchEnd: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchEnd(event);
},
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200upx;
width: 200upx;
margin-top: 200upx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50upx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36upx;
color: #8f8f94;
}
</style>
注意
element.IsUniApp=true
如果是uniapp-app平台的需要设置为true,其他平台的不需要设置
3.运行到模拟器
特别感谢 秋云
帮我解决了在uniapp-app 环境中measureText 无法计算的问题。
官方bugi地址: https://ask.dcloud.net.cn/question/70374
如果还有问题可以加交流QQ群: 950092318
HQChart代码地址
地址:github.com/jones2000/HQChart
@[TOC](HQChart使用教程37 - 如何在uni-app创建k线图 app)
插件目录
创建步骤
1. 拷贝插件到工程中
创建一个空的uni-app 工程, 把wechathqchart 目录拷贝到工程中。
例子里 我建了一个hqchart.uniapp的目录, 把插件的文件拷贝到这个目录里
2. 创建一个page页 在page页中
import umychart.wechat.3.0.js
创建一个画布元素
绑定手势事件
代码如下:
<template>
<view>
<view>
<canvas id="kline" canvas-id='kline' class='kline' style="width: 400px; height: 600px"
@touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>
</view>
<view class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_DAY_ID)">日线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_WEEK_ID)">周线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_MINUTE_ID)">1分钟</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(PERIOD_ID.KLINE_15MINUTE_ID)">15分钟</button>
</view>
<view class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(0,'BOLL')">BOLL</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(1,'RSI')">RSI</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(2,'WR')">WR</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(0,'MA')">MA</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(1,'VOL')">VOL</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLineIndex(1,'MACD')">MACD</button>
</view>
<view class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeSymbol('000001.sz')">000001.sz</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeSymbol('600000.sh')">600000.sh</button>
</view>
</view>
</template>
<script>
import {JSCommon} from '../../umychart.uniapp/umychart.wechat.3.0.js'
function DefaultData() { }
DefaultData.GetKLineOption = function ()
{
let data =
{
Type: '历史K线图',
Windows: //窗口指标
[
{Index:"MA",Modify: false, Change: false},
{Index:"VOL",Modify: false, Change: false},
{Index:"MACD",Modify: false, Change: false},
],
CorssCursorTouchEnd:true,
IsShowRightMenu:false, //是否显示右键菜单
CorssCursorInfo:{ Left:2,Right:2 },
Border: //边框
{
Left: 1,
Right: 1, //右边间距
Top: 1,
Bottom: 25,
},
KLine:
{
Right:1, //复权 0 不复权 1 前复权 2 后复权
Period:0, //周期: 0 日线 1 周线 2 月线 3 年线
PageSize:12,
IsShowTooltip:false,
},
Frame: //子框架设置 (Height 窗口高度比例值)
[
{ SplitCount:3,
//Height:4,
IsShowLeftText:true,
IsShowRightText:false
},
{
SplitCount:2,
//Height:2,
IsShowLeftText:true,
IsShowRightText:false
},
{
SplitCount:2,
//Height:2,
IsShowLeftText:true,
IsShowRightText:false
}
],
ExtendChart:
[
{Name:'KLineTooltip' },
],
};
return data;
}
//周期枚举
var PERIOD_ID=
{
KLINE_DAY_ID:0,
KLINE_WEEK_ID:1,
KLINE_MONTH_ID:2,
KLINE_YEAR_ID:3,
KLINE_MINUTE_ID:4,
KLINE_5MINUTE_ID:5,
KLINE_15MINUTE_ID:6,
KLINE_30MINUTE_ID:7,
KLINE_60MINUTE_ID:8
}
var g_KLine=
{
JSChart:null
};
export default
{
name:'HQChart',
data()
{
let data=
{
Symbol:'600000.sh',
KLine:
{
Option:DefaultData.GetKLineOption(),
IsShow:false,
Display:'none',
Width:400,
Height:600,
},
PERIOD_ID:PERIOD_ID,
};
return data;
},
onLoad()
{
},
onReady()
{
this.ChangeKLinePeriod(PERIOD_ID.KLINE_DAY_ID);
},
methods:
{
//////////////////////////////////////////////////////////////////////////////////////
CreateKLineChart:function()
{
if (this.KLine.JSChart) return;
let element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'kline';
element.Height = this.KLine.Height; //高度宽度需要手动绑定!!
element.Width = this.KLine.Width;
g_KLine.JSChart = JSCommon.JSChart.Init(element);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
this.KLine.Option.Symbol=this.Symbol;
g_KLine.JSChart.SetOption(this.KLine.Option);
},
//K线周期切换
ChangeKLinePeriod:function(period)
{
if (!g_KLine.JSChart) //不存在创建
{
this.KLine.Option.Period=period;
this.CreateKLineChart();
}
else
{
g_KLine.JSChart.ChangePeriod(period);
}
},
//切换指标 windowIndex=窗口索引 0开始, name=指标名字/ID
ChangeKLineIndex:function(windowIndex, name)
{
if (!g_KLine.JSChart) return;
g_KLine.JSChart.ChangeIndex(windowIndex,name);
},
//切换股票
ChangeSymbol:function(symbol)
{
if (!g_KLine.JSChart) return;
g_KLine.JSChart.ChangeSymbol(symbol);
},
NetworkFilter:function(data, callback)
{
console.log('[HQChart:NetworkFilter] data', data.Name);
},
//KLine事件
KLineTouchStart: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchStart(event);
},
KLineTouchMove: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchMove(event);
},
KLineTouchEnd: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchEnd(event);
},
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200upx;
width: 200upx;
margin-top: 200upx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50upx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36upx;
color: #8f8f94;
}
</style>
注意
element.IsUniApp=true
如果是uniapp-app平台的需要设置为true,其他平台的不需要设置
3.运行到模拟器
特别感谢 秋云
帮我解决了在uniapp-app 环境中measureText 无法计算的问题。
官方bugi地址: https://ask.dcloud.net.cn/question/70374
如果还有问题可以加交流QQ群: 950092318
HQChart代码地址
地址:github.com/jones2000/HQChart
HQChart使用教程35 - 如何在uni-app创建K线图(h5)
插件目录
创建步骤
1. 创建一个空的uni-app 工程, 把umychart_uniapp_h5 目录拷贝到工程中。
2. 创建一个page页 在page页中 import umychart.uniapp.h5.js
代码如下:
<template>
<div class='divchart' >
<div class='kline' id="kline" ref='kline' v-show="KLine.IsShow"></div>
</div>
</template>
<script>
import HQChart from '../../umychart_uniapp_h5/umychart.uniapp.h5.js'
function DefaultData() { }
DefaultData.GetKLineOption = function ()
{
let data =
{
Type: '历史K线图',
Windows: //窗口指标
[
{Index:"MA",Modify: false, Change: false},
{Index:"VOL",Modify: false, Change: false}
],
IsCorssOnlyDrawKLine:true,
CorssCursorTouchEnd:true,
Border: //边框
{
Left: 1,
Right: 1, //右边间距
Top: 25,
Bottom: 25,
},
KLine:
{
Right:1, //复权 0 不复权 1 前复权 2 后复权
Period:0, //周期: 0 日线 1 周线 2 月线 3 年线
PageSize:70,
IsShowTooltip:false
},
ExtendChart:
[
{Name:'KLineTooltip' }, //开启手机端tooltip
],
};
return data;
}
export default
{
data()
{
let data=
{
Symbol:'600000.sh',
ChartWidth:300,
ChartHeight:600,
KLine:
{
JSChart:null,
Option:DefaultData.GetKLineOption(),
IsShow:true,
}
};
return data;
},
onLoad()
{
},
onReady()
{
this.OnSize();
this.CreateKLineChart();
},
methods:
{
OnSize()
{
var chartHeight = this.ChartHeight;
var chartWidth = this.ChartWidth;
var kline=this.$refs.kline;
kline.style.width=chartWidth+'px';
kline.style.height=chartHeight+'px';
if (this.KLine.JSChart) this.KLine.JSChart.OnSize();
},
CreateKLineChart() //创建K线图
{
if (this.KLine.JSChart) return;
this.KLine.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.kline);
chart.SetOption(this.KLine.Option);
this.KLine.JSChart=chart;
},
}
}
</script>
<style>
</style>
3.在浏览器里运行
效果图
如果还有问题可以加交流QQ群: 950092318
HQChart代码地址
地址:github.com/jones2000/HQChart
插件目录
创建步骤
1. 创建一个空的uni-app 工程, 把umychart_uniapp_h5 目录拷贝到工程中。
2. 创建一个page页 在page页中 import umychart.uniapp.h5.js
代码如下:
<template>
<div class='divchart' >
<div class='kline' id="kline" ref='kline' v-show="KLine.IsShow"></div>
</div>
</template>
<script>
import HQChart from '../../umychart_uniapp_h5/umychart.uniapp.h5.js'
function DefaultData() { }
DefaultData.GetKLineOption = function ()
{
let data =
{
Type: '历史K线图',
Windows: //窗口指标
[
{Index:"MA",Modify: false, Change: false},
{Index:"VOL",Modify: false, Change: false}
],
IsCorssOnlyDrawKLine:true,
CorssCursorTouchEnd:true,
Border: //边框
{
Left: 1,
Right: 1, //右边间距
Top: 25,
Bottom: 25,
},
KLine:
{
Right:1, //复权 0 不复权 1 前复权 2 后复权
Period:0, //周期: 0 日线 1 周线 2 月线 3 年线
PageSize:70,
IsShowTooltip:false
},
ExtendChart:
[
{Name:'KLineTooltip' }, //开启手机端tooltip
],
};
return data;
}
export default
{
data()
{
let data=
{
Symbol:'600000.sh',
ChartWidth:300,
ChartHeight:600,
KLine:
{
JSChart:null,
Option:DefaultData.GetKLineOption(),
IsShow:true,
}
};
return data;
},
onLoad()
{
},
onReady()
{
this.OnSize();
this.CreateKLineChart();
},
methods:
{
OnSize()
{
var chartHeight = this.ChartHeight;
var chartWidth = this.ChartWidth;
var kline=this.$refs.kline;
kline.style.width=chartWidth+'px';
kline.style.height=chartHeight+'px';
if (this.KLine.JSChart) this.KLine.JSChart.OnSize();
},
CreateKLineChart() //创建K线图
{
if (this.KLine.JSChart) return;
this.KLine.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.kline);
chart.SetOption(this.KLine.Option);
this.KLine.JSChart=chart;
},
}
}
</script>
<style>
</style>
3.在浏览器里运行
效果图
如果还有问题可以加交流QQ群: 950092318
HQChart代码地址
地址:github.com/jones2000/HQChart
HQChart使用教程44-uniapp使用条件编译同时支持h5,app,小程序
今天在群里帮一位朋友排查HQChart同时在多端使用的问题。我整理了以下
背景
由于小程序/app很多局限性(无法创建DOM,canvas异步绘图等)导致HQChart开发的时候分成两个版本(小程序版本和H5页面版本)。
使用条件编译我们可以把这2个版本组件同时包含的工程中,并且通过平台判断动态加载对应版本的js.
安装插件
在工程里建2个目录,把HQChart的2个版本分别考入对应的目录里。
版本对应关系看以前的教程
HQChart使用教程35 - 如何在uni-app创建K线图(h5)
HQChart使用教程37 - 如何在uni-app创建k线图(app)
template 设置
由于小程序/app是无法动态创建dom,所有只能是先在模板里写死一个画布,在初始化的时候绑定到HQChart中。H5是可以直接内部创建dom,所以只需要传入一个div,HQChart自动会创建画布. 我们使用条件编译在不同的平台使用不同的模板
注意 id的名字尽量使用不一样的,如h5如果使用id='kline' 在app/小程序就使用id='kline2'
<!-- #ifdef H5 -->
<div>
<div class='kline' id="kline" ref='kline' v-show="KLine.IsShow"></div>
<div class='minute' id="minute" ref='minute' v-show="Minute.IsShow"></div>
</div>
<!-- #endif -->
<!-- #ifndef H5 -->
<view>
<canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="KLine.IsShow"
@touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>
<canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"
@touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>
</view>
<!-- #endif -->
这样2个模板就同时存在一个页面中了
创建插件设置
为每个平台创建一个创建插件的函数,然后通过一个总的创建函数动态调用对应的创建方法
下面是也创建K线图为例子‘CreateKLineChart_h5()'是h5平台的创建插件方法, 'CreateKLineChart_app()'是app和小程序创建的方法,在"CreateKLineChart()'通过条件编译就可以动态选择使用对应的创建函数
为了代码的可读和可维护性我这边是拆分成2个创建函数,你也可以在CreateKLineChart里面把2个平台的创建都写里面,只过不这样可读性比较差。
CreateKLineChart()
{
// #ifdef H5
this.CreateKLineChart_h5();
// #endif
// #ifndef H5
this.CreateKLineChart_app();
// #endif
},
下面是2个平台对应的创建方法,走势图的创建也是一样
CreateKLineChart_h5() //创建K线图
{
if (g_KLine.JSChart) return;
this.KLine.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.kline);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
chart.SetOption(this.KLine.Option);
g_KLine.JSChart=chart;
},
CreateKLineChart_app()
{
if (this.KLine.JSChart) return;
let element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'kline2';
element.Height = this.ChartHeight; //高度宽度需要手动绑定!!
element.Width = this.ChartWidth;
g_KLine.JSChart = JSCommon.JSChart.Init(element);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
this.KLine.Option.Symbol=this.Symbol;
g_KLine.JSChart.SetOption(this.KLine.Option);
},
HQChart大小调整
由于app/小程序无法获取dom,所以只能是在外部把画布的长宽设置到HQChart中(动态获取只能通过其他的查询元素函数获取,比较麻烦,关键还是异步的,非常搞不懂获取一个元素信息还使用异步,难道以目前的手机配置查询几千几万了dom信息不能在毫秒级处理完),
H5是可以动态获取dom,就不存在这个文件,改变了外层的div调用HQChart的OnSize()方法就可以动态把div的大小绑定画布上。
OnSize()
{
// #ifdef H5
this.OnSize_h5();
// #endif
},
OnSize_h5()
{
var chartHeight = this.ChartHeight;
var chartWidth = this.ChartWidth;
var kline=this.$refs.kline;
kline.style.width=chartWidth+'px';
kline.style.height=chartHeight+'px';
if (g_KLine.JSChart) g_KLine.JSChart.OnSize();
var minute=this.$refs.minute;
minute.style.width=chartWidth+'px';
minute.style.height=chartHeight+'px';
if (g_Minute.JSChart) g_Minute.JSChart.OnSize();
},
这样多端支持就完成了。点运行,然后喝杯咖啡吧,编译调试真的很慢。
效果图
完整代码
<template>
<div class='divchart' >
<!-- #ifdef H5 -->
<div>
<div class='kline' id="kline" ref='kline' v-show="KLine.IsShow"></div>
<div class='minute' id="minute" ref='minute' v-show="Minute.IsShow"></div>
</div>
<!-- #endif -->
<!-- #ifndef H5 -->
<view>
<canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="KLine.IsShow"
@touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>
<canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"
@touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>
</view>
<!-- #endif -->
<div class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_ID)">分时</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_5DAY_ID)">5日</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_DAY_ID)">日线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_WEEK_ID)">周线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_MINUTE_ID)">1分钟</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_15MINUTE_ID)">15分钟</button>
</div>
</div>
</template>
<script>
// #ifdef H5
import HQChart from '../../umychart_uniapp_h5/umychart.uniapp.h5.js'
// #endif
// #ifndef H5
import {JSCommon} from '../../umychart.uniapp/umychart.wechat.3.0.js'
// #endif
function DefaultData() { }
DefaultData.GetKLineOption = function ()
{
let data =
{
Type: '历史K线图',
Windows: //窗口指标
[
{Index:"MA",Modify: false, Change: false},
{Index:"VOL",Modify: false, Change: false}
],
IsCorssOnlyDrawKLine:true,
CorssCursorTouchEnd:true,
Border: //边框
{
Left: 1,
Right: 1, //右边间距
Top: 25,
Bottom: 25,
},
KLine:
{
Right:1, //复权 0 不复权 1 前复权 2 后复权
Period:0, //周期: 0 日线 1 周线 2 月线 3 年线
PageSize:30,
IsShowTooltip:false
},
ExtendChart:
[
{Name:'KLineTooltip' }, //开启手机端tooltip
],
Frame: //子框架设置
[
{SplitCount:3},
{SplitCount:2},
{SplitCount:3},
],
};
return data;
}
DefaultData.GetMinuteOption=function()
{
var option=
{
Type:'分钟走势图', //创建图形类型
Windows: //窗口指标
[
],
IsAutoUpdate:true, //是自动更新数据
DayCount:1, //1 最新交易日数据 >1 多日走势图
IsShowRightMenu:false, //是否显示右键菜单
CorssCursorTouchEnd:true,
MinuteLine:
{
//IsDrawAreaPrice:false, //是否画价格面积图
},
Border: //边框
{
Left:1, //左边间距
Right:1, //右边间距
Top:20,
Bottom:20
},
Frame: //子框架设置
[
{SplitCount:3},
{SplitCount:2},
{SplitCount:3},
],
ExtendChart: //扩展图形
[
{Name:'MinuteTooltip' } //手机端tooltip
],
};
return option;
}
//周期枚举
var KLINE_PERIOD_ID=
{
KLINE_DAY_ID:0,
KLINE_WEEK_ID:1,
KLINE_MONTH_ID:2,
KLINE_YEAR_ID:3,
KLINE_MINUTE_ID:4,
KLINE_5MINUTE_ID:5,
KLINE_15MINUTE_ID:6,
KLINE_30MINUTE_ID:7,
KLINE_60MINUTE_ID:8
}
//周期枚举
var MINUTE_PERIOD_ID=
{
MINUTE_ID:1,
MINUTE_2DAY_ID:2,
MINUTE_3DAY_ID:3,
MINUTE_4DAY_ID:4,
MINUTE_5DAY_ID:5,
}
var g_KLine={ JSChart:null };
var g_Minute={ JSChart:null };
export default
{
data()
{
let data=
{
Symbol:'600000.sh',
ChartWidth:300,
ChartHeight:500,
KLine:
{
Option:DefaultData.GetKLineOption(),
IsShow:true,
},
Minute:
{
Option:DefaultData.GetMinuteOption(),
IsShow:false,
},
MINUTE_PERIOD_ID:MINUTE_PERIOD_ID,
KLINE_PERIOD_ID:KLINE_PERIOD_ID,
};
return data;
},
onLoad()
{
},
onReady()
{
this.OnSize();
this.CreateKLineChart();
},
methods:
{
OnSize()
{
// #ifdef H5
this.OnSize_h5();
// #endif
},
OnSize_h5()
{
var chartHeight = this.ChartHeight;
var chartWidth = this.ChartWidth;
var kline=this.$refs.kline;
kline.style.width=chartWidth+'px';
kline.style.height=chartHeight+'px';
if (g_KLine.JSChart) g_KLine.JSChart.OnSize();
var minute=this.$refs.minute;
minute.style.width=chartWidth+'px';
minute.style.height=chartHeight+'px';
if (g_Minute.JSChart) g_Minute.JSChart.OnSize();
},
CreateKLineChart_h5() //创建K线图
{
if (g_KLine.JSChart) return;
this.KLine.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.kline);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
chart.SetOption(this.KLine.Option);
g_KLine.JSChart=chart;
},
CreateKLineChart_app()
{
if (this.KLine.JSChart) return;
let element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'kline2';
element.Height = this.ChartHeight; //高度宽度需要手动绑定!!
element.Width = this.ChartWidth;
g_KLine.JSChart = JSCommon.JSChart.Init(element);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
this.KLine.Option.Symbol=this.Symbol;
g_KLine.JSChart.SetOption(this.KLine.Option);
},
CreateKLineChart()
{
// #ifdef H5
this.CreateKLineChart_h5();
// #endif
// #ifndef H5
this.CreateKLineChart_app();
// #endif
},
//K线周期切换
ChangeKLinePeriod:function(period)
{
this.Minute.IsShow=false;
this.KLine.IsShow=true;
if (!g_KLine.JSChart) //不存在创建
{
this.KLine.Option.Period=period;
this.CreateKLineChart_h5();
}
else
{
g_KLine.JSChart.ChangePeriod(period);
}
},
CreateMinuteChart_h5() //创建日线图
{
if (g_Minute.JSChart) return;
this.Minute.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.minute);
this.Minute.Option.NetworkFilter=this.NetworkFilter;
chart.SetOption(this.Minute.Option);
g_Minute.JSChart=chart;
},
CreateMinuteChart_app()
{
if (g_Minute.JSChart) return;
var element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'minute2';
element.Height = this.ChartHeight; //高度宽度需要手动绑定!!
element.Width = this.ChartWidth;
g_Minute.JSChart = JSCommon.JSChart.Init(element);
this.Minute.Option.NetworkFilter=this.NetworkFilter;
this.Minute.Option.Symbol=this.Symbol;
g_Minute.JSChart.SetOption(this.Minute.Option);
},
CreateMinuteChart()
{
// #ifdef H5
this.CreateMinuteChart_h5();
// #endif
// #ifndef H5
this.CreateMinuteChart_app();
// #endif
},
//走势图多日切换
ChangeMinutePeriod:function(period)
{
this.Minute.IsShow=true;
this.KLine.IsShow=false;
if (!g_Minute.JSChart) //不存在创建
{
this.Minute.Option.DayCount=period;
this.CreateMinuteChart();
}
else
{
g_Minute.JSChart.ChangeDayCount(period);
}
},
NetworkFilter:function(data, callback)
{
console.log(`[HQChart:NetworkFilter] Name=${data.Name} Explain=${data.Explain}` );
},
///////////////////////////////////////////////
//手势事件 app/小程序才有
//KLine事件
KLineTouchStart: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchStart(event);
},
KLineTouchMove: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchMove(event);
},
KLineTouchEnd: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchEnd(event);
},
//走势图事件
MinuteTouchStart: function (event)
{
if (g_Minute.JSChart) g_Minute.JSChart.OnTouchStart(event);
},
MinuteTouchMove: function (event)
{
if (g_Minute.JSChart) g_Minute.JSChart.OnTouchMove(event);
},
MinuteTouchEnd: function (event)
{
if (g_Minute.JSChart) g_Minute.JSChart.OnTouchEnd(event);
},
}
}
</script>
<style>
</style>
如果还有问题可以加交流QQ群: 950092318
HQChart代码地址
地址:github.com/jones2000/HQChart
今天在群里帮一位朋友排查HQChart同时在多端使用的问题。我整理了以下
背景
由于小程序/app很多局限性(无法创建DOM,canvas异步绘图等)导致HQChart开发的时候分成两个版本(小程序版本和H5页面版本)。
使用条件编译我们可以把这2个版本组件同时包含的工程中,并且通过平台判断动态加载对应版本的js.
安装插件
在工程里建2个目录,把HQChart的2个版本分别考入对应的目录里。
版本对应关系看以前的教程
HQChart使用教程35 - 如何在uni-app创建K线图(h5)
HQChart使用教程37 - 如何在uni-app创建k线图(app)
template 设置
由于小程序/app是无法动态创建dom,所有只能是先在模板里写死一个画布,在初始化的时候绑定到HQChart中。H5是可以直接内部创建dom,所以只需要传入一个div,HQChart自动会创建画布. 我们使用条件编译在不同的平台使用不同的模板
注意 id的名字尽量使用不一样的,如h5如果使用id='kline' 在app/小程序就使用id='kline2'
<!-- #ifdef H5 -->
<div>
<div class='kline' id="kline" ref='kline' v-show="KLine.IsShow"></div>
<div class='minute' id="minute" ref='minute' v-show="Minute.IsShow"></div>
</div>
<!-- #endif -->
<!-- #ifndef H5 -->
<view>
<canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="KLine.IsShow"
@touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>
<canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"
@touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>
</view>
<!-- #endif -->
这样2个模板就同时存在一个页面中了
创建插件设置
为每个平台创建一个创建插件的函数,然后通过一个总的创建函数动态调用对应的创建方法
下面是也创建K线图为例子‘CreateKLineChart_h5()'是h5平台的创建插件方法, 'CreateKLineChart_app()'是app和小程序创建的方法,在"CreateKLineChart()'通过条件编译就可以动态选择使用对应的创建函数
为了代码的可读和可维护性我这边是拆分成2个创建函数,你也可以在CreateKLineChart里面把2个平台的创建都写里面,只过不这样可读性比较差。
CreateKLineChart()
{
// #ifdef H5
this.CreateKLineChart_h5();
// #endif
// #ifndef H5
this.CreateKLineChart_app();
// #endif
},
下面是2个平台对应的创建方法,走势图的创建也是一样
CreateKLineChart_h5() //创建K线图
{
if (g_KLine.JSChart) return;
this.KLine.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.kline);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
chart.SetOption(this.KLine.Option);
g_KLine.JSChart=chart;
},
CreateKLineChart_app()
{
if (this.KLine.JSChart) return;
let element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'kline2';
element.Height = this.ChartHeight; //高度宽度需要手动绑定!!
element.Width = this.ChartWidth;
g_KLine.JSChart = JSCommon.JSChart.Init(element);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
this.KLine.Option.Symbol=this.Symbol;
g_KLine.JSChart.SetOption(this.KLine.Option);
},
HQChart大小调整
由于app/小程序无法获取dom,所以只能是在外部把画布的长宽设置到HQChart中(动态获取只能通过其他的查询元素函数获取,比较麻烦,关键还是异步的,非常搞不懂获取一个元素信息还使用异步,难道以目前的手机配置查询几千几万了dom信息不能在毫秒级处理完),
H5是可以动态获取dom,就不存在这个文件,改变了外层的div调用HQChart的OnSize()方法就可以动态把div的大小绑定画布上。
OnSize()
{
// #ifdef H5
this.OnSize_h5();
// #endif
},
OnSize_h5()
{
var chartHeight = this.ChartHeight;
var chartWidth = this.ChartWidth;
var kline=this.$refs.kline;
kline.style.width=chartWidth+'px';
kline.style.height=chartHeight+'px';
if (g_KLine.JSChart) g_KLine.JSChart.OnSize();
var minute=this.$refs.minute;
minute.style.width=chartWidth+'px';
minute.style.height=chartHeight+'px';
if (g_Minute.JSChart) g_Minute.JSChart.OnSize();
},
这样多端支持就完成了。点运行,然后喝杯咖啡吧,编译调试真的很慢。
效果图
完整代码
<template>
<div class='divchart' >
<!-- #ifdef H5 -->
<div>
<div class='kline' id="kline" ref='kline' v-show="KLine.IsShow"></div>
<div class='minute' id="minute" ref='minute' v-show="Minute.IsShow"></div>
</div>
<!-- #endif -->
<!-- #ifndef H5 -->
<view>
<canvas id="kline2" canvas-id='kline2' class='kline2' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="KLine.IsShow"
@touchstart="KLineTouchStart" @touchmove='KLineTouchMove' @touchend='KLineTouchEnd' ></canvas>
<canvas id="minute2" canvas-id='minute2' class='minute' v-bind:style="{width: ChartWidth+'px', height: ChartHeight+'px'}" v-show="Minute.IsShow"
@touchstart="MinuteTouchStart" @touchmove='MinuteTouchMove' @touchend='MinuteTouchEnd' ></canvas>
</view>
<!-- #endif -->
<div class="button-sp-area">
<button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_ID)">分时</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeMinutePeriod(MINUTE_PERIOD_ID.MINUTE_5DAY_ID)">5日</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_DAY_ID)">日线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_WEEK_ID)">周线</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_MINUTE_ID)">1分钟</button>
<button class="mini-btn" type="default" size="mini" @click="ChangeKLinePeriod(KLINE_PERIOD_ID.KLINE_15MINUTE_ID)">15分钟</button>
</div>
</div>
</template>
<script>
// #ifdef H5
import HQChart from '../../umychart_uniapp_h5/umychart.uniapp.h5.js'
// #endif
// #ifndef H5
import {JSCommon} from '../../umychart.uniapp/umychart.wechat.3.0.js'
// #endif
function DefaultData() { }
DefaultData.GetKLineOption = function ()
{
let data =
{
Type: '历史K线图',
Windows: //窗口指标
[
{Index:"MA",Modify: false, Change: false},
{Index:"VOL",Modify: false, Change: false}
],
IsCorssOnlyDrawKLine:true,
CorssCursorTouchEnd:true,
Border: //边框
{
Left: 1,
Right: 1, //右边间距
Top: 25,
Bottom: 25,
},
KLine:
{
Right:1, //复权 0 不复权 1 前复权 2 后复权
Period:0, //周期: 0 日线 1 周线 2 月线 3 年线
PageSize:30,
IsShowTooltip:false
},
ExtendChart:
[
{Name:'KLineTooltip' }, //开启手机端tooltip
],
Frame: //子框架设置
[
{SplitCount:3},
{SplitCount:2},
{SplitCount:3},
],
};
return data;
}
DefaultData.GetMinuteOption=function()
{
var option=
{
Type:'分钟走势图', //创建图形类型
Windows: //窗口指标
[
],
IsAutoUpdate:true, //是自动更新数据
DayCount:1, //1 最新交易日数据 >1 多日走势图
IsShowRightMenu:false, //是否显示右键菜单
CorssCursorTouchEnd:true,
MinuteLine:
{
//IsDrawAreaPrice:false, //是否画价格面积图
},
Border: //边框
{
Left:1, //左边间距
Right:1, //右边间距
Top:20,
Bottom:20
},
Frame: //子框架设置
[
{SplitCount:3},
{SplitCount:2},
{SplitCount:3},
],
ExtendChart: //扩展图形
[
{Name:'MinuteTooltip' } //手机端tooltip
],
};
return option;
}
//周期枚举
var KLINE_PERIOD_ID=
{
KLINE_DAY_ID:0,
KLINE_WEEK_ID:1,
KLINE_MONTH_ID:2,
KLINE_YEAR_ID:3,
KLINE_MINUTE_ID:4,
KLINE_5MINUTE_ID:5,
KLINE_15MINUTE_ID:6,
KLINE_30MINUTE_ID:7,
KLINE_60MINUTE_ID:8
}
//周期枚举
var MINUTE_PERIOD_ID=
{
MINUTE_ID:1,
MINUTE_2DAY_ID:2,
MINUTE_3DAY_ID:3,
MINUTE_4DAY_ID:4,
MINUTE_5DAY_ID:5,
}
var g_KLine={ JSChart:null };
var g_Minute={ JSChart:null };
export default
{
data()
{
let data=
{
Symbol:'600000.sh',
ChartWidth:300,
ChartHeight:500,
KLine:
{
Option:DefaultData.GetKLineOption(),
IsShow:true,
},
Minute:
{
Option:DefaultData.GetMinuteOption(),
IsShow:false,
},
MINUTE_PERIOD_ID:MINUTE_PERIOD_ID,
KLINE_PERIOD_ID:KLINE_PERIOD_ID,
};
return data;
},
onLoad()
{
},
onReady()
{
this.OnSize();
this.CreateKLineChart();
},
methods:
{
OnSize()
{
// #ifdef H5
this.OnSize_h5();
// #endif
},
OnSize_h5()
{
var chartHeight = this.ChartHeight;
var chartWidth = this.ChartWidth;
var kline=this.$refs.kline;
kline.style.width=chartWidth+'px';
kline.style.height=chartHeight+'px';
if (g_KLine.JSChart) g_KLine.JSChart.OnSize();
var minute=this.$refs.minute;
minute.style.width=chartWidth+'px';
minute.style.height=chartHeight+'px';
if (g_Minute.JSChart) g_Minute.JSChart.OnSize();
},
CreateKLineChart_h5() //创建K线图
{
if (g_KLine.JSChart) return;
this.KLine.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.kline);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
chart.SetOption(this.KLine.Option);
g_KLine.JSChart=chart;
},
CreateKLineChart_app()
{
if (this.KLine.JSChart) return;
let element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'kline2';
element.Height = this.ChartHeight; //高度宽度需要手动绑定!!
element.Width = this.ChartWidth;
g_KLine.JSChart = JSCommon.JSChart.Init(element);
this.KLine.Option.NetworkFilter=this.NetworkFilter;
this.KLine.Option.Symbol=this.Symbol;
g_KLine.JSChart.SetOption(this.KLine.Option);
},
CreateKLineChart()
{
// #ifdef H5
this.CreateKLineChart_h5();
// #endif
// #ifndef H5
this.CreateKLineChart_app();
// #endif
},
//K线周期切换
ChangeKLinePeriod:function(period)
{
this.Minute.IsShow=false;
this.KLine.IsShow=true;
if (!g_KLine.JSChart) //不存在创建
{
this.KLine.Option.Period=period;
this.CreateKLineChart_h5();
}
else
{
g_KLine.JSChart.ChangePeriod(period);
}
},
CreateMinuteChart_h5() //创建日线图
{
if (g_Minute.JSChart) return;
this.Minute.Option.Symbol=this.Symbol;
let chart=HQChart.JSChart.Init(this.$refs.minute);
this.Minute.Option.NetworkFilter=this.NetworkFilter;
chart.SetOption(this.Minute.Option);
g_Minute.JSChart=chart;
},
CreateMinuteChart_app()
{
if (g_Minute.JSChart) return;
var element = new JSCommon.JSCanvasElement();
// #ifdef APP-PLUS
element.IsUniApp=true; //canvas需要指定下 是uniapp的app
// #endif
element.ID = 'minute2';
element.Height = this.ChartHeight; //高度宽度需要手动绑定!!
element.Width = this.ChartWidth;
g_Minute.JSChart = JSCommon.JSChart.Init(element);
this.Minute.Option.NetworkFilter=this.NetworkFilter;
this.Minute.Option.Symbol=this.Symbol;
g_Minute.JSChart.SetOption(this.Minute.Option);
},
CreateMinuteChart()
{
// #ifdef H5
this.CreateMinuteChart_h5();
// #endif
// #ifndef H5
this.CreateMinuteChart_app();
// #endif
},
//走势图多日切换
ChangeMinutePeriod:function(period)
{
this.Minute.IsShow=true;
this.KLine.IsShow=false;
if (!g_Minute.JSChart) //不存在创建
{
this.Minute.Option.DayCount=period;
this.CreateMinuteChart();
}
else
{
g_Minute.JSChart.ChangeDayCount(period);
}
},
NetworkFilter:function(data, callback)
{
console.log(`[HQChart:NetworkFilter] Name=${data.Name} Explain=${data.Explain}` );
},
///////////////////////////////////////////////
//手势事件 app/小程序才有
//KLine事件
KLineTouchStart: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchStart(event);
},
KLineTouchMove: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchMove(event);
},
KLineTouchEnd: function (event)
{
if (g_KLine.JSChart) g_KLine.JSChart.OnTouchEnd(event);
},
//走势图事件
MinuteTouchStart: function (event)
{
if (g_Minute.JSChart) g_Minute.JSChart.OnTouchStart(event);
},
MinuteTouchMove: function (event)
{
if (g_Minute.JSChart) g_Minute.JSChart.OnTouchMove(event);
},
MinuteTouchEnd: function (event)
{
if (g_Minute.JSChart) g_Minute.JSChart.OnTouchEnd(event);
},
}
}
</script>
<style>
</style>
如果还有问题可以加交流QQ群: 950092318
HQChart代码地址
地址:github.com/jones2000/HQChart
Android平台解决使用UniPush和个推推送违反谷歌应用商店(GooglePlay)个人和敏感信息政策无法上架的说明
HBuilderX2.8.4+版本调整为默认不再弹出隐私政策提示框,建议使用Android平台隐私与政策提示框配置方法,参考:https://ask.dcloud.net.cn/article/36937
最近谷歌应用商店(GooglePlay)加强对应用的审查,如果违反个人和敏感信息政策无法通过审核上架,甚至已经上架的应用也可能被下架。
提示信息如下:
sue: Violation of Personal and Sensitive Information policy
We’ve identified that your app is using an SDK or library that facilitates the collection and transmission of installed packages information without meeting the prominent disclosure guidelines.
If necessary, you can consult your SDK provider(s) for further information.
Next steps: Submit your app for another review
Read through the Personal and Sensitive Information policy and make the appropriate changes to your app. Your app is using the Igexin SDK, which is uploading users installed packages information to http://sdk.open.phone.igexin.com/api.php without a prominent disclosure. Prior to the collection and transmission, it must prominently highlight how the user data will be used, describe the type of data being collected and have the user provide affirmative consent for such use. Your app must handle user data securely, including transmitting it using modern cryptography (for example, over HTTPS).
这是因为UniPush和个推推送使用的SDK需要获取设备的唯一标识等用于下发推送消息,但没有提示用户确认导致违反谷歌的个人和敏感政策。
HBuilderX2.3.4+版已经更新个推SDK解决此问题
使用UniPush和个推推送时会在应用启动时会弹出隐私政策提示框
以上提示框可能会影响用户的使用体验,因此仅会在打GooglePlay渠道包时生效
提交云端打包时请在渠道包项中选择“GooglePlay”
<a id="noprompt"/>
配置不弹出隐私政策提示框
HBuilder2.6.13+版本支持配置不弹出隐私政策提示框
打开manifest.json文件,切换到源码视图:
- 5+ App项目
在 "plus" -> "distribute" -> "plugins" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段 - uni-app项目
在 "app-plus" -> "distribute" -> "sdkConfigs" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
"push" : {
"unipush" : {
"__privacy_prompt__" : "none" //值为"show"则显示隐私政策提示框
}
},
提交云端打包后生效
离线打包配置
在AndroidManifest.xml文件中添加以下meta-data数据
<meta-data android:name="DCLOUD_PUSH_PRIVACY" android:value="false"/>
HBuilderX2.8.4+版本调整为默认不再弹出隐私政策提示框,建议使用Android平台隐私与政策提示框配置方法,参考:https://ask.dcloud.net.cn/article/36937
最近谷歌应用商店(GooglePlay)加强对应用的审查,如果违反个人和敏感信息政策无法通过审核上架,甚至已经上架的应用也可能被下架。
提示信息如下:
sue: Violation of Personal and Sensitive Information policy
We’ve identified that your app is using an SDK or library that facilitates the collection and transmission of installed packages information without meeting the prominent disclosure guidelines.
If necessary, you can consult your SDK provider(s) for further information.
Next steps: Submit your app for another review
Read through the Personal and Sensitive Information policy and make the appropriate changes to your app. Your app is using the Igexin SDK, which is uploading users installed packages information to http://sdk.open.phone.igexin.com/api.php without a prominent disclosure. Prior to the collection and transmission, it must prominently highlight how the user data will be used, describe the type of data being collected and have the user provide affirmative consent for such use. Your app must handle user data securely, including transmitting it using modern cryptography (for example, over HTTPS).
这是因为UniPush和个推推送使用的SDK需要获取设备的唯一标识等用于下发推送消息,但没有提示用户确认导致违反谷歌的个人和敏感政策。
HBuilderX2.3.4+版已经更新个推SDK解决此问题
使用UniPush和个推推送时会在应用启动时会弹出隐私政策提示框
以上提示框可能会影响用户的使用体验,因此仅会在打GooglePlay渠道包时生效
提交云端打包时请在渠道包项中选择“GooglePlay”
<a id="noprompt"/>
配置不弹出隐私政策提示框
HBuilder2.6.13+版本支持配置不弹出隐私政策提示框
打开manifest.json文件,切换到源码视图:
- 5+ App项目
在 "plus" -> "distribute" -> "plugins" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段 - uni-app项目
在 "app-plus" -> "distribute" -> "sdkConfigs" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
"push" : {
"unipush" : {
"__privacy_prompt__" : "none" //值为"show"则显示隐私政策提示框
}
},
提交云端打包后生效
离线打包配置
在AndroidManifest.xml文件中添加以下meta-data数据
<meta-data android:name="DCLOUD_PUSH_PRIVACY" android:value="false"/>
收起阅读 »
uniapp H5端不能够定位的问题
- H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)
- 在manifest.json 下面需要配置腾讯地图的key(可自己申请)
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "xxxxxxxxxx"
}
}
}
- H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)
- 在manifest.json 下面需要配置腾讯地图的key(可自己申请)
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "xxxxxxxxxx"
}
}
}
收起阅读 »
准则2.1-性能、运行Wi-Fi在iPad上一个或多个错误问题
很多开发者上架遇到这个问题,苹果那边打不开APP,加载不出来内容!
很多人以为是没有兼容ipad,其实是苹果审核都用ipad,跟有没有支持兼容没有关系。
如果自己在国内测试加载正常,要看APP服务器是否支持国外访问,因为苹果审核在国外,自己用国外网络测试看下!!
具体反馈翻译内容
准则2.1-性能-应用程序完整性
我们在运行Wi-Fi iOS 12.4.1的iPad上查看您的应用程序时发现一个或多个错误。
具体来说,我们无法在应用程序中加载某些内容。
下一步
若要解决此问题,请在设备上运行应用程序以确定任何问题,然后修订并重新提交应用程序以供审阅。
如果我们误解了您的应用程序的预期行为,请在Resolution Center中回复此消息,以提供有关这些功能的预期工作方式的信息。
对于新应用程序,请从设备中卸载所有以前版本的应用程序,然后安装并按照步骤重现问题。对于更新,请将新版本安装为对以前版本的更新,然后按照步骤重现问题。
资源
有关测试应用程序并准备进行审阅的信息,请参阅技术说明TN2431:应用程序测试指南。
有关网络概述,请查看有关网络的信息
很多开发者上架遇到这个问题,苹果那边打不开APP,加载不出来内容!
很多人以为是没有兼容ipad,其实是苹果审核都用ipad,跟有没有支持兼容没有关系。
如果自己在国内测试加载正常,要看APP服务器是否支持国外访问,因为苹果审核在国外,自己用国外网络测试看下!!
具体反馈翻译内容
准则2.1-性能-应用程序完整性
我们在运行Wi-Fi iOS 12.4.1的iPad上查看您的应用程序时发现一个或多个错误。
具体来说,我们无法在应用程序中加载某些内容。
下一步
若要解决此问题,请在设备上运行应用程序以确定任何问题,然后修订并重新提交应用程序以供审阅。
如果我们误解了您的应用程序的预期行为,请在Resolution Center中回复此消息,以提供有关这些功能的预期工作方式的信息。
对于新应用程序,请从设备中卸载所有以前版本的应用程序,然后安装并按照步骤重现问题。对于更新,请将新版本安装为对以前版本的更新,然后按照步骤重现问题。
资源
有关测试应用程序并准备进行审阅的信息,请参阅技术说明TN2431:应用程序测试指南。
有关网络概述,请查看有关网络的信息
收起阅读 »