关于谷歌商店(Google Play Store)应用因stream.dcloud.net下架的公告
目前有开发者反馈,应用在Google Play上架后被审核小组下架,提示违反个人和敏感信息政策,如下提示:
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 DCloud
SDK, which is uploading users Installed Packages information without a
prominent disclosure to http://stream.dcloud.net. 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).
Make sure your app is compliant with the User Data policy and all
other Developer Program Policies. Additional enforcement could occur
if there are further policy violations.
Sign in to your Play Console and upload the modified, policy compliant
APK. Make sure to increment the version number of the APK.
Submit your app.
If you’ve reviewed the policy and feel this removal may have been in
error, please reach out to our policy support team. One of my
colleagues will get back to you within 2 business days.
根据谷歌应用市场反馈的信息,我们排查到应用启动时会发送请求到“http://stream.dcloud.net.cn/device/location”,查询当前设备Webview是否支持需要将标准H5定位API(navigator.geolocation)替换为5+定位API(plus.geolocation),用于解决部分设备在国内无法使用标准H5定位API的问题。
navigator.geolocation这个是H5标准的定位API,但在中国,由于谷歌定位服务被墙,导致很多Android Rom上这个API无法成功定位。
主流浏览器不存在这个问题,它们都已经修改过内核。部分国产Rom也修改了自家webview,可以让navigator.geolocation成功定位。所以这个问题只存在于部分rom的webview中。
自动替换 navigator.geolocation 为 plus.geolocation 是有性能损耗的。所以DCloud官方没有强制全部自动替换。而是在云端维护了一个白名单,根据指定rom来替换。
“http://stream.dcloud.net.cn/device/location”,这个链接请求并没有发送应用安装列表。而是根据rom情况,决定是否替换navigator.geolocation为plus.geolocation。
但谷歌市场仍然认为此请求会提交用户敏感数据,并且使用了http的非加密方式。
当然如果开发者使用了一些其他三方sdk,它们也有可能采集了应用列表信息。
为了尽快帮助开发者恢复上架,已发布新的HBulderX 版本
- 1.9.11 alpha版,该版本临时去掉自动替换navigator.geolocation为5+定位API(plus.geolocation)的逻辑。已被更新的2.0版本替代。
- 2.0 版,该版本打包时,必须选择google play store 渠道包。
在HBuilderX 2.0里,打包选择google play渠道包时,还会自动校验targetSdkVersion。避免提交时才发现targetSdkVersion不符合要求,耽误审核时间。
如果开发者仍需要定位替换功能,将标准H5定位API替换为5+定位API,可以使用以下方法进行配置,这种配置是全部替换,不是根据有问题的rom来替换:
- 创建Webview窗口时设置是否需要替换
var wv = plus.webview.create(url, 'id', { replacewebapi: { geolocation: 'alldevice' } }); - 应用首页可在manifest.json的plus->launchwebview中配置是否需要替换
//... "plus": { "launchwebview": { "replacewebapi": { "geolocation": "alldevice" } }, //... }
另不少开发者关注谷歌的新政策:8月1日前,不提供64位版本APK的应用,将无法通过谷歌play store安装在64位的Android手机上。
HBuilderX新版已经提供支持,详见:https://ask.dcloud.net.cn/article/36195
重新提交google play时注意要把alpha、beta里面的旧版全部清掉后再提交。
提交按钮为灰,不是因为64位,检查页面里其他项目是否填全或填对。
目前有开发者反馈,应用在Google Play上架后被审核小组下架,提示违反个人和敏感信息政策,如下提示:
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 DCloud
SDK, which is uploading users Installed Packages information without a
prominent disclosure to http://stream.dcloud.net. 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).
Make sure your app is compliant with the User Data policy and all
other Developer Program Policies. Additional enforcement could occur
if there are further policy violations.
Sign in to your Play Console and upload the modified, policy compliant
APK. Make sure to increment the version number of the APK.
Submit your app.
If you’ve reviewed the policy and feel this removal may have been in
error, please reach out to our policy support team. One of my
colleagues will get back to you within 2 business days.
根据谷歌应用市场反馈的信息,我们排查到应用启动时会发送请求到“http://stream.dcloud.net.cn/device/location”,查询当前设备Webview是否支持需要将标准H5定位API(navigator.geolocation)替换为5+定位API(plus.geolocation),用于解决部分设备在国内无法使用标准H5定位API的问题。
navigator.geolocation这个是H5标准的定位API,但在中国,由于谷歌定位服务被墙,导致很多Android Rom上这个API无法成功定位。
主流浏览器不存在这个问题,它们都已经修改过内核。部分国产Rom也修改了自家webview,可以让navigator.geolocation成功定位。所以这个问题只存在于部分rom的webview中。
自动替换 navigator.geolocation 为 plus.geolocation 是有性能损耗的。所以DCloud官方没有强制全部自动替换。而是在云端维护了一个白名单,根据指定rom来替换。
“http://stream.dcloud.net.cn/device/location”,这个链接请求并没有发送应用安装列表。而是根据rom情况,决定是否替换navigator.geolocation为plus.geolocation。
但谷歌市场仍然认为此请求会提交用户敏感数据,并且使用了http的非加密方式。
当然如果开发者使用了一些其他三方sdk,它们也有可能采集了应用列表信息。
为了尽快帮助开发者恢复上架,已发布新的HBulderX 版本
- 1.9.11 alpha版,该版本临时去掉自动替换navigator.geolocation为5+定位API(plus.geolocation)的逻辑。已被更新的2.0版本替代。
- 2.0 版,该版本打包时,必须选择google play store 渠道包。
在HBuilderX 2.0里,打包选择google play渠道包时,还会自动校验targetSdkVersion。避免提交时才发现targetSdkVersion不符合要求,耽误审核时间。
如果开发者仍需要定位替换功能,将标准H5定位API替换为5+定位API,可以使用以下方法进行配置,这种配置是全部替换,不是根据有问题的rom来替换:
- 创建Webview窗口时设置是否需要替换
var wv = plus.webview.create(url, 'id', { replacewebapi: { geolocation: 'alldevice' } }); - 应用首页可在manifest.json的plus->launchwebview中配置是否需要替换
//... "plus": { "launchwebview": { "replacewebapi": { "geolocation": "alldevice" } }, //... }
另不少开发者关注谷歌的新政策:8月1日前,不提供64位版本APK的应用,将无法通过谷歌play store安装在64位的Android手机上。
HBuilderX新版已经提供支持,详见:https://ask.dcloud.net.cn/article/36195
重新提交google play时注意要把alpha、beta里面的旧版全部清掉后再提交。
提交按钮为灰,不是因为64位,检查页面里其他项目是否填全或填对。
西安招聘uni-app前端工程师 7K-12K
突然断电后,在hbuilderx编辑器当前打开的文件会变成空白或乱码问题
问题如下,最近几天比较忙一些,笔记本经常忘记插上电源,以前还有提示,现在电池也不给力了,还没到低电量预警状态就自动断电了,问题就来了,在重启电脑后,断电前编辑器打开的文件变成了空白,内容都没有了
,使用sublime text打开此文件显示全是0000
,不过在编辑器中打开的其他处于非编辑状态的文件正常没有问题,出现此情况的hx版本为最新的20190522版本
,以前版本是否有问题不太清楚因为以前没有遇到,首次出现此问题时正在编辑一个重要文件,因为时间比较紧一直没有提交git保存,不能恢复的话多半天工作就白干了,还好在使用了diskgenius扫描了磁盘,发现了以前的数据,恢复到了断电前最后保存的内容,总算有惊无险,不知道其他同学有没有遇到过类似问题,以及官方对此问题有没有解决方案,仅在此记录一下,方便后面同样有问题的同学参考
问题如下,最近几天比较忙一些,笔记本经常忘记插上电源,以前还有提示,现在电池也不给力了,还没到低电量预警状态就自动断电了,问题就来了,在重启电脑后,断电前编辑器打开的文件变成了空白,内容都没有了
,使用sublime text打开此文件显示全是0000
,不过在编辑器中打开的其他处于非编辑状态的文件正常没有问题,出现此情况的hx版本为最新的20190522版本
,以前版本是否有问题不太清楚因为以前没有遇到,首次出现此问题时正在编辑一个重要文件,因为时间比较紧一直没有提交git保存,不能恢复的话多半天工作就白干了,还好在使用了diskgenius扫描了磁盘,发现了以前的数据,恢复到了断电前最后保存的内容,总算有惊无险,不知道其他同学有没有遇到过类似问题,以及官方对此问题有没有解决方案,仅在此记录一下,方便后面同样有问题的同学参考 收起阅读 »
uni-app全局水印
文章废话比较多,主要是想讲一下做这个功能所经历的一些事情,先上代码吧:
由于需要借助canvas生成图片,这里的代码是在应用的首页index.vue执行的,在App.vue无法实现,需要在index.vue中定义
代码写的比较乱,先将就着看,之后再整理
<!-- #ifdef APP-PLUS -->
<block v-if="showWatermark">
<canvas class="watermarkCans" canvas-id="watermarkCanvas"></canvas>
</block>
<!-- #endif -->
下面是js
initWatermark(msg) {
// #ifdef APP-PLUS
let _self = this;
let id = '1.23452384164.123412415';
if (plus.nativeObj.View.getViewById(id) !== null) {
plus.nativeObj.View.getViewById(id).close();
}
let canvasInAppPlusContext = uni.createCanvasContext('watermarkCanvas');
canvasInAppPlusContext.rotate(-30 * Math.PI / 180);
canvasInAppPlusContext.setFontSize(uni.upx2px(28));
canvasInAppPlusContext.setFillStyle('rgba(200, 200, 200, 0.50)');
canvasInAppPlusContext.setTextAlign('left');
canvasInAppPlusContext.setTextBaseline('middle');
canvasInAppPlusContext.fillText(msg, -25, uni.upx2px(170));
canvasInAppPlusContext.draw(false, function() {
uni.canvasToTempFilePath({
canvasId: "watermarkCanvas",
success: function(res) {
_self.showWatermark = false;
let path = res.tempFilePath;
uni.getSystemInfo({
success: function (res) {
//水印排列行数
let row = Math.floor(res.windowHeight / uni.upx2px(250));
let tarArr = [];
for(let i = 0; i < row; i++) {
for(let j = 0; j < 3; j++){
tarArr.push({
tag: 'img',
src: path,
position: {
top: (uni.upx2px(255) * i) + 'px',
left: (uni.upx2px(255) * j) + 'px',
width: uni.upx2px(255) + 'px',
height: uni.upx2px(255) + 'px'
}
});
}
}
var watermarkView = new plus.nativeObj.View(id, {
top:'70px',
left:'0px',
right: '0px',
bottom: '50px'
}, tarArr);
//拦截View控件的触屏事件,将事件穿透给下一层view
watermarkView.interceptTouchEvent(false);
watermarkView.show();
}
});
}
});
});
// #endif
// #ifdef H5
let id = '1.23452384164.123412415';
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id));
}
let can = document.createElement('canvas');
can.width = uni.upx2px(250);
can.height = uni.upx2px(250);
let cans = can.getContext('2d');
cans.rotate(-30 * Math.PI / 180);
cans.font = uni.upx2px(28) + 'px';
cans.fillStyle = 'rgba(200, 200, 200, 0.50)';
cans.textAlign = 'left';
cans.textBaseline = 'Middle';
cans.fillText(msg, -25, uni.upx2px(170));
let div = document.createElement('div');
div.id = id;
div.style.pointerEvents = 'none';
div.style.top = '44px';
div.style.left = '-40px';
div.style.bottom = '50px';
div.style.right = '0px';
div.style.position = 'fixed';
div.style.zIndex = '100000';
// div.style.width = document.documentElement.clientWidth + 'px'
// div.style.height = document.documentElement.clientHeight + 'px'
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat';
document.body.appendChild(div);
return id;
// #endif
}
前两天在研究一个全局水印的功能,需要达到以下的效果:
1、根据给出的文字生成水印
2、水印需要在全局生效,不能跳转页面后消失
3、H5端和APP端都要兼容
根据以上的需求,我研究了以下几种方案:
1、封装水印组件。
做一个漂浮的div,定义z-index层级和pointer-event属性和背景平铺重复,在需要的页面中使用组件。这个实现起来很简单,兼容H5和APP,但是有一个问题需要在每一个页面中去单独引入,不符合全局水印的定义,而且项目要提供给项目组使用,很明显不是一个完美的方案。
2、往页面中插入一个div
实现方式和第一点类似,优点是只能在H5端生效,APP端没有body,这个方案直接被pass了
3、用native.js分别实现安卓和IOS端的效果
但是由于对原生开发不了解,这个方案直接就实现不了了
4、直接丢给原生开发做,再去引入插件
这明显不是个好点子,而且研究了这么久不能半半途而废
尝试了很多方法之后我就思考:有没有这样一个顶层的视图容器的东西,可以让我去插入内容,还不会影响事件的穿透,基于这个想法,我又做了一下尝试
1、应用生命周期
由于对移动开发和vue都不熟,尝试了很多vue的方法,生命周期钩子,createElement,render等等,都失败了。
2、webview
我了解到uni-app在APP端实质上也是一个webview,每一个页面其实是这个webview的子webview,我想在应用的onLaunch里边去获取到顶层的webview,往webview中添加子窗口的方式实现,因为各种原因,实现了一半,但是感觉好像有了苗头
3、plus.nativeObj.View
也就是我最终的实现方式,研究了好久,发现了这个宝藏对象。
plus.nativeObj:可以管理系统原生对象。
plus.nativeObj.View:原生控件对象可用于在屏幕上绘制图片或文本内容,当控件不再使用时需要调用close方法销毁控件。
链接:http://www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
尝试了之后发现可以在屏幕上绘制内容,并且在整个应用生效,开心到爆炸,但是!!又遇到了一个问题,这些内容会形成类似遮罩层的东西,导致事件无法穿透。研究了一番,找到了这样一个方法:view.interceptTouchEvent(false),它可以拦截View控件的触屏事件。
接下来,就是文字生成水印图片的问题了,我用的canvas绘制了一个水印,转换成图片实现。期间遇到了一个问题,APP端的uni.canvasToTempFilePath事件需要放在cans.draw()的回调里去实现。
文章废话比较多,主要是想讲一下做这个功能所经历的一些事情,先上代码吧:
由于需要借助canvas生成图片,这里的代码是在应用的首页index.vue执行的,在App.vue无法实现,需要在index.vue中定义
代码写的比较乱,先将就着看,之后再整理
<!-- #ifdef APP-PLUS -->
<block v-if="showWatermark">
<canvas class="watermarkCans" canvas-id="watermarkCanvas"></canvas>
</block>
<!-- #endif -->
下面是js
initWatermark(msg) {
// #ifdef APP-PLUS
let _self = this;
let id = '1.23452384164.123412415';
if (plus.nativeObj.View.getViewById(id) !== null) {
plus.nativeObj.View.getViewById(id).close();
}
let canvasInAppPlusContext = uni.createCanvasContext('watermarkCanvas');
canvasInAppPlusContext.rotate(-30 * Math.PI / 180);
canvasInAppPlusContext.setFontSize(uni.upx2px(28));
canvasInAppPlusContext.setFillStyle('rgba(200, 200, 200, 0.50)');
canvasInAppPlusContext.setTextAlign('left');
canvasInAppPlusContext.setTextBaseline('middle');
canvasInAppPlusContext.fillText(msg, -25, uni.upx2px(170));
canvasInAppPlusContext.draw(false, function() {
uni.canvasToTempFilePath({
canvasId: "watermarkCanvas",
success: function(res) {
_self.showWatermark = false;
let path = res.tempFilePath;
uni.getSystemInfo({
success: function (res) {
//水印排列行数
let row = Math.floor(res.windowHeight / uni.upx2px(250));
let tarArr = [];
for(let i = 0; i < row; i++) {
for(let j = 0; j < 3; j++){
tarArr.push({
tag: 'img',
src: path,
position: {
top: (uni.upx2px(255) * i) + 'px',
left: (uni.upx2px(255) * j) + 'px',
width: uni.upx2px(255) + 'px',
height: uni.upx2px(255) + 'px'
}
});
}
}
var watermarkView = new plus.nativeObj.View(id, {
top:'70px',
left:'0px',
right: '0px',
bottom: '50px'
}, tarArr);
//拦截View控件的触屏事件,将事件穿透给下一层view
watermarkView.interceptTouchEvent(false);
watermarkView.show();
}
});
}
});
});
// #endif
// #ifdef H5
let id = '1.23452384164.123412415';
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id));
}
let can = document.createElement('canvas');
can.width = uni.upx2px(250);
can.height = uni.upx2px(250);
let cans = can.getContext('2d');
cans.rotate(-30 * Math.PI / 180);
cans.font = uni.upx2px(28) + 'px';
cans.fillStyle = 'rgba(200, 200, 200, 0.50)';
cans.textAlign = 'left';
cans.textBaseline = 'Middle';
cans.fillText(msg, -25, uni.upx2px(170));
let div = document.createElement('div');
div.id = id;
div.style.pointerEvents = 'none';
div.style.top = '44px';
div.style.left = '-40px';
div.style.bottom = '50px';
div.style.right = '0px';
div.style.position = 'fixed';
div.style.zIndex = '100000';
// div.style.width = document.documentElement.clientWidth + 'px'
// div.style.height = document.documentElement.clientHeight + 'px'
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat';
document.body.appendChild(div);
return id;
// #endif
}
前两天在研究一个全局水印的功能,需要达到以下的效果:
1、根据给出的文字生成水印
2、水印需要在全局生效,不能跳转页面后消失
3、H5端和APP端都要兼容
根据以上的需求,我研究了以下几种方案:
1、封装水印组件。
做一个漂浮的div,定义z-index层级和pointer-event属性和背景平铺重复,在需要的页面中使用组件。这个实现起来很简单,兼容H5和APP,但是有一个问题需要在每一个页面中去单独引入,不符合全局水印的定义,而且项目要提供给项目组使用,很明显不是一个完美的方案。
2、往页面中插入一个div
实现方式和第一点类似,优点是只能在H5端生效,APP端没有body,这个方案直接被pass了
3、用native.js分别实现安卓和IOS端的效果
但是由于对原生开发不了解,这个方案直接就实现不了了
4、直接丢给原生开发做,再去引入插件
这明显不是个好点子,而且研究了这么久不能半半途而废
尝试了很多方法之后我就思考:有没有这样一个顶层的视图容器的东西,可以让我去插入内容,还不会影响事件的穿透,基于这个想法,我又做了一下尝试
1、应用生命周期
由于对移动开发和vue都不熟,尝试了很多vue的方法,生命周期钩子,createElement,render等等,都失败了。
2、webview
我了解到uni-app在APP端实质上也是一个webview,每一个页面其实是这个webview的子webview,我想在应用的onLaunch里边去获取到顶层的webview,往webview中添加子窗口的方式实现,因为各种原因,实现了一半,但是感觉好像有了苗头
3、plus.nativeObj.View
也就是我最终的实现方式,研究了好久,发现了这个宝藏对象。
plus.nativeObj:可以管理系统原生对象。
plus.nativeObj.View:原生控件对象可用于在屏幕上绘制图片或文本内容,当控件不再使用时需要调用close方法销毁控件。
链接:http://www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
尝试了之后发现可以在屏幕上绘制内容,并且在整个应用生效,开心到爆炸,但是!!又遇到了一个问题,这些内容会形成类似遮罩层的东西,导致事件无法穿透。研究了一番,找到了这样一个方法:view.interceptTouchEvent(false),它可以拦截View控件的触屏事件。
接下来,就是文字生成水印图片的问题了,我用的canvas绘制了一个水印,转换成图片实现。期间遇到了一个问题,APP端的uni.canvasToTempFilePath事件需要放在cans.draw()的回调里去实现。
uni.canvasToTempFilePath的坑
昨天做一个将canvas转图片的功能,发现以下问题:
1、在APP端,uni.canvasToTempFilePath方法需要放在convasContext.draw的回调里面,否则会一直报canvas is empty
2、在H5端,需要convasContext.draw与uni.canvasToTempFilePath同步执行,也就是不能放在回调里
3、在APP端uni.canvasToTempFilePath返回的路径是一个临时的图片路径
4、在H5端,生成的是base64路径
昨天做一个将canvas转图片的功能,发现以下问题:
1、在APP端,uni.canvasToTempFilePath方法需要放在convasContext.draw的回调里面,否则会一直报canvas is empty
2、在H5端,需要convasContext.draw与uni.canvasToTempFilePath同步执行,也就是不能放在回调里
3、在APP端uni.canvasToTempFilePath返回的路径是一个临时的图片路径
4、在H5端,生成的是base64路径
uniapp配置request请求的networktimeout
在uniapp中,默认的networktimeout是6000ms,也就是六秒。虽然我在使用时等了好久才进入到fail方法体内,具体也没去测量,总之就是很影响用户体验。那么怎么更改请
求中的networktimeout属性呢?
在uniapp中,networktimeout是属于配置项里面的内容,所以得在配置项里面对request进行配置。
① 点击项目目录下的manifest.json会进入到配置页面,而默认页面只能更改appid,名称,版本号等少量内容,这时需要点击左下方的源码视图进行配置。
② 点击源码视图进入之后你会发现一堆的配置文件。具体参考https://uniapp.dcloud.io/collocation/manifest
③ 根据官方提供的配置说明https://uniapp.dcloud.io/collocation/manifest?id=networktimeout 对照着源码试图进行查看。我们会发现并没有networktimeout这一配置。那么我们
需要增加这一项配置。
④ 在与name同级的地方新增networkTimeout属性,由于该属性下还具有更多的参数可以指定如(connectSocket,uploadFile,downloadFile),所以采用大括号的形式进行
赋值。如
"name" : "firstapp",
"appid" : "__UNI__A0A853F",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"networkTimeout":{
"request":3000
},
........
设置其他的请求超时时间也同理。
新手入门,自学中,总结得有错的地方请指正,别骂我,我怂...
在uniapp中,默认的networktimeout是6000ms,也就是六秒。虽然我在使用时等了好久才进入到fail方法体内,具体也没去测量,总之就是很影响用户体验。那么怎么更改请
求中的networktimeout属性呢?
在uniapp中,networktimeout是属于配置项里面的内容,所以得在配置项里面对request进行配置。
① 点击项目目录下的manifest.json会进入到配置页面,而默认页面只能更改appid,名称,版本号等少量内容,这时需要点击左下方的源码视图进行配置。
② 点击源码视图进入之后你会发现一堆的配置文件。具体参考https://uniapp.dcloud.io/collocation/manifest
③ 根据官方提供的配置说明https://uniapp.dcloud.io/collocation/manifest?id=networktimeout 对照着源码试图进行查看。我们会发现并没有networktimeout这一配置。那么我们
需要增加这一项配置。
④ 在与name同级的地方新增networkTimeout属性,由于该属性下还具有更多的参数可以指定如(connectSocket,uploadFile,downloadFile),所以采用大括号的形式进行
赋值。如
"name" : "firstapp",
"appid" : "__UNI__A0A853F",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"networkTimeout":{
"request":3000
},
........
设置其他的请求超时时间也同理。
新手入门,自学中,总结得有错的地方请指正,别骂我,我怂...
收起阅读 »失踪人口从apicloud正式回归dcloud
dcloud一下简称D公司
我工作中第一个正式用的IDE编辑器是D公司的hbuider。
虽然这之前也用过sublimit和zend和PHP风暴等等,但总体来说hbuider更适合我,
以及目前开发PHP后端仍然再用hbuider。
三年前因为需要开发App,
用了D公司之后发现问题大了去了,
因为我是PHP人员,不会安卓和苹果的第三方SDK打包,
所以就用了apicloud,
时至今日,我发现D公司终于为我等这种人解决了第三方SDK的难题,
而且uni-app的强大,从新让我跪舔。
dcloud一下简称D公司
我工作中第一个正式用的IDE编辑器是D公司的hbuider。
虽然这之前也用过sublimit和zend和PHP风暴等等,但总体来说hbuider更适合我,
以及目前开发PHP后端仍然再用hbuider。
三年前因为需要开发App,
用了D公司之后发现问题大了去了,
因为我是PHP人员,不会安卓和苹果的第三方SDK打包,
所以就用了apicloud,
时至今日,我发现D公司终于为我等这种人解决了第三方SDK的难题,
而且uni-app的强大,从新让我跪舔。
关于dataset的不同表现方式和解决方式
先看一波简单源代码:
<template>
<view @tap="tapFunc" data-param="param">
点我
</view>
</template>
<script>
export default {
data() {
return {
param: {
a : 1,
b : 2
}
}
},
onLoad() {
},
methods: {
tapFunc(e){
// e.currentTarget.dataset.param = ????; 在H5环境中需要从新赋值即可解决 e.currentTarget.dataset.param = this.param;
}
}
}
</script>
<style>
</style>
请注意,方法tapFunc里面的e.currentTarget.dataset.param获取到的dataset是根据环境不同的,
H5环境获取到的是字符串[objct],不能进行任何操作,单纯只是完全的字符串[objct],
微信小程序环境中获取到的objct对象,没问题。
所以在H5环境中需要从新赋值即可解决 e.currentTarget.dataset.param = this.param;
先看一波简单源代码:
<template>
<view @tap="tapFunc" data-param="param">
点我
</view>
</template>
<script>
export default {
data() {
return {
param: {
a : 1,
b : 2
}
}
},
onLoad() {
},
methods: {
tapFunc(e){
// e.currentTarget.dataset.param = ????; 在H5环境中需要从新赋值即可解决 e.currentTarget.dataset.param = this.param;
}
}
}
</script>
<style>
</style>
请注意,方法tapFunc里面的e.currentTarget.dataset.param获取到的dataset是根据环境不同的,
H5环境获取到的是字符串[objct],不能进行任何操作,单纯只是完全的字符串[objct],
微信小程序环境中获取到的objct对象,没问题。
所以在H5环境中需要从新赋值即可解决 e.currentTarget.dataset.param = this.param;
关于页面栈中getCurrentPages()方法,H5和微信小程序的不同表现
H5没问题。
微信小程序中返回的是微信小程序页面栈,
其里面有一个$vm,才是真实的页面栈。
所以统一页面栈获取方法是
let pages = getCurrentPages();
// #ifdef MP-WEIXIN
return pages[pages.length - 1].$vm;
// #endif
return pages[pages.length - 1]; H5没问题。
微信小程序中返回的是微信小程序页面栈,
其里面有一个$vm,才是真实的页面栈。
所以统一页面栈获取方法是
let pages = getCurrentPages();
// #ifdef MP-WEIXIN
return pages[pages.length - 1].$vm;
// #endif
return pages[pages.length - 1]; 收起阅读 »
h5显示图片正常,app与小程序图片不显示
问题说明:
H5页面图片显示正常,app端与小程序端不显示图片
我怀疑是v-bind:src="item.url"不适用用手机端或者小程序。但是查了所有文档都没找到证据。
最后比对了几个页面得出结论,如果在图片组件前面了加了过滤器filter,那么图片就会不显示,只有h5端显示正常。
替换方案:使用计算方法替换过滤器。
所以,是过滤器不支持手机端和小程序端的原因吗???
根据官方回复说,自定义组件才能使用filter(做笔记)。
问题说明:
H5页面图片显示正常,app端与小程序端不显示图片
我怀疑是v-bind:src="item.url"不适用用手机端或者小程序。但是查了所有文档都没找到证据。
最后比对了几个页面得出结论,如果在图片组件前面了加了过滤器filter,那么图片就会不显示,只有h5端显示正常。
替换方案:使用计算方法替换过滤器。
所以,是过滤器不支持手机端和小程序端的原因吗???
根据官方回复说,自定义组件才能使用filter(做笔记)。
收起阅读 »uni-app subNVue 原生子窗体开发指南
此功能需要 HBuilderX 版本 1.9.10+, 不支持非自定义组件模式。
需求背景
在我们的开发中,经常会遇到各种层级覆盖和原生界面自定义的问题:
- 覆盖原生导航栏、
tabbar的弹出层组件。比如侧滑菜单盖不住地图、视频、原生导航栏,比如popup盖不住tabbar。 - 弹出层内部元素可滚动,
- 在地图、视频等组件上的添加复杂覆盖组件:比如直播视频上覆盖滚动的聊天记录。
在小程序中只能用 cover-view 来解决。App中,开发者希望有更强的解决方案。
当然在App端使用nvue是不存在前端元素无法覆盖原生元素的层级问题的,但app-vue页面仍然需要面对复杂的层级问题:
- app-vue的
cover-view不支持嵌套、只能在video、map上使用、样式和控件少; plus.nativeObj.view虽然更灵活,但易用性比较差、没有动画、不支持内部内容滚动。
既然uni-app已经支持 nvue 的原生渲染,我们何不做一个subNVue,来替代 cover-view,实现更强的功能?
顾名思义,subNVue是 vue 页面的子窗体,它不是全屏页面,就是用于解决 vue 页面中的层级覆盖和原生界面自定义用的。它也不是组件,就是一个原生子窗体
在新版的hello uni-app里,接口-界面-原生子窗体新增了subNVue 示例。包括了4个 subNVue 示例:
- 顶部原生的渐变背景色导航栏(注:此示例其实已过期,HBuilderX 2.6.6起pages.json自带的titleNView已经可以实现渐变背景色和更多自定义能力,性能是高于subnvue方案的)
- 侧滑菜单,可以盖住原生视频
- 弹出一个原生的
popup,并且内部内容可滚动 - 视频上覆盖一个滚动聊天记录
有了 subNVue,插件市场的一些插件就没有意义了,比如这个原生增强提示框插件,完全可以用 subNVue 替代,免去原生插件打包的麻烦。
在通信方面: subNVue 页面可以和 vue 页面进行通信,来告知 vue 页面用户执行的操作。或者通过 vue 页面对 subNVue 进行数据和状态的更新。 subNVue 除了与 vue 页面进行通信,还 可以与 nvue 页面进行通信。
使用 subNVue 子窗体的页面结构
我们建议 subNVue 子窗体与引用该子窗体的vue页面放在同一目录下,新建 subNVue 目录包含这些 subNVue 子窗体,例如:
|-- pages
|-- index // index 目录
| |-- subNVue // subNVue 目录
| |-- nav.nvue // 自定义导航栏
| |-- popup.nvue // 弹出层子窗体
|-- index.vue // index 页面
当然你也可以提供公共的 subNVue 子窗体,供多个 vue 页面引用,此时我们建议放在 最外层与 pages 文件同级的 platform\app-plus\subNVue 下。(只是建议,不是约束。不管放哪里,只要 pages.json 里引用了,都会编译到App端)
使用 subNVue 子窗体的 pages.json 配置
在 pages.json 中,新增了 subNVues 节点, 与 titleNView 在同一级别。支持配置 subNVue 子窗体的相关属性。配置结构如下:
subNVues:
- id: [String], 全局唯一,不能重复。
- path: [String], subNVue 子窗体的路径。
- type: [String], 内置的特殊子窗体类型,弹出(popup)和导航(navigationBar)。
- style: [Object], 配置子窗体的位置,背景等样式属性。
代码示例:
{
"pages": [{
"path": "pages/index/index", //首页
"style": {
"app-plus": {
"subNVues":[{
"id": "concat", // 唯一标识
"path": "pages/index/subnvue/concat", // 页面路径
/*"type": "popup", 这里不需要*/
"style": {
"position": "absolute",
"dock": "right",
"width": "100rpx",
"height": "150rpx",
"background": "transparent"
}
}]
}
}
}]
}
关于 subNVue 更多详细的配置见: 完整配置
注意事项:
id属性是全局唯一的,path路径只能是nuve页面路径type属性目前只有导航栏 (navigationBar) 和弹出层 (popup) 类型,且级别最高,一旦设置type为navigationBar或popup,position和dock的值都会被忽略。position为原生子窗体的定位方式。dock表示原生子窗体的停靠位置,只有当position值为dock时才生效,如top,bottom,right,left等。- 在配置中可以使用 upx 单位,方便你进行响应式布局。
subNVue 子窗体书写
subNVue 子窗体引用的是 nvue 页面。所以只需要书写 nvue 页面。
需要注意的是,nvue 与 vue 页面的开发注意事项。两者开发起来还是有一些区别。
相关参考
- 使用
nvue开发注意事项:https://uniapp.dcloud.io/use-weex - 使用
vue开发注意事项:https://uniapp.dcloud.io/use
怎么在页面中使用 subNVue 子窗体
在 pages.json 中增加完配置,也写好了 subNVue 子窗体,接下来就是在 vue/nvue 页面中使用了。 在 vue 和 nvue 页面中使用方式是一样的,这里以 vue 页面为例进行说明:
在页面中打开和关闭 subNVue 子窗体
// 通过 id 获取 nvue 子窗体
const subNVue = uni.getSubNVueById('map_widget')
// 打开 nvue 子窗体
subNVue.show('slide-in-left', 300, function(){
// 打开后进行一些操作...
//
});
// 关闭 nvue 子窗体
subNVue.hide('fade-out', 300)
动态修改 subNVue 子窗体位置,大小
subNVue.setStyle({
top: '100px',
left: '20px',
width: '100px',
height = '50px',
})
subNVue 子窗体与 vue/nvue 页面通信
无论是页面与页面,子窗体与子窗体之间,如果没有了彼此之间的通信,都只是孤立的散件而已。 nvue 子窗体与使用子窗体的 vue/nvue 页面之间,可以互相发送和传递消息,进而实现彼此之间的互相更新和表现协调。 在 vue 和 nvue 中进行通信的方式一致,这里仍然以 vue 页面为例:
推荐使用页面通讯完成与子窗体通讯(新增)
关于页面通讯的内容详见: 页面通讯指南
通讯实现方式
// 在 subNVue/vue 页面注册事件监听方法
// $on(eventName, callback)
uni.$on('page-popup', (data) => {
vm.title = data.title;
vm.content = data.content;
})
// 在 subNVue/vue 页面触发事件
// $emit(eventName, data)
uni.$emit('page-popup', {
title: '我是一个title',
content: '我是data content'
});
使用页面通讯时注意事项: 要在页面卸载前,使用 uni.$off 移除事件监听器。
旧的通讯方式(推荐上述使用页面通讯机制)
vue 页面中监听 subNVue 子窗体的消息和向 subNVue 子窗体传递消息
// 获取要通信的 subNVue 子窗体
const subNVue = uni.getSubNVueById('map_widget')
// vue 向 subNVue 子窗体发送消息
// postMessage(<Object>)
subNVue.postMessage({
type: 'message',
title: '我是来自 vue 页面的消息',
content: 'Hello, map_widget'
});
// vue 监听 subNVue 子窗体传递的消息
subNVue.onMessage((res) => {
const data = res.data;
// 执行一些操作
});
subNVue 子窗体监听 vue 页面的消息和向 vue 页面发送消息
// 获取当前 subNVue 子窗体
// 可以使用 getSubNVueById 查找的方式,但推荐使用下面的方式
const subNVue = uni.getCurrentSubNVue();
// subNVue 子窗体向 vue 页面发送消息
// postMessage(<Object>)
subNVue.postMessage({
type: 'message',
title: '我是来自 subNVue 子窗体的消息',
content: 'Hello, map_widget'
});
// subNVue 子窗体监听 vue 页面传递的消息
subNVue.onMessage((res) => {
const data = res.data;
// 执行一些操作
});
总结
基本的使用方式和场景已经介绍完了, 对于使用 subNVue 在更多的应用场景中去实现更多的功能,就需要大家去不断的尝试和创新了。
当然如果一些简单的需求,如果 cover-view 已经能搞定,那也没必要使用subNVue,毕竟能跨端,内存占用也更低。
强大的东西往往也意味着消耗更多内存,为了保证更好的性能体验,一个vue页面不要加载太多 subNVue 子窗体,建议控制在三个以内。
注意事项:
在使用 subNVue 子窗体的页面中,同时满足下面两种情形时:
-
页面包含 map, video 之类的原生组件
-
页面使用了 type 为 navigationBar 的 subNVue 子窗体
原生组件可能会出现错位的问题,目前可以使用以下方法进行解决:
- 将此类元素放在页面的 onReady 中进行渲染。
- 采用延时的策略,保证元素在页面渲染后,再去定位位置。
此功能需要 HBuilderX 版本 1.9.10+, 不支持非自定义组件模式。
需求背景
在我们的开发中,经常会遇到各种层级覆盖和原生界面自定义的问题:
- 覆盖原生导航栏、
tabbar的弹出层组件。比如侧滑菜单盖不住地图、视频、原生导航栏,比如popup盖不住tabbar。 - 弹出层内部元素可滚动,
- 在地图、视频等组件上的添加复杂覆盖组件:比如直播视频上覆盖滚动的聊天记录。
在小程序中只能用 cover-view 来解决。App中,开发者希望有更强的解决方案。
当然在App端使用nvue是不存在前端元素无法覆盖原生元素的层级问题的,但app-vue页面仍然需要面对复杂的层级问题:
- app-vue的
cover-view不支持嵌套、只能在video、map上使用、样式和控件少; plus.nativeObj.view虽然更灵活,但易用性比较差、没有动画、不支持内部内容滚动。
既然uni-app已经支持 nvue 的原生渲染,我们何不做一个subNVue,来替代 cover-view,实现更强的功能?
顾名思义,subNVue是 vue 页面的子窗体,它不是全屏页面,就是用于解决 vue 页面中的层级覆盖和原生界面自定义用的。它也不是组件,就是一个原生子窗体
在新版的hello uni-app里,接口-界面-原生子窗体新增了subNVue 示例。包括了4个 subNVue 示例:
- 顶部原生的渐变背景色导航栏(注:此示例其实已过期,HBuilderX 2.6.6起pages.json自带的titleNView已经可以实现渐变背景色和更多自定义能力,性能是高于subnvue方案的)
- 侧滑菜单,可以盖住原生视频
- 弹出一个原生的
popup,并且内部内容可滚动 - 视频上覆盖一个滚动聊天记录
有了 subNVue,插件市场的一些插件就没有意义了,比如这个原生增强提示框插件,完全可以用 subNVue 替代,免去原生插件打包的麻烦。
在通信方面: subNVue 页面可以和 vue 页面进行通信,来告知 vue 页面用户执行的操作。或者通过 vue 页面对 subNVue 进行数据和状态的更新。 subNVue 除了与 vue 页面进行通信,还 可以与 nvue 页面进行通信。
使用 subNVue 子窗体的页面结构
我们建议 subNVue 子窗体与引用该子窗体的vue页面放在同一目录下,新建 subNVue 目录包含这些 subNVue 子窗体,例如:
|-- pages
|-- index // index 目录
| |-- subNVue // subNVue 目录
| |-- nav.nvue // 自定义导航栏
| |-- popup.nvue // 弹出层子窗体
|-- index.vue // index 页面
当然你也可以提供公共的 subNVue 子窗体,供多个 vue 页面引用,此时我们建议放在 最外层与 pages 文件同级的 platform\app-plus\subNVue 下。(只是建议,不是约束。不管放哪里,只要 pages.json 里引用了,都会编译到App端)
使用 subNVue 子窗体的 pages.json 配置
在 pages.json 中,新增了 subNVues 节点, 与 titleNView 在同一级别。支持配置 subNVue 子窗体的相关属性。配置结构如下:
subNVues:
- id: [String], 全局唯一,不能重复。
- path: [String], subNVue 子窗体的路径。
- type: [String], 内置的特殊子窗体类型,弹出(popup)和导航(navigationBar)。
- style: [Object], 配置子窗体的位置,背景等样式属性。
代码示例:
{
"pages": [{
"path": "pages/index/index", //首页
"style": {
"app-plus": {
"subNVues":[{
"id": "concat", // 唯一标识
"path": "pages/index/subnvue/concat", // 页面路径
/*"type": "popup", 这里不需要*/
"style": {
"position": "absolute",
"dock": "right",
"width": "100rpx",
"height": "150rpx",
"background": "transparent"
}
}]
}
}
}]
}
关于 subNVue 更多详细的配置见: 完整配置
注意事项:
id属性是全局唯一的,path路径只能是nuve页面路径type属性目前只有导航栏 (navigationBar) 和弹出层 (popup) 类型,且级别最高,一旦设置type为navigationBar或popup,position和dock的值都会被忽略。position为原生子窗体的定位方式。dock表示原生子窗体的停靠位置,只有当position值为dock时才生效,如top,bottom,right,left等。- 在配置中可以使用 upx 单位,方便你进行响应式布局。
subNVue 子窗体书写
subNVue 子窗体引用的是 nvue 页面。所以只需要书写 nvue 页面。
需要注意的是,nvue 与 vue 页面的开发注意事项。两者开发起来还是有一些区别。
相关参考
- 使用
nvue开发注意事项:https://uniapp.dcloud.io/use-weex - 使用
vue开发注意事项:https://uniapp.dcloud.io/use
怎么在页面中使用 subNVue 子窗体
在 pages.json 中增加完配置,也写好了 subNVue 子窗体,接下来就是在 vue/nvue 页面中使用了。 在 vue 和 nvue 页面中使用方式是一样的,这里以 vue 页面为例进行说明:
在页面中打开和关闭 subNVue 子窗体
// 通过 id 获取 nvue 子窗体
const subNVue = uni.getSubNVueById('map_widget')
// 打开 nvue 子窗体
subNVue.show('slide-in-left', 300, function(){
// 打开后进行一些操作...
//
});
// 关闭 nvue 子窗体
subNVue.hide('fade-out', 300)
动态修改 subNVue 子窗体位置,大小
subNVue.setStyle({
top: '100px',
left: '20px',
width: '100px',
height = '50px',
})
subNVue 子窗体与 vue/nvue 页面通信
无论是页面与页面,子窗体与子窗体之间,如果没有了彼此之间的通信,都只是孤立的散件而已。 nvue 子窗体与使用子窗体的 vue/nvue 页面之间,可以互相发送和传递消息,进而实现彼此之间的互相更新和表现协调。 在 vue 和 nvue 中进行通信的方式一致,这里仍然以 vue 页面为例:
推荐使用页面通讯完成与子窗体通讯(新增)
关于页面通讯的内容详见: 页面通讯指南
通讯实现方式
// 在 subNVue/vue 页面注册事件监听方法
// $on(eventName, callback)
uni.$on('page-popup', (data) => {
vm.title = data.title;
vm.content = data.content;
})
// 在 subNVue/vue 页面触发事件
// $emit(eventName, data)
uni.$emit('page-popup', {
title: '我是一个title',
content: '我是data content'
});
使用页面通讯时注意事项: 要在页面卸载前,使用 uni.$off 移除事件监听器。
旧的通讯方式(推荐上述使用页面通讯机制)
vue 页面中监听 subNVue 子窗体的消息和向 subNVue 子窗体传递消息
// 获取要通信的 subNVue 子窗体
const subNVue = uni.getSubNVueById('map_widget')
// vue 向 subNVue 子窗体发送消息
// postMessage(<Object>)
subNVue.postMessage({
type: 'message',
title: '我是来自 vue 页面的消息',
content: 'Hello, map_widget'
});
// vue 监听 subNVue 子窗体传递的消息
subNVue.onMessage((res) => {
const data = res.data;
// 执行一些操作
});
subNVue 子窗体监听 vue 页面的消息和向 vue 页面发送消息
// 获取当前 subNVue 子窗体
// 可以使用 getSubNVueById 查找的方式,但推荐使用下面的方式
const subNVue = uni.getCurrentSubNVue();
// subNVue 子窗体向 vue 页面发送消息
// postMessage(<Object>)
subNVue.postMessage({
type: 'message',
title: '我是来自 subNVue 子窗体的消息',
content: 'Hello, map_widget'
});
// subNVue 子窗体监听 vue 页面传递的消息
subNVue.onMessage((res) => {
const data = res.data;
// 执行一些操作
});
总结
基本的使用方式和场景已经介绍完了, 对于使用 subNVue 在更多的应用场景中去实现更多的功能,就需要大家去不断的尝试和创新了。
当然如果一些简单的需求,如果 cover-view 已经能搞定,那也没必要使用subNVue,毕竟能跨端,内存占用也更低。
强大的东西往往也意味着消耗更多内存,为了保证更好的性能体验,一个vue页面不要加载太多 subNVue 子窗体,建议控制在三个以内。
注意事项:
在使用 subNVue 子窗体的页面中,同时满足下面两种情形时:
-
页面包含 map, video 之类的原生组件
-
页面使用了 type 为 navigationBar 的 subNVue 子窗体
原生组件可能会出现错位的问题,目前可以使用以下方法进行解决:
- 将此类元素放在页面的 onReady 中进行渲染。
- 采用延时的策略,保证元素在页面渲染后,再去定位位置。







