
【通知权限】app判断通知权限是否开启,前往系统设置页面开关通知权限
来源:https://ext.dcloud.net.cn/plugin?id=789
// 判断通知权限是否开启
function isOn() {
// #ifdef APP-PLUS
if (plus.os.name == "Android") {
var main = plus.android.runtimeMainActivity();
var NotificationManagerCompat = plus.android.importClass("android.support.v4.app.NotificationManagerCompat");
if (NotificationManagerCompat == null) {
NotificationManagerCompat = plus.android.importClass("androidx.core.app.NotificationManagerCompat");
}
var areNotificationsEnabled = NotificationManagerCompat.from(main).areNotificationsEnabled();
return areNotificationsEnabled;
} else if (plus.os.name == "iOS") {
var isIosOn = undefined;
var types = 0;
var app = plus.ios.invoke("UIApplication", "sharedApplication");
var settings = plus.ios.invoke(app, "currentUserNotificationSettings");
if (settings) {
types = settings.plusGetAttribute("types");
plus.ios.deleteObject(settings);
} else {
types = plus.ios.invoke(app, "enabledRemoteNotificationTypes");
}
plus.ios.deleteObject(app);
isIosOn = 0 != types;
return isIosOn;
}
// #endif
}
// 前往系统设置页面开启或关闭通知权限
function switchPushPermissions() {
// #ifdef APP-PLUS
let title = isOn() ? "通知权限关闭提醒" : "通知权限开启提醒";
let content = isOn()
? "通知权限已开启,是否前往设置关闭?"
: "您还没有开启通知权限,无法接受到消息通知,是否前往设置?";
// 打开安卓系统设置页面
let openAndroidSetting = () => {
var main = plus.android.runtimeMainActivity();
var pkName = main.getPackageName();
var uid = main.getApplicationInfo().plusGetAttribute("uid");
var Intent = plus.android.importClass("android.content.Intent");
var Build = plus.android.importClass("android.os.Build");
//android 8.0引导
if (Build.VERSION.SDK_INT >= 26) {
var intent = new Intent("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("android.provider.extra.APP_PACKAGE", pkName);
} else if (Build.VERSION.SDK_INT >= 21) {
//android 5.0-7.0
var intent = new Intent("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("app_package", pkName);
intent.putExtra("app_uid", uid);
} else {
//(<21)其他--跳转到该应用管理的详情页
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
}
// 跳转到该应用的系统通知设置页
main.startActivity(intent);
};
// 打开苹果系统设置页面
let openIOSSetting = () => {
var app = plus.ios.invoke("UIApplication", "sharedApplication");
var setting = plus.ios.invoke("NSURL", "URLWithString:", "app-settings:");
plus.ios.invoke(app, "openURL:", setting);
plus.ios.deleteObject(setting);
plus.ios.deleteObject(app);
// ios18跳转不过去的解决方案
// 方案一:使用uni.openAppAuthorizeSetting(),文档地址:https://uniapp.dcloud.net.cn/api/system/openappauthorizesetting.html
// 方案二:待验证;来源评论区:iOS 18以后,openURL:完全被弃用,需要用openURL:options:completionHandler:替代。
// var app = plus.ios.invoke("UIApplication", "sharedApplication");
// var url;
// // 获取安全 URL(iOS 8+)
// if (plus.ios.invoke("UIApplication", "respondsToSelector:", "openSettingsURLString")) {
// var urlString = plus.ios.invoke("UIApplication", "openSettingsURLString");
// url = plus.ios.invoke("NSURL", "URLWithString:", urlString);
// } else {
// url = plus.ios.invoke("NSURL", "URLWithString:", "app-settings:");
// }
// // 判断新 API 可用性(iOS 10+)
// var supportsNewAPI = plus.ios.invoke(app, "respondsToSelector:", "openURL:options:completionHandler:");
// if (supportsNewAPI) {
// var options = plus.ios.newObject("NSDictionary");
// var completion = plus.ios.newBlock(function(success) {
// console.log("跳转结果:" + success);
// if (!success) {
// plus.nativeUI.toast("无法打开设置,请手动前往");
// }
// }, "v@:B");
// plus.ios.invoke(app, "openURL:options:completionHandler:", url, options, completion);
// plus.ios.deleteObject(completion);
// } else {
// if (plus.ios.invoke(app, "canOpenURL:", url)) {
// plus.ios.invoke(app, "openURL:", url);
// } else {
// throw new Error("当前系统不支持跳转设置");
// }
// }
// plus.ios.deleteObject(url);
// plus.ios.deleteObject(app);
};
// 弹窗提醒开通或关闭权限,点击确认后,跳转到系统设置页面进行设置
uni.showModal({
title: title,
content: content,
showCancel: true,
confirmColor: "#ff903f",
success: (res) => {
if (res.confirm) {
if (plus.os.name == "Android") {
openAndroidSetting();
} else if (plus.os.name == "iOS") {
openIOSSetting();
}
}
},
});
// #endif
}
export default {
isOn,
switchPushPermissions,
};
↓↓↓ 各位大佬点点赞
来源:https://ext.dcloud.net.cn/plugin?id=789
// 判断通知权限是否开启
function isOn() {
// #ifdef APP-PLUS
if (plus.os.name == "Android") {
var main = plus.android.runtimeMainActivity();
var NotificationManagerCompat = plus.android.importClass("android.support.v4.app.NotificationManagerCompat");
if (NotificationManagerCompat == null) {
NotificationManagerCompat = plus.android.importClass("androidx.core.app.NotificationManagerCompat");
}
var areNotificationsEnabled = NotificationManagerCompat.from(main).areNotificationsEnabled();
return areNotificationsEnabled;
} else if (plus.os.name == "iOS") {
var isIosOn = undefined;
var types = 0;
var app = plus.ios.invoke("UIApplication", "sharedApplication");
var settings = plus.ios.invoke(app, "currentUserNotificationSettings");
if (settings) {
types = settings.plusGetAttribute("types");
plus.ios.deleteObject(settings);
} else {
types = plus.ios.invoke(app, "enabledRemoteNotificationTypes");
}
plus.ios.deleteObject(app);
isIosOn = 0 != types;
return isIosOn;
}
// #endif
}
// 前往系统设置页面开启或关闭通知权限
function switchPushPermissions() {
// #ifdef APP-PLUS
let title = isOn() ? "通知权限关闭提醒" : "通知权限开启提醒";
let content = isOn()
? "通知权限已开启,是否前往设置关闭?"
: "您还没有开启通知权限,无法接受到消息通知,是否前往设置?";
// 打开安卓系统设置页面
let openAndroidSetting = () => {
var main = plus.android.runtimeMainActivity();
var pkName = main.getPackageName();
var uid = main.getApplicationInfo().plusGetAttribute("uid");
var Intent = plus.android.importClass("android.content.Intent");
var Build = plus.android.importClass("android.os.Build");
//android 8.0引导
if (Build.VERSION.SDK_INT >= 26) {
var intent = new Intent("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("android.provider.extra.APP_PACKAGE", pkName);
} else if (Build.VERSION.SDK_INT >= 21) {
//android 5.0-7.0
var intent = new Intent("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("app_package", pkName);
intent.putExtra("app_uid", uid);
} else {
//(<21)其他--跳转到该应用管理的详情页
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
}
// 跳转到该应用的系统通知设置页
main.startActivity(intent);
};
// 打开苹果系统设置页面
let openIOSSetting = () => {
var app = plus.ios.invoke("UIApplication", "sharedApplication");
var setting = plus.ios.invoke("NSURL", "URLWithString:", "app-settings:");
plus.ios.invoke(app, "openURL:", setting);
plus.ios.deleteObject(setting);
plus.ios.deleteObject(app);
// ios18跳转不过去的解决方案
// 方案一:使用uni.openAppAuthorizeSetting(),文档地址:https://uniapp.dcloud.net.cn/api/system/openappauthorizesetting.html
// 方案二:待验证;来源评论区:iOS 18以后,openURL:完全被弃用,需要用openURL:options:completionHandler:替代。
// var app = plus.ios.invoke("UIApplication", "sharedApplication");
// var url;
// // 获取安全 URL(iOS 8+)
// if (plus.ios.invoke("UIApplication", "respondsToSelector:", "openSettingsURLString")) {
// var urlString = plus.ios.invoke("UIApplication", "openSettingsURLString");
// url = plus.ios.invoke("NSURL", "URLWithString:", urlString);
// } else {
// url = plus.ios.invoke("NSURL", "URLWithString:", "app-settings:");
// }
// // 判断新 API 可用性(iOS 10+)
// var supportsNewAPI = plus.ios.invoke(app, "respondsToSelector:", "openURL:options:completionHandler:");
// if (supportsNewAPI) {
// var options = plus.ios.newObject("NSDictionary");
// var completion = plus.ios.newBlock(function(success) {
// console.log("跳转结果:" + success);
// if (!success) {
// plus.nativeUI.toast("无法打开设置,请手动前往");
// }
// }, "v@:B");
// plus.ios.invoke(app, "openURL:options:completionHandler:", url, options, completion);
// plus.ios.deleteObject(completion);
// } else {
// if (plus.ios.invoke(app, "canOpenURL:", url)) {
// plus.ios.invoke(app, "openURL:", url);
// } else {
// throw new Error("当前系统不支持跳转设置");
// }
// }
// plus.ios.deleteObject(url);
// plus.ios.deleteObject(app);
};
// 弹窗提醒开通或关闭权限,点击确认后,跳转到系统设置页面进行设置
uni.showModal({
title: title,
content: content,
showCancel: true,
confirmColor: "#ff903f",
success: (res) => {
if (res.confirm) {
if (plus.os.name == "Android") {
openAndroidSetting();
} else if (plus.os.name == "iOS") {
openIOSSetting();
}
}
},
});
// #endif
}
export default {
isOn,
switchPushPermissions,
};
↓↓↓ 各位大佬点点赞
收起阅读 »
页面向上滚动的情况和居中样式
描述:点击元素后,页面滚动到顶部,
有可能是滚动元素绑定了某个事件并执行滚动相关的方法,
如:给滚动元素添加一个事件并执行滚动元素.scrollIntoView(true); 为true时,是滚动到顶部。
在元素出发相应的事件就会页面滚动到顶部
居中方式:
这里有种居中方式:绝定定位+margin:0
可以实现上下左右居中。
如果只左右居中,margin-left:0;margin-right:0;
如果只左右居中,margin-top:0;margin-bottom:0;
注意:最好不要用实现居中后的其他margin属性,可能会导致居中失败(你可以试一试,如果不影响,就没事),如左右居中时,想要调整上下的距离,可以使用top或bottom的定位属性。
描述:点击元素后,页面滚动到顶部,
有可能是滚动元素绑定了某个事件并执行滚动相关的方法,
如:给滚动元素添加一个事件并执行滚动元素.scrollIntoView(true); 为true时,是滚动到顶部。
在元素出发相应的事件就会页面滚动到顶部
居中方式:
这里有种居中方式:绝定定位+margin:0
可以实现上下左右居中。
如果只左右居中,margin-left:0;margin-right:0;
如果只左右居中,margin-top:0;margin-bottom:0;
注意:最好不要用实现居中后的其他margin属性,可能会导致居中失败(你可以试一试,如果不影响,就没事),如左右居中时,想要调整上下的距离,可以使用top或bottom的定位属性。

html制作腾讯地图定位
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0,minimum=1.0,user-scalable=no">
<style type="text/css">
#mapWrap {
margin: 10px 0;
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<p>HTML5定位功能</p>
<p>您的纬度:<span id="latitude"></span></p>
<p>您的精度:<span id="longitude"></span></p>
<p id="errMsg"></p>
<button id="openTXMap">开启腾讯地图</button>
<div id="mapWrap"></div>
<script>
var latitude, longitude
if (navigator.geolocation) {
console.log("支持")
navigator.geolocation.getCurrentPosition(showPosition, showError)
// navigator.geolocation.watchPosition(showPosition, showError)
}
else {
console.log("不支持")
}
function showPosition(position) {
document.querySelector('#latitude').innerText = position.coords.latitude
document.querySelector('#longitude').innerText = position.coords.longitude
// 这里做了经纬度偏差的修正
latitude = position.coords.latitude - 0.002463
longitude = position.coords.longitude + 0.005591
}
function showError(error) {
errMsg = document.querySelector('#errMsg')
switch (error.code) {
case error.PERMISSION_DENIED:
// User denied the request for Geolocation.
errMsg.innerText = "定位失败,用户拒绝请求地理定位"
break;
case error.POSITION_UNAVAILABLE:
// Location information is unavailable.
errMsg.innerText = "定位失败,位置信息是不可用"
break;
case error.TIMEOUT:
// The request to get user location timed out.
errMsg.innerText = "定位失败,请求获取用户位置超时"
break;
case error.UNKNOWN_ERROR:
// An unknown error occurred.
errMsg.innerText = "定位失败,定位系统失效"
break;
}
}
function init() {
var myLatlng = new qq.maps.LatLng(latitude, longitude);
var myOptions = {
zoom: 16,
center: myLatlng
};
var map = new qq.maps.Map(document.getElementById("mapWrap"), myOptions);
}
function loadScript() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://map.qq.com/api/js?v=2.exp&callback=init";
document.body.appendChild(script);
}
document.getElementById("openTXMap").onclick = function () {
loadScript()
init()
}
</script>
</body>
为了这条文章不丢失,请把点在和关注点亮,谢谢大家!
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0,minimum=1.0,user-scalable=no">
<style type="text/css">
#mapWrap {
margin: 10px 0;
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<p>HTML5定位功能</p>
<p>您的纬度:<span id="latitude"></span></p>
<p>您的精度:<span id="longitude"></span></p>
<p id="errMsg"></p>
<button id="openTXMap">开启腾讯地图</button>
<div id="mapWrap"></div>
<script>
var latitude, longitude
if (navigator.geolocation) {
console.log("支持")
navigator.geolocation.getCurrentPosition(showPosition, showError)
// navigator.geolocation.watchPosition(showPosition, showError)
}
else {
console.log("不支持")
}
function showPosition(position) {
document.querySelector('#latitude').innerText = position.coords.latitude
document.querySelector('#longitude').innerText = position.coords.longitude
// 这里做了经纬度偏差的修正
latitude = position.coords.latitude - 0.002463
longitude = position.coords.longitude + 0.005591
}
function showError(error) {
errMsg = document.querySelector('#errMsg')
switch (error.code) {
case error.PERMISSION_DENIED:
// User denied the request for Geolocation.
errMsg.innerText = "定位失败,用户拒绝请求地理定位"
break;
case error.POSITION_UNAVAILABLE:
// Location information is unavailable.
errMsg.innerText = "定位失败,位置信息是不可用"
break;
case error.TIMEOUT:
// The request to get user location timed out.
errMsg.innerText = "定位失败,请求获取用户位置超时"
break;
case error.UNKNOWN_ERROR:
// An unknown error occurred.
errMsg.innerText = "定位失败,定位系统失效"
break;
}
}
function init() {
var myLatlng = new qq.maps.LatLng(latitude, longitude);
var myOptions = {
zoom: 16,
center: myLatlng
};
var map = new qq.maps.Map(document.getElementById("mapWrap"), myOptions);
}
function loadScript() {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://map.qq.com/api/js?v=2.exp&callback=init";
document.body.appendChild(script);
}
document.getElementById("openTXMap").onclick = function () {
loadScript()
init()
}
</script>
</body>
为了这条文章不丢失,请把点在和关注点亮,谢谢大家!
收起阅读 »
帮你跳过Canvas操作大坑
重点:绝对不要重新设定canvas对象的width属性(不是.style.width)!!!!!
操作:
我把之前的JS模块拿到uniapp里用(在renderjs里调用),发现canvas绘制总是有问题,但是问题又不是稳定复现。
操作流程是:打开页面 => 初始化canvas => 渲染canvas => 结果 文字渲染行数超出
在我多次尝试后发现,假如在页面渲染前使用context.fillRect()刷新canvas,渲染结果就是正确的。不过偶尔会失效。
原因:
之前的JS模块已经做了高清化处理,而uniapp也做了高清处理。
高清化处理无非就是修改canvas.width然后缩放content即可。
但是无论你怎么操作canvas.width和canvas.height都是无效的,关了自动高清化也改不了,开了还是改不了。
这里最恶心,由于操作无效,导致我以为没有修改成功,于是保留了旧代码。
后面修改后直接打印,发现值是修改成功的。不过执行了context.fillRect()操作后值又变回去了。
这里我可以确定了,uniapp内部的方法会重置数值。
处理:
那好办,每次uniapp重置,我也重置回来就行。
结果是卡的起飞,渲染还是有问题。
想了又想,抱着试一试的想法把处理canvas.width的地方注释了。
没想到注释了就好了...
这个问题弄了我2星期,官方文档那叫一个简单,啥也没说。
要不是看示例,发现没有对canvas.width的操作感觉可以尝试尝试,估计还能卡一个月。
重点:绝对不要重新设定canvas对象的width属性(不是.style.width)!!!!!
操作:
我把之前的JS模块拿到uniapp里用(在renderjs里调用),发现canvas绘制总是有问题,但是问题又不是稳定复现。
操作流程是:打开页面 => 初始化canvas => 渲染canvas => 结果 文字渲染行数超出
在我多次尝试后发现,假如在页面渲染前使用context.fillRect()刷新canvas,渲染结果就是正确的。不过偶尔会失效。
原因:
之前的JS模块已经做了高清化处理,而uniapp也做了高清处理。
高清化处理无非就是修改canvas.width然后缩放content即可。
但是无论你怎么操作canvas.width和canvas.height都是无效的,关了自动高清化也改不了,开了还是改不了。
这里最恶心,由于操作无效,导致我以为没有修改成功,于是保留了旧代码。
后面修改后直接打印,发现值是修改成功的。不过执行了context.fillRect()操作后值又变回去了。
这里我可以确定了,uniapp内部的方法会重置数值。
处理:
那好办,每次uniapp重置,我也重置回来就行。
结果是卡的起飞,渲染还是有问题。
想了又想,抱着试一试的想法把处理canvas.width的地方注释了。
没想到注释了就好了...
这个问题弄了我2星期,官方文档那叫一个简单,啥也没说。
要不是看示例,发现没有对canvas.width的操作感觉可以尝试尝试,估计还能卡一个月。

关于在 renderjs 父子组件通信的的实现【有着一点自己的经验】
前言
最新在做的一个项目,需要同时实现 H5 和 APP 以及小程序三端,所以自然而然的选择了 uni-app,但是引用的库同时有着需要使用 dom API 和 worker 这两个痛点,寻寻觅觅,我找到了 renderjs
为啥选他
renderjs 首先能够支持使用 dom API,在一些三方库频繁操作 dom,你又不得不依赖这个库的时候,renderjs 会是一个不错的方案。
app 端不支持使用 worker, uni 文档上写着需要使用 renderjs 或者 webview,而 webview 往往会设计 webview 通信的问题,在 APP 端的 webview 通信想要不错的支持只能使用 nvue 来实现,而如果你是想要实现一个组件的话就会是 .nvue 组件,而 uni 官方说了 .nvue 组件只能在 .vue 页面中使用,这就带来了其他页面使用该组件的负担,及app的实现必须使用 .nvue,而同时维护 .vue 和 .nvue 文件是有着一定代价的(关于这里,只是单纯的我还没想好怎么处理这里的关系)。所以,我选择了 renderjs
问题
renderjs 在 h5 的表现和你正常开发一个 vue 组件没有什么区别,唯一要注意的点就是使用了不太建议在 vue2 + typeScript 的项目中去实现它,因为一旦使用 vue.extned() 的这个方法去定义组件就会导致 h5 渲染不正常(也可能是单纯的我是个菜逼)
而 renderjs 在 App端 的差异就比较大了,最重要的一点就是视图层和逻辑层的方法与变量不能互相通信,而另一方面,如果使用了 renderjs 作为子组件,在使用最经典的 props/emit 实现的父子间传值就会有点麻烦,就是本文的重点。
实现
首先,我们这个方案依赖于官方文档给的 echarts 的那个 demo。那个 demo 展示了在 app 端如何让视图层与逻辑层通信(即 renderjs 与原来的 script )。
上面两张图片就是官方文档给的 echarts 的那个 demo 外带一点小注解。
下面开始讲讲它作为子组件如何完成与父组件的双向通信。
父组件向子组件传值
- 首先,在逻辑层(原先的 script )中正常写 props 接收你父组件要传的值
- renderjs 监听该值的变动,随着变动修改自己的属性
<template>
<div>
<div :prop="test" :change:prop="renderModule.onUpdateTestChange" ></div>
</div>
</template>
<script lang="ts">
export default {
props: {
test: {
type: String,
default: "",
},
},
}
</script>
<script module="renderModule" lang="renderjs">
export default {
data() {
return {
renderTest: '',
}
}
methods: {
onUpdateTestChange(newVal, oldVal, ownerInstance, instance) {
this.renderTest = newVal; // 到这里,成功的获得了父组件传来的 test 值并保存在 renderjs 中的 renderTest 中。
}
}
}
</script>
但是,实际上有更快的方法。
在视图层与逻辑层能够访问统一个对象(文档上说的),该对象包含一个对象指向本组件,也就是说,可以直接通过该对象访问组件的所有属性和方法,实际上我只在视图层使用过(毕竟在逻辑层没必要=-=),他就是 this.$ownerInstance
所以在逻辑层想要访问 test 值,可以直接
this.$ownerInstance.$vm.test
再啰嗦一句,这个 $vm 就是组件实例等价于每次正常写的那个 this,所以这样能够访问到就能够理解了。
不过有点奇怪的就是我写的 this.$ownerInstance.$vm.$emit 方法想要尝试触发父组件方法并没有成功,有可能是我写错了,具体原因还不明白,之后有结论了我再更新。
子组件向父组件传值
methods: {
onUpdateTestChange(newVal, oldVal, ownerInstance, instance) {
this.renderTest = newVal; // 到这里,成功的获得了父组件传来的 test 值并保存在 renderjs 中的 renderTest 中。
}
}
这个是刚才的代码,我再借用下。请注意下 ownerInstance 。
我再上面的图片有标注,这个很重要就是因为我们能够看到在点击触发逻辑层方法时,能够通过 ownerInstance.callMethod 触发普通 script 的方法,进而在普通 script 的那个方法下使用 this.$emit 触发父组件方法。但是这样就会有着必须点击才能触发父组件方法的限制,这不能够啊。这个时候可以将 ownerInstance 保存起来,之后想要调用方法的时候就不需要通过点击事件传来的 ownerInstance 才能触发逻辑层的方法了。
methods: {
onUpdateTestChange(newVal, oldVal, ownerInstance, instance) {
this.renderTest = newVal; // 到这里,成功的获得了父组件传来的 test 值并保存在 renderjs 中的 renderTest 中。
if (!this.ownerInstance) this.ownerInstance = ownerInstance;// 这样就能保存了 ownerInstance
},
// 这个就是尝试
niceTry() {
this.ownerInstance?.callMethod('%父组件的方法%',%要传给父组件的参数%);
}
}
另外再提一嘴,就是 this.$ownerInstance.callMethod 不成,小伙伴们可以自己试试,没准也是我搞错了。
至此,父子组件双向通信就算成了。不懂或是发现错误的小伙伴们可以直接评论,只要我还在做现在这个项目应该会满经常上 uni 这边逛逛的。
前言
最新在做的一个项目,需要同时实现 H5 和 APP 以及小程序三端,所以自然而然的选择了 uni-app,但是引用的库同时有着需要使用 dom API 和 worker 这两个痛点,寻寻觅觅,我找到了 renderjs
为啥选他
renderjs 首先能够支持使用 dom API,在一些三方库频繁操作 dom,你又不得不依赖这个库的时候,renderjs 会是一个不错的方案。
app 端不支持使用 worker, uni 文档上写着需要使用 renderjs 或者 webview,而 webview 往往会设计 webview 通信的问题,在 APP 端的 webview 通信想要不错的支持只能使用 nvue 来实现,而如果你是想要实现一个组件的话就会是 .nvue 组件,而 uni 官方说了 .nvue 组件只能在 .vue 页面中使用,这就带来了其他页面使用该组件的负担,及app的实现必须使用 .nvue,而同时维护 .vue 和 .nvue 文件是有着一定代价的(关于这里,只是单纯的我还没想好怎么处理这里的关系)。所以,我选择了 renderjs
问题
renderjs 在 h5 的表现和你正常开发一个 vue 组件没有什么区别,唯一要注意的点就是使用了不太建议在 vue2 + typeScript 的项目中去实现它,因为一旦使用 vue.extned() 的这个方法去定义组件就会导致 h5 渲染不正常(也可能是单纯的我是个菜逼)
而 renderjs 在 App端 的差异就比较大了,最重要的一点就是视图层和逻辑层的方法与变量不能互相通信,而另一方面,如果使用了 renderjs 作为子组件,在使用最经典的 props/emit 实现的父子间传值就会有点麻烦,就是本文的重点。
实现
首先,我们这个方案依赖于官方文档给的 echarts 的那个 demo。那个 demo 展示了在 app 端如何让视图层与逻辑层通信(即 renderjs 与原来的 script )。
上面两张图片就是官方文档给的 echarts 的那个 demo 外带一点小注解。
下面开始讲讲它作为子组件如何完成与父组件的双向通信。
父组件向子组件传值
- 首先,在逻辑层(原先的 script )中正常写 props 接收你父组件要传的值
- renderjs 监听该值的变动,随着变动修改自己的属性
<template>
<div>
<div :prop="test" :change:prop="renderModule.onUpdateTestChange" ></div>
</div>
</template>
<script lang="ts">
export default {
props: {
test: {
type: String,
default: "",
},
},
}
</script>
<script module="renderModule" lang="renderjs">
export default {
data() {
return {
renderTest: '',
}
}
methods: {
onUpdateTestChange(newVal, oldVal, ownerInstance, instance) {
this.renderTest = newVal; // 到这里,成功的获得了父组件传来的 test 值并保存在 renderjs 中的 renderTest 中。
}
}
}
</script>
但是,实际上有更快的方法。
在视图层与逻辑层能够访问统一个对象(文档上说的),该对象包含一个对象指向本组件,也就是说,可以直接通过该对象访问组件的所有属性和方法,实际上我只在视图层使用过(毕竟在逻辑层没必要=-=),他就是 this.$ownerInstance
所以在逻辑层想要访问 test 值,可以直接
this.$ownerInstance.$vm.test
再啰嗦一句,这个 $vm 就是组件实例等价于每次正常写的那个 this,所以这样能够访问到就能够理解了。
不过有点奇怪的就是我写的 this.$ownerInstance.$vm.$emit 方法想要尝试触发父组件方法并没有成功,有可能是我写错了,具体原因还不明白,之后有结论了我再更新。
子组件向父组件传值
methods: {
onUpdateTestChange(newVal, oldVal, ownerInstance, instance) {
this.renderTest = newVal; // 到这里,成功的获得了父组件传来的 test 值并保存在 renderjs 中的 renderTest 中。
}
}
这个是刚才的代码,我再借用下。请注意下 ownerInstance 。
我再上面的图片有标注,这个很重要就是因为我们能够看到在点击触发逻辑层方法时,能够通过 ownerInstance.callMethod 触发普通 script 的方法,进而在普通 script 的那个方法下使用 this.$emit 触发父组件方法。但是这样就会有着必须点击才能触发父组件方法的限制,这不能够啊。这个时候可以将 ownerInstance 保存起来,之后想要调用方法的时候就不需要通过点击事件传来的 ownerInstance 才能触发逻辑层的方法了。
methods: {
onUpdateTestChange(newVal, oldVal, ownerInstance, instance) {
this.renderTest = newVal; // 到这里,成功的获得了父组件传来的 test 值并保存在 renderjs 中的 renderTest 中。
if (!this.ownerInstance) this.ownerInstance = ownerInstance;// 这样就能保存了 ownerInstance
},
// 这个就是尝试
niceTry() {
this.ownerInstance?.callMethod('%父组件的方法%',%要传给父组件的参数%);
}
}
另外再提一嘴,就是 this.$ownerInstance.callMethod 不成,小伙伴们可以自己试试,没准也是我搞错了。
至此,父子组件双向通信就算成了。不懂或是发现错误的小伙伴们可以直接评论,只要我还在做现在这个项目应该会满经常上 uni 这边逛逛的。
收起阅读 »
微信登录时,开发版正常,体验版报"获取第三方账号失败"
uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json
把这个文件里面的配置全部再配置一遍就好了. 重点: 如果是微信小程序,把小程序的配置和web端的配置都配置一遍。
怀疑这个bug可能是识别平台的问题,可能是把微信小程序识别成web造成的。
uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json
把这个文件里面的配置全部再配置一遍就好了. 重点: 如果是微信小程序,把小程序的配置和web端的配置都配置一遍。
怀疑这个bug可能是识别平台的问题,可能是把微信小程序识别成web造成的。

【报Bug】request:fail,系统异常,请联系管理员!
https://unicloud.dcloud.net.cn/pages/login/login 后台报错误。从中午左右到这会了,还没好!!!!
https://unicloud.dcloud.net.cn/pages/login/login 后台报错误。从中午左右到这会了,还没好!!!!

画中画(全局)、支持为第三方播放器无缝添加、进度同步更新(andorid)
画中画(全局)、里面的播放器为官方的video、支持为第三方播放器无缝添加(进度同步更新)、完全关闭app/清后台都不会消失:https://ext.dcloud.net.cn/plugin?id=10185
画中画(全局)、里面的播放器为官方的video、支持为第三方播放器无缝添加(进度同步更新)、完全关闭app/清后台都不会消失:https://ext.dcloud.net.cn/plugin?id=10185
收起阅读 »