HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

【分享】uniCloud云函数结合nodemailer发送邮件的方法

uniCloud

网上找的node发邮件,用到最多的是nodemailer,于是利用百度出来的代码,直接运行在uniCloud,本地运行发送成功,但是云端一直报错。
仔细看了报错内容,提到Thread,所以猜测代码里新建了线程去发邮件。
为了找到如何用当前线程发,第一时间想到的是nodemailer的官方文档:https://nodemailer.com/about/
结合官方文档,写了个可以在uniCloud云端正常发邮件,并且亲测成功的代码:

'use strict';  
const nodemailer = require('nodemailer') // 记得在当前文件夹执行npm install nodemailer后才能使用  
exports.main = async (event, context) => {  
    let transporter = nodemailer.createTransport({  
        host: 'smtp.126.com',  
        secureConnection: true,  
        port: 465,  
        secure: true,  
        auth: {  
            user: 'yourname@126.com',  
            pass: 'SMTP授权码'  
        }  
    })  
    const info = await transporter.sendMail({  
        from: '"邮箱昵称"<yourname@126.com>',  
        to: 'receiver@163.com',  
        subject: '主题',  
        html: '<h1>HTML代码</h1>',  
        text: '文本'  
    })  
    if (info.messageId) {  
        return {code: 0, msg: '发送成功'}  
    } else {  
        return {code: 1, msg: '发送失败', info}  
    }  
}

代码里最关键的是await transporter.sendMail这行,这样写才是在当前线程发

百度上找到的大部分人是这么写的:

transporter.sendMail({  
        from: '"邮箱昵称"<yourname@126.com>',  
        to: 'receiver@163.com',  
        subject: '主题',  
        html: '<h1>HTML代码</h1>',  
        text: '文本'  
    }, (error, info) => {  
    if (error) {  
        return console.log(error);  
    }  
    console.log(info)  
})

这样写的话会新建线程,这在普通的node环境当然没错,但是uniCloud却报了关于线程的错误,猜测是uniCloud不允许多线程

原文出处:https://coding3.com/archives/uniCloud-nodemailer.html

继续阅读 »

网上找的node发邮件,用到最多的是nodemailer,于是利用百度出来的代码,直接运行在uniCloud,本地运行发送成功,但是云端一直报错。
仔细看了报错内容,提到Thread,所以猜测代码里新建了线程去发邮件。
为了找到如何用当前线程发,第一时间想到的是nodemailer的官方文档:https://nodemailer.com/about/
结合官方文档,写了个可以在uniCloud云端正常发邮件,并且亲测成功的代码:

'use strict';  
const nodemailer = require('nodemailer') // 记得在当前文件夹执行npm install nodemailer后才能使用  
exports.main = async (event, context) => {  
    let transporter = nodemailer.createTransport({  
        host: 'smtp.126.com',  
        secureConnection: true,  
        port: 465,  
        secure: true,  
        auth: {  
            user: 'yourname@126.com',  
            pass: 'SMTP授权码'  
        }  
    })  
    const info = await transporter.sendMail({  
        from: '"邮箱昵称"<yourname@126.com>',  
        to: 'receiver@163.com',  
        subject: '主题',  
        html: '<h1>HTML代码</h1>',  
        text: '文本'  
    })  
    if (info.messageId) {  
        return {code: 0, msg: '发送成功'}  
    } else {  
        return {code: 1, msg: '发送失败', info}  
    }  
}

代码里最关键的是await transporter.sendMail这行,这样写才是在当前线程发

百度上找到的大部分人是这么写的:

transporter.sendMail({  
        from: '"邮箱昵称"<yourname@126.com>',  
        to: 'receiver@163.com',  
        subject: '主题',  
        html: '<h1>HTML代码</h1>',  
        text: '文本'  
    }, (error, info) => {  
    if (error) {  
        return console.log(error);  
    }  
    console.log(info)  
})

这样写的话会新建线程,这在普通的node环境当然没错,但是uniCloud却报了关于线程的错误,猜测是uniCloud不允许多线程

原文出处:https://coding3.com/archives/uniCloud-nodemailer.html

收起阅读 »

红包雨--简单实现,没有过度的修饰

<template>  
    <view class="home">  
        <view class="content">  
                <view @animationend="runend(index)" @animationstart="runstart(index)" v-for="(item,index) in packStyle" :key="index" class="envelope" :style="item">  

                </view>  

        </view>  
    </view>  
</template>  

<script>  

export default {  
    data() {  
        return {  
            packStyle: []  
        };  
    },  
    onLoad() {  
        // 随即生成红包  
        this.createRedPack();  
    },  
    mounted() {  

    },  
    onReady() {  

    },  
    methods: {  
        runstart(key){  
            if(key===0){  
                console.log('监听动画开始');  
            }     
        },  
        runend(key){  
            if(key===this.packStyle.length-1){  
                console.log('监听动画结束---下一波红包开始');  
                this.createRedPack()  
            }  
        },  
        createRedPack(){  
            // 随机生成30个红包  
            var initNumber = 0;  
            for (var i = 0; i < 60; i++) {  
               let lefts = (Math.floor(Math.random()*5+5)); // 随机边距  
               let delay = Math.floor(Math.random()*5+2); // 延迟时间  
               initNumber+=lefts; // 确保唯一,不让红包出现重叠现象  
               this.packStyle.push({  
                   'left': initNumber+'%',  
                   'top': lefts+'px',  
                   'animation-delay': delay/2+'s'  
               })  
            }     
        }  
    }  
};  
</script>  

<style>  
.home {  
    width: 100%;  
    height: 100%;  
}  
.content {  
  position: relative;  
  height: 100%;  
  background: #fff;  
  overflow: hidden;  
}  
.envelope {  
    position: fixed;  
    opacity: 0;  
    animation: drops 1.2s cubic-bezier(.22,.22,.39,.26) 1;  
    width: 60px;  
    height: 60px;  
    background: url(../../static/hongbaotu.png) no-repeat;  
    /* background-color: #007AFF; */  
    background-size: 60px 60px;  
}  
@keyframes drops {  
  0% {  
    opacity: 0;  
  }  
  20% {  
    opacity: 1;  
  }  
  90% {  
    opacity: 1;  
  }  
  100% {  
    opacity: 0;  
    transform: translate3d(10px, 100vh, -10px);  
  }  
}  
</style>  
继续阅读 »
<template>  
    <view class="home">  
        <view class="content">  
                <view @animationend="runend(index)" @animationstart="runstart(index)" v-for="(item,index) in packStyle" :key="index" class="envelope" :style="item">  

                </view>  

        </view>  
    </view>  
</template>  

<script>  

export default {  
    data() {  
        return {  
            packStyle: []  
        };  
    },  
    onLoad() {  
        // 随即生成红包  
        this.createRedPack();  
    },  
    mounted() {  

    },  
    onReady() {  

    },  
    methods: {  
        runstart(key){  
            if(key===0){  
                console.log('监听动画开始');  
            }     
        },  
        runend(key){  
            if(key===this.packStyle.length-1){  
                console.log('监听动画结束---下一波红包开始');  
                this.createRedPack()  
            }  
        },  
        createRedPack(){  
            // 随机生成30个红包  
            var initNumber = 0;  
            for (var i = 0; i < 60; i++) {  
               let lefts = (Math.floor(Math.random()*5+5)); // 随机边距  
               let delay = Math.floor(Math.random()*5+2); // 延迟时间  
               initNumber+=lefts; // 确保唯一,不让红包出现重叠现象  
               this.packStyle.push({  
                   'left': initNumber+'%',  
                   'top': lefts+'px',  
                   'animation-delay': delay/2+'s'  
               })  
            }     
        }  
    }  
};  
</script>  

<style>  
.home {  
    width: 100%;  
    height: 100%;  
}  
.content {  
  position: relative;  
  height: 100%;  
  background: #fff;  
  overflow: hidden;  
}  
.envelope {  
    position: fixed;  
    opacity: 0;  
    animation: drops 1.2s cubic-bezier(.22,.22,.39,.26) 1;  
    width: 60px;  
    height: 60px;  
    background: url(../../static/hongbaotu.png) no-repeat;  
    /* background-color: #007AFF; */  
    background-size: 60px 60px;  
}  
@keyframes drops {  
  0% {  
    opacity: 0;  
  }  
  20% {  
    opacity: 1;  
  }  
  90% {  
    opacity: 1;  
  }  
  100% {  
    opacity: 0;  
    transform: translate3d(10px, 100vh, -10px);  
  }  
}  
</style>  
收起阅读 »

安卓离线打包小米推送Register mipush appId or appKey is null or empty的解决方法

安卓离线打包,小米离线推送,打开页面,提示Register mipush appId or appKey is null or empty

问了官方人员,见这个帖子。https://ask.dcloud.net.cn/question/118126

我们分3步来分析可能会出什么问题。  
第一步、引入lib:aps-release.aar和aps-unipush-release.aar  
第二步、build配置key  
第三步、工程的xml引入这个key

第一步,类库没有引入。
因为我的华为测试还好的,可以离线推送,所以我首先排除掉这个问题,我尝试把这两个类库去掉,安装好app,会提示push库不存在。
所以,排除掉这个问题。

第二步、build配置key


这步,似乎也没啥好看的,按照官方的离线打包文档,就是这么写的。

第三步、工程的xml引入这个key


后来读了push的安卓源代码。

发现是这步的问题,meta-data的位置放错了,要放到application后面,但奇怪的是华为的也在前面,不知道为什么能识别。

如果大家觉得受益匪浅的话,记得打赏一下哈。有问题加我QQ:13040

微信

继续阅读 »

安卓离线打包,小米离线推送,打开页面,提示Register mipush appId or appKey is null or empty

问了官方人员,见这个帖子。https://ask.dcloud.net.cn/question/118126

我们分3步来分析可能会出什么问题。  
第一步、引入lib:aps-release.aar和aps-unipush-release.aar  
第二步、build配置key  
第三步、工程的xml引入这个key

第一步,类库没有引入。
因为我的华为测试还好的,可以离线推送,所以我首先排除掉这个问题,我尝试把这两个类库去掉,安装好app,会提示push库不存在。
所以,排除掉这个问题。

第二步、build配置key


这步,似乎也没啥好看的,按照官方的离线打包文档,就是这么写的。

第三步、工程的xml引入这个key


后来读了push的安卓源代码。

发现是这步的问题,meta-data的位置放错了,要放到application后面,但奇怪的是华为的也在前面,不知道为什么能识别。

如果大家觉得受益匪浅的话,记得打赏一下哈。有问题加我QQ:13040

微信

收起阅读 »

个推解读Android 12首个开发者预览版

Android

引言

近日,Google发布了新系统 Android 12 的首个开发者预览版。根据谷歌官方消息,最终版本的Android 12预计于今年下半年正式上线。此次发布预览版的目的主要是帮助开发者提前了解Android 12的新变化,为后续进行应用适配提前做准备。

作为陪伴开发者多年的重要伙伴,个推一直密切关注和跟进行业发展趋势。在Android12首个开发者预览版发布后,个推快速对新系统的有关更新展开了调研。本文对Android 12预览版的部分新功能、新特性进行了解读,希望能帮助广大开发者对新系统有个快速了解。

Android 12行为变更:面向所有应用

用户体验升级

沉浸式手势导航改进

从Android 10 开始,Android 系统就已支持手势导航,致力于给用户带来沉浸式的全新体验。在之前版本的系统下,用户启动安卓的沉浸模式,默认需要的手势操作是:从屏幕侧方滑入,退出沉浸模式,然后再返回上一个界面。最新发布的Android 12首个开发者预览版则对手势导航模式进行了简化:用户不再需要滑动两次,只需一次滑动手势,即可退出全屏沉浸模式并返回上个界面,更加高效和便利。

隐私/安全保护增强

MAC 地址权限限制

为进一步保护用户隐私和数据安全,Android 11引入了“单次授权”“权限自动重置”“分区存储”等功能。Android 12则更进一步限制了所有非系统应用程序对设备MAC地址的访问,无论目标API级别如何。

相关API返回空值或占位符值,具体取决于应用程序的目标SDK版本:
➀ target = 12 ,返回 null
➁ target <12 ,返回 02:00:00:00:00:00

未被信任的触摸事件将被阻止

为了保护系统安全和更好的用户体验,Android 12将会阻止某些窗口的触摸。
例如:
➀ 申请了 SYSTEM_ALERT_WINDOW 权限的 windows
➁ Toast messages

应用不能关闭系统对话框

ACTION_CLOSE_SYSTEM_DIALOGS intent 在 Android 12 被废弃。
➀ target = 12, 将抛出 SecurityException 异常
➁ target < 12 ,该 intent 不会执行,会在 logcat 打印警告日志:
E ActivityTaskManager Permission Denial:
android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from
com.package.name requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS,
dropping broadcast.

Android 12行为变更:针对Target = 12的应用

自定义通知栏

之前,开发者能自定义整个通知栏区域的布局和样式,这就导致了不同设备的兼容适配问题以及用户的浏览不适应问题。

Android 12更改了完全自定义通知的外观。对于 target = 12 的应用,在通知栏的消息展示均使用统一的模板。上面应用名字显示和折叠按钮都是相同的、固定的,下面折叠和展开状态呈现的区域是可自定义的:

折叠和展开的样式:

折叠状态

展开状态

若APP中存在自定义Notification.Style,亦或是使用了Notification.Builder中 setCustomContentView(RemoteViews), setCustomBigContentView(RemoteViews)和setCustomHeadsUpContentView(RemoteViews)方法,可能会受此影响。

隐私/安全

WebView 中的SameSite cookie行为

Android的WebView组件基于Chromium来提高安全性和隐私性,去年,Chromium对第三方Cookie的处理方式进行了更改,并已面向众多Chrome用户推出。从Android 12开始,这些更改将应用于WebView。

SameSitecookie的属性控制它是否可以与任何请求一起发送,还是只能与相同站点的请求一起发送。Android 12中的WebView基本版本(版本89.0.4385.0)改进了第三方Cookie的默认处理,将有助于防止意外的跨站点共享。

ADB backup 限制

Android 12 限制了 adb backup 命令行的默认行为 (该命令行是用来备份恢复数据的),对应用程序数据adb backup有依赖的开发者可以在清单文件中设置 android:debuggable 为 true。

组件需要添加 exported 配置

target=12时,使用的activity 、service或者广播有用到 intent filters ,则需声明 android:exported 属性。不配置的话,在安卓 12 设备上将不能安装,logcat 也会打印错误日志:
Targeting S+ (version 10000 and above) requires that an explicit value for
android:exported be defined when intent filters are present

Pending intents 必须声明意图

使用 PendingIntent 需要声明 PendingIntent.FLAG_MUTABLE 或者 PendingIntent.FLAG_IMMUTABLE flag,否则系统会抛出异常 IllegalArgumentException。

性能

前台服务启动限制

以 Android 12 为目标的应用程序,无法在后台运行时启动前台服务,应用程序在后台运行时,可考虑使用 WorkManager 执行任务。

ForegroundService通知延迟

前台服务启动后必须调用startForeground() 来显示前台通知,如果应用在5s内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR。
在Android 12中,限制时间由5s改为了10s。这样一来,对于部分APP来讲,将会有更充分的处理时间。

通知跳转

services 或者 broadcast receivers 中创建的通知将不能调用 startActivity() !!!
logcat 会打印:
Indirect notification activity start (trampoline) from PACKAGE_NAME, \this should be avoided for performance reasons.

总结

以上,是个推对Android 12首个开发者预览版本中几个重要更新点的解读。

除了以上内容外,Android 12 预览版还在视频、音频和图片处理方面做了很多有趣的更新。比如,通过手机的振动马达增加对触觉耦合音频效果的支持,可以帮助游戏类APP提升玩家体验;引入了兼容媒体转码功能,可以让那些不支持 HEVC的应用,也能将文件高效转码为 AVC 格式;同时还引入了对 AV1 图像文件格式(AVIF)的支持,使得开发者可以同样的文件大小,收获比 JPEG 图像更高的图像质量……感兴趣的开发者可以进入Android 12官网进一步详细了解。

https://developer.android.google.cn/about/versions/12

后续,个推还将在持续打磨开发者服务和SDK产品的同时,密切跟进移动开发领域的相关动态,为开发者升级产品功能、迭代服务体验提供有效建议。

也欢迎更多的开发者和我们一起交流和探索Android及移动开发新技术,共同建设更好的安卓开发生态。

*本文图片来源于Android官网

继续阅读 »

引言

近日,Google发布了新系统 Android 12 的首个开发者预览版。根据谷歌官方消息,最终版本的Android 12预计于今年下半年正式上线。此次发布预览版的目的主要是帮助开发者提前了解Android 12的新变化,为后续进行应用适配提前做准备。

作为陪伴开发者多年的重要伙伴,个推一直密切关注和跟进行业发展趋势。在Android12首个开发者预览版发布后,个推快速对新系统的有关更新展开了调研。本文对Android 12预览版的部分新功能、新特性进行了解读,希望能帮助广大开发者对新系统有个快速了解。

Android 12行为变更:面向所有应用

用户体验升级

沉浸式手势导航改进

从Android 10 开始,Android 系统就已支持手势导航,致力于给用户带来沉浸式的全新体验。在之前版本的系统下,用户启动安卓的沉浸模式,默认需要的手势操作是:从屏幕侧方滑入,退出沉浸模式,然后再返回上一个界面。最新发布的Android 12首个开发者预览版则对手势导航模式进行了简化:用户不再需要滑动两次,只需一次滑动手势,即可退出全屏沉浸模式并返回上个界面,更加高效和便利。

隐私/安全保护增强

MAC 地址权限限制

为进一步保护用户隐私和数据安全,Android 11引入了“单次授权”“权限自动重置”“分区存储”等功能。Android 12则更进一步限制了所有非系统应用程序对设备MAC地址的访问,无论目标API级别如何。

相关API返回空值或占位符值,具体取决于应用程序的目标SDK版本:
➀ target = 12 ,返回 null
➁ target <12 ,返回 02:00:00:00:00:00

未被信任的触摸事件将被阻止

为了保护系统安全和更好的用户体验,Android 12将会阻止某些窗口的触摸。
例如:
➀ 申请了 SYSTEM_ALERT_WINDOW 权限的 windows
➁ Toast messages

应用不能关闭系统对话框

ACTION_CLOSE_SYSTEM_DIALOGS intent 在 Android 12 被废弃。
➀ target = 12, 将抛出 SecurityException 异常
➁ target < 12 ,该 intent 不会执行,会在 logcat 打印警告日志:
E ActivityTaskManager Permission Denial:
android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from
com.package.name requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS,
dropping broadcast.

Android 12行为变更:针对Target = 12的应用

自定义通知栏

之前,开发者能自定义整个通知栏区域的布局和样式,这就导致了不同设备的兼容适配问题以及用户的浏览不适应问题。

Android 12更改了完全自定义通知的外观。对于 target = 12 的应用,在通知栏的消息展示均使用统一的模板。上面应用名字显示和折叠按钮都是相同的、固定的,下面折叠和展开状态呈现的区域是可自定义的:

折叠和展开的样式:

折叠状态

展开状态

若APP中存在自定义Notification.Style,亦或是使用了Notification.Builder中 setCustomContentView(RemoteViews), setCustomBigContentView(RemoteViews)和setCustomHeadsUpContentView(RemoteViews)方法,可能会受此影响。

隐私/安全

WebView 中的SameSite cookie行为

Android的WebView组件基于Chromium来提高安全性和隐私性,去年,Chromium对第三方Cookie的处理方式进行了更改,并已面向众多Chrome用户推出。从Android 12开始,这些更改将应用于WebView。

SameSitecookie的属性控制它是否可以与任何请求一起发送,还是只能与相同站点的请求一起发送。Android 12中的WebView基本版本(版本89.0.4385.0)改进了第三方Cookie的默认处理,将有助于防止意外的跨站点共享。

ADB backup 限制

Android 12 限制了 adb backup 命令行的默认行为 (该命令行是用来备份恢复数据的),对应用程序数据adb backup有依赖的开发者可以在清单文件中设置 android:debuggable 为 true。

组件需要添加 exported 配置

target=12时,使用的activity 、service或者广播有用到 intent filters ,则需声明 android:exported 属性。不配置的话,在安卓 12 设备上将不能安装,logcat 也会打印错误日志:
Targeting S+ (version 10000 and above) requires that an explicit value for
android:exported be defined when intent filters are present

Pending intents 必须声明意图

使用 PendingIntent 需要声明 PendingIntent.FLAG_MUTABLE 或者 PendingIntent.FLAG_IMMUTABLE flag,否则系统会抛出异常 IllegalArgumentException。

性能

前台服务启动限制

以 Android 12 为目标的应用程序,无法在后台运行时启动前台服务,应用程序在后台运行时,可考虑使用 WorkManager 执行任务。

ForegroundService通知延迟

前台服务启动后必须调用startForeground() 来显示前台通知,如果应用在5s内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR。
在Android 12中,限制时间由5s改为了10s。这样一来,对于部分APP来讲,将会有更充分的处理时间。

通知跳转

services 或者 broadcast receivers 中创建的通知将不能调用 startActivity() !!!
logcat 会打印:
Indirect notification activity start (trampoline) from PACKAGE_NAME, \this should be avoided for performance reasons.

总结

以上,是个推对Android 12首个开发者预览版本中几个重要更新点的解读。

除了以上内容外,Android 12 预览版还在视频、音频和图片处理方面做了很多有趣的更新。比如,通过手机的振动马达增加对触觉耦合音频效果的支持,可以帮助游戏类APP提升玩家体验;引入了兼容媒体转码功能,可以让那些不支持 HEVC的应用,也能将文件高效转码为 AVC 格式;同时还引入了对 AV1 图像文件格式(AVIF)的支持,使得开发者可以同样的文件大小,收获比 JPEG 图像更高的图像质量……感兴趣的开发者可以进入Android 12官网进一步详细了解。

https://developer.android.google.cn/about/versions/12

后续,个推还将在持续打磨开发者服务和SDK产品的同时,密切跟进移动开发领域的相关动态,为开发者升级产品功能、迭代服务体验提供有效建议。

也欢迎更多的开发者和我们一起交流和探索Android及移动开发新技术,共同建设更好的安卓开发生态。

*本文图片来源于Android官网

收起阅读 »

uniapp升级v3之后,手机上的复选框样式没有了,h5还是正常

v3

uniapp升级v3之后,手机上的复选框样式没有了,h5还是正常

因为升级v3 之前,web上是通过 .uni-checkbox-input 来修改的复选框样式,app是通过 .wx-checkbox-input 来修改的复选框样式

升级之后.wx-checkbox-input就失效了,统一使用.uni-checkbox-input了

继续阅读 »

uniapp升级v3之后,手机上的复选框样式没有了,h5还是正常

因为升级v3 之前,web上是通过 .uni-checkbox-input 来修改的复选框样式,app是通过 .wx-checkbox-input 来修改的复选框样式

升级之后.wx-checkbox-input就失效了,统一使用.uni-checkbox-input了

收起阅读 »

mui ajax ios wkwebview请求的问题

Vue iOS

之前项目jquery+mui写的,脏乱差,现在进行改版,采用vue+mui,去掉沉重的html拼接,开始上手顺的一塌糊涂,后面打包发布真机,android的一切照旧,ios的ajax访问不到数据.心凉了一截,后面是所有代码放在mui.plusready里面,但是html里面却又不能实现vue的渲染,愁啊;调试都不方便;

后面又是百度又是啥的终于琢磨出一个办法,也不知道对大家有没有用,我这里在下面贴出来;
如下代码:

                   //业务页面js  
                        mui.init();  
            mui.plusReady(function() {  
                my_packer_info.initPackageList(); //要写,否者会出现第一次打开页面没数据,因为当时plus还没被初始化,这里只要调一下vue里面的初始化数据的方法即可  
            });  

                        var my_packer_info = new Vue({  
                              el: "#my_packer_info",  
                              data:{},  
                              methods: {  
                                     //初始化包裹列表  
                    initPackageList: function() {  
                        //;  
                        var req_data = {  
                            PlatForm: this.PlatForm,  
                            Shop: this.ClientInfo.ClientInfo.Shop,  
                            Page: 1,  
                            PageCount: 100,  
                        }  
                        let that = this;  
                                                //自己在mui.ajax外面封装了一层的方法,同时也要改一下mui.js里面的ajax模块的xhr的创建  
                        AxiosPost('GetBillCode', req_data, (res) => {   
                            if (res.State) {  
                                ....  
                            } else {  
                                mui.toast("系統繁忙,請稍後重試:" + res.MsgText);  
                            }  
                        }, (err) => {  
                            mui.toast("系統繁忙,請稍後重試:" + err);  
                        });  
                    }  
                              },  
                             created: function() {  
                    this.initPackageList();  
                 }  
                        });
           //mui.js里面 $.ajax模块的修改,不是很完善,大家可以自行加点判断,我这里是从ajax下面有WKWebView提醒哪里直接拷贝过来用的  
           xhr: function(protocol) {  
        if(location.protocol === 'file:' && $.os.ios && window.webkit && window.webkit.messageHandlers && !(xhr instanceof plus.net.XMLHttpRequest)){  
                return plus.net.XMLHttpRequest;  
        }  
        else{  
            return new window.XMLHttpRequest();  
        }  
    }  
继续阅读 »

之前项目jquery+mui写的,脏乱差,现在进行改版,采用vue+mui,去掉沉重的html拼接,开始上手顺的一塌糊涂,后面打包发布真机,android的一切照旧,ios的ajax访问不到数据.心凉了一截,后面是所有代码放在mui.plusready里面,但是html里面却又不能实现vue的渲染,愁啊;调试都不方便;

后面又是百度又是啥的终于琢磨出一个办法,也不知道对大家有没有用,我这里在下面贴出来;
如下代码:

                   //业务页面js  
                        mui.init();  
            mui.plusReady(function() {  
                my_packer_info.initPackageList(); //要写,否者会出现第一次打开页面没数据,因为当时plus还没被初始化,这里只要调一下vue里面的初始化数据的方法即可  
            });  

                        var my_packer_info = new Vue({  
                              el: "#my_packer_info",  
                              data:{},  
                              methods: {  
                                     //初始化包裹列表  
                    initPackageList: function() {  
                        //;  
                        var req_data = {  
                            PlatForm: this.PlatForm,  
                            Shop: this.ClientInfo.ClientInfo.Shop,  
                            Page: 1,  
                            PageCount: 100,  
                        }  
                        let that = this;  
                                                //自己在mui.ajax外面封装了一层的方法,同时也要改一下mui.js里面的ajax模块的xhr的创建  
                        AxiosPost('GetBillCode', req_data, (res) => {   
                            if (res.State) {  
                                ....  
                            } else {  
                                mui.toast("系統繁忙,請稍後重試:" + res.MsgText);  
                            }  
                        }, (err) => {  
                            mui.toast("系統繁忙,請稍後重試:" + err);  
                        });  
                    }  
                              },  
                             created: function() {  
                    this.initPackageList();  
                 }  
                        });
           //mui.js里面 $.ajax模块的修改,不是很完善,大家可以自行加点判断,我这里是从ajax下面有WKWebView提醒哪里直接拷贝过来用的  
           xhr: function(protocol) {  
        if(location.protocol === 'file:' && $.os.ios && window.webkit && window.webkit.messageHandlers && !(xhr instanceof plus.net.XMLHttpRequest)){  
                return plus.net.XMLHttpRequest;  
        }  
        else{  
            return new window.XMLHttpRequest();  
        }  
    }  
收起阅读 »

关于前端弹窗无法覆盖原生导航栏及tabbar的一个解决方案

经验分享

直接简单的先说一下我的需求, 首页加载的时候有弹窗,可能会多个,但是需求不能一次性全部弹出需要队列弹出,就是关闭一个之后再弹出下一个。这边使用到了vuex来存储全局弹窗队列 最下方附带有效果视频

这边是再App.vue中监听一下vuex > state中的弹窗队列

watch: {  
    ALERT_LIST(newVal, oldVal) {  
        if (newVal.length >= 1 && oldVal.length == 0) { //该条件存在则证明是第一个弹窗,需要先跳转页面  
            this.myRouter({     //这边是跳转一个新页面  
                type: "navigateTo",  
                url: "/pages/public/alert/alert",  
                animationType:"fade-in",  
                animationDuration:300  
            })  

        }  
    }  
}  

///pages/public/alert/alert跳转的新页面中的page.json一定要这样设置,这样这个页面就把下面的页面给覆盖住包括tabbar与到导航栏,并且这个页面是透明的你可以再页面的style中自己给背景透明色  
"style": {  
                "navigationStyle": "custom",  
                "app-plus": {  
                    "animationType": "fade-in",  
                    "animationDuration": "200",  
                    "bounce": "none",  
                    "backgroundColor": "rgba(0,0,0,0)" // 背景透明  
                }  
            }  

在这个新页面中监听(也是这个队列)  
watch: {  
            ALERT_LIST(newVal, oldVal) {  
                if (newVal.length == 0) { //如果是最后一个弹窗的情况下就返回也就是关闭这个弹窗容器  
                                        uni.navigateBack({})  
                }   
            }  
        },  

这个就是你要显示的弹窗因为考虑到后期弹窗多的话我就做成了组件形式
说明:ALERT_LIST[0]就是拿这个队列中的第一项 name:就是你在往队列中添加新弹窗的时候定义的要与下面代码中的v-if后面对应的值一致这样在队列往前走的时候他就能显示对应的弹窗

<!-- 隐私的弹出 -->  
<protocol-alert @closeAlert="_closeAlert" v-if="ALERT_LIST[0].name == 'protoco'"></protocol-alert>   
<!-- //版本更新的弹出 -->  
<versionup-date @closeAlert="_closeAlert" v-if="ALERT_LIST[0].name == 'version'" :versionInfo="ALERT_LIST[0].data"></versionup-date>     
<!-- //淘宝客猜你想找的弹出 -->  
<copy-search @closeAlert="_closeAlert" v-if="ALERT_LIST[0].name == 'copysearch'"></copy-search>

注:在弹窗中如果有按钮出发了关闭弹窗要将队列中的这一项删除掉 (代码只是提供了一个大概的思路具体写法看你自己)

继续阅读 »

直接简单的先说一下我的需求, 首页加载的时候有弹窗,可能会多个,但是需求不能一次性全部弹出需要队列弹出,就是关闭一个之后再弹出下一个。这边使用到了vuex来存储全局弹窗队列 最下方附带有效果视频

这边是再App.vue中监听一下vuex > state中的弹窗队列

watch: {  
    ALERT_LIST(newVal, oldVal) {  
        if (newVal.length >= 1 && oldVal.length == 0) { //该条件存在则证明是第一个弹窗,需要先跳转页面  
            this.myRouter({     //这边是跳转一个新页面  
                type: "navigateTo",  
                url: "/pages/public/alert/alert",  
                animationType:"fade-in",  
                animationDuration:300  
            })  

        }  
    }  
}  

///pages/public/alert/alert跳转的新页面中的page.json一定要这样设置,这样这个页面就把下面的页面给覆盖住包括tabbar与到导航栏,并且这个页面是透明的你可以再页面的style中自己给背景透明色  
"style": {  
                "navigationStyle": "custom",  
                "app-plus": {  
                    "animationType": "fade-in",  
                    "animationDuration": "200",  
                    "bounce": "none",  
                    "backgroundColor": "rgba(0,0,0,0)" // 背景透明  
                }  
            }  

在这个新页面中监听(也是这个队列)  
watch: {  
            ALERT_LIST(newVal, oldVal) {  
                if (newVal.length == 0) { //如果是最后一个弹窗的情况下就返回也就是关闭这个弹窗容器  
                                        uni.navigateBack({})  
                }   
            }  
        },  

这个就是你要显示的弹窗因为考虑到后期弹窗多的话我就做成了组件形式
说明:ALERT_LIST[0]就是拿这个队列中的第一项 name:就是你在往队列中添加新弹窗的时候定义的要与下面代码中的v-if后面对应的值一致这样在队列往前走的时候他就能显示对应的弹窗

<!-- 隐私的弹出 -->  
<protocol-alert @closeAlert="_closeAlert" v-if="ALERT_LIST[0].name == 'protoco'"></protocol-alert>   
<!-- //版本更新的弹出 -->  
<versionup-date @closeAlert="_closeAlert" v-if="ALERT_LIST[0].name == 'version'" :versionInfo="ALERT_LIST[0].data"></versionup-date>     
<!-- //淘宝客猜你想找的弹出 -->  
<copy-search @closeAlert="_closeAlert" v-if="ALERT_LIST[0].name == 'copysearch'"></copy-search>

注:在弹窗中如果有按钮出发了关闭弹窗要将队列中的这一项删除掉 (代码只是提供了一个大概的思路具体写法看你自己)

收起阅读 »

UniPush从0到1

个推 uni_app unipush

掘金地址

UniPush是DCloud联合个推公司推出的集成型统一推送服务,内建了苹果、华为、小米、OPPO、VIVO、魅族、谷歌FCM等手机厂商的系统级推送和个推等第三方推送。

推送的准备

  • 1.CID 设备标识
// 最简单的推送把cid给到后端,后端通过cid来推送  

  const { clientid = '' } = plus.push.getClientInfo() || {} // 获取cid
  • 2.alias 别名 给cid绑定一个别名(在个推服务器上)
    
    // 别名推送后端不需要保存cid 前端打上别名后 后端通过别名进行推送 比如 用户ID 推送的时候只需要给相应的ID使用别名推送  

ios
const GtSdk:any = plus.ios.importClass('GeTuiSdk')
// 为该设备绑定别名
GtSdk.bindAliasandSequenceNum(alias, alias)

Android
const PushManager:any = plus.android.importClass('com.igexin.sdk.PushManager')
const context:any = plus.android.runtimeMainActivity().getContext()
const Instance:any = PushManager.getInstance()
// 为该设备绑定别名
Instance.bindAlias(context, alias)


- 3.tag 把用户(cid)进行分组 (也是在个推服务器上)  

// 后端通过标签进行推送 比如我们需要推送所有男性用户 首先要对用户打上标签(男) 后端只需要给个推服务器发送推送指令男 有效减少服务器压力

// uniPush 暂不支持前端打标签 (可由后端来打标签)


# 推送的接受   

一般是可以写到App.vue文件里面  

//消息的监听
plus.push.addEventListener(
'receive',
function (msg: any = {}) {
const isAndroid = plus.os.name === 'Android'
console.log(msg, !isAndroid)
if (!isAndroid) {
// 如果是IOS
const payload = msg.payload
// 【APP离线】收到消息,但没有提醒(发生在一次收到多个离线消息时,只有一个有提醒,但其他的没有提醒)
// 【APP在线】收到消息,不会触发系统消息,需要创建本地消息,但不能重复创建。必须加msg.type验证去除死循环
if (msg.aps == null && msg.type === 'receive') {
const messageTitle = payload.messageTitle || ''
const messageContent = payload.messageContent || ''
// 创建本地消息,发送的本地消息也会被receive方法接收到,但没有type属性,且aps是null
plus.push.createMessage(messageContent, JSON.stringify(payload), {
title: messageTitle
})
}
} else {
let payload:any = {}
console.log(typeof msg.payload === 'object', typeof msg.payload)
if (typeof msg.payload === 'object') {
payload = msg.payload || {}
} else {
payload = JSON.parse(msg.payload) || {}
}
console.log(payload)
const messageTitle = payload.messageTitle || ''
const messageContent = payload.messageContent || ''

      plus.push.createMessage(messageContent, JSON.stringify(payload), {  
        title: messageTitle  
      })  
    }  
    console.log(msg)  
  },  
  false  
)  

// 消息的点击事件  
plus.push.addEventListener(  
  'click',  
  function (msg: any = {}) {  
    const isAndroid = plus.os.name === 'Android'  
    if (!isAndroid) {  
      // 如果是IOS  
      let payload  
      if (msg.type === 'click') {  
        // APP离线点击包含click属性,这时payload是JSON对象  
        payload = msg.payload  
      } else {  
        // APP在线,收到消息不会包含type属性,这时的payload是JSON字符串,需要转为JSON对象  
        if (typeof msg.payload === 'object') {  
          payload = msg.payload || {}  
        } else {  
          payload = JSON.parse(msg.payload) || {}  
        }  
      }  
         // 点击后业务逻辑的处理  
      this.messageClick(payload)  
    } else {  
      // 如果是Android,收到playload均是是JSON字符串,需要转为JSON对象  
      let payload:any = {}  
      if (typeof msg.payload === 'object') {  
        payload = msg.payload || {}  
      } else {  
        payload = JSON.parse(msg.payload) || {}  
      }  
      // 点击后业务逻辑的处理  
      this.messageClick(payload)  
    }  
  },  
  false  
)


如果想离线推送需APP上架市场 申请厂商推送 把相应参数填写到Dcloud后台  
继续阅读 »

掘金地址

UniPush是DCloud联合个推公司推出的集成型统一推送服务,内建了苹果、华为、小米、OPPO、VIVO、魅族、谷歌FCM等手机厂商的系统级推送和个推等第三方推送。

推送的准备

  • 1.CID 设备标识
// 最简单的推送把cid给到后端,后端通过cid来推送  

  const { clientid = '' } = plus.push.getClientInfo() || {} // 获取cid
  • 2.alias 别名 给cid绑定一个别名(在个推服务器上)
    
    // 别名推送后端不需要保存cid 前端打上别名后 后端通过别名进行推送 比如 用户ID 推送的时候只需要给相应的ID使用别名推送  

ios
const GtSdk:any = plus.ios.importClass('GeTuiSdk')
// 为该设备绑定别名
GtSdk.bindAliasandSequenceNum(alias, alias)

Android
const PushManager:any = plus.android.importClass('com.igexin.sdk.PushManager')
const context:any = plus.android.runtimeMainActivity().getContext()
const Instance:any = PushManager.getInstance()
// 为该设备绑定别名
Instance.bindAlias(context, alias)


- 3.tag 把用户(cid)进行分组 (也是在个推服务器上)  

// 后端通过标签进行推送 比如我们需要推送所有男性用户 首先要对用户打上标签(男) 后端只需要给个推服务器发送推送指令男 有效减少服务器压力

// uniPush 暂不支持前端打标签 (可由后端来打标签)


# 推送的接受   

一般是可以写到App.vue文件里面  

//消息的监听
plus.push.addEventListener(
'receive',
function (msg: any = {}) {
const isAndroid = plus.os.name === 'Android'
console.log(msg, !isAndroid)
if (!isAndroid) {
// 如果是IOS
const payload = msg.payload
// 【APP离线】收到消息,但没有提醒(发生在一次收到多个离线消息时,只有一个有提醒,但其他的没有提醒)
// 【APP在线】收到消息,不会触发系统消息,需要创建本地消息,但不能重复创建。必须加msg.type验证去除死循环
if (msg.aps == null && msg.type === 'receive') {
const messageTitle = payload.messageTitle || ''
const messageContent = payload.messageContent || ''
// 创建本地消息,发送的本地消息也会被receive方法接收到,但没有type属性,且aps是null
plus.push.createMessage(messageContent, JSON.stringify(payload), {
title: messageTitle
})
}
} else {
let payload:any = {}
console.log(typeof msg.payload === 'object', typeof msg.payload)
if (typeof msg.payload === 'object') {
payload = msg.payload || {}
} else {
payload = JSON.parse(msg.payload) || {}
}
console.log(payload)
const messageTitle = payload.messageTitle || ''
const messageContent = payload.messageContent || ''

      plus.push.createMessage(messageContent, JSON.stringify(payload), {  
        title: messageTitle  
      })  
    }  
    console.log(msg)  
  },  
  false  
)  

// 消息的点击事件  
plus.push.addEventListener(  
  'click',  
  function (msg: any = {}) {  
    const isAndroid = plus.os.name === 'Android'  
    if (!isAndroid) {  
      // 如果是IOS  
      let payload  
      if (msg.type === 'click') {  
        // APP离线点击包含click属性,这时payload是JSON对象  
        payload = msg.payload  
      } else {  
        // APP在线,收到消息不会包含type属性,这时的payload是JSON字符串,需要转为JSON对象  
        if (typeof msg.payload === 'object') {  
          payload = msg.payload || {}  
        } else {  
          payload = JSON.parse(msg.payload) || {}  
        }  
      }  
         // 点击后业务逻辑的处理  
      this.messageClick(payload)  
    } else {  
      // 如果是Android,收到playload均是是JSON字符串,需要转为JSON对象  
      let payload:any = {}  
      if (typeof msg.payload === 'object') {  
        payload = msg.payload || {}  
      } else {  
        payload = JSON.parse(msg.payload) || {}  
      }  
      // 点击后业务逻辑的处理  
      this.messageClick(payload)  
    }  
  },  
  false  
)


如果想离线推送需APP上架市场 申请厂商推送 把相应参数填写到Dcloud后台  
收起阅读 »

【分享】小程序原生项目转成uni-app中的vue项目

微信小程序

本片文章将从以下三个方面进行讲解:

  • 背景
  • 使用转换
  • 注意事项

一、背景

之前一段时间,调研过跨平台开发一系列框架,最终筛选出uni-app这款可以适用多端的开源框架。一套代码可以同时生成ios,Android,H5,微信小程序,支付宝小程序,百度小程序等。拓展能力很强,封装了H5+,支持nvue,也支持原生Android,ios开发。可以将原有的移动应用和H5应用改成uni-app应用。基于此,恰好可以将我们之前早期的小程序项目,转化成基于vue.js的uni-app组件

这将带来很多优势:

  • 更优的组件渲染更新性能
  • 更强的拓展性、和可维护性
  • 更快的打包速度、和更便捷的打包方式
  • 一套代码多端适用
  • ...

二、使用转换

使用官方推荐的HBuilderX, github地址:https://github.com/zhangdaren/miniprogram-to-uniapp。该插件十分强大,通过简单的脚本命令,直接将我本地的小程序原生项目,clone生成了一份vue.js的项目。
诸如: 小程序setData的兼容、小程序生命周期的兼容,小程序各种api的封装,几乎涵盖了所有的小程序原生api。
但是尽管如此,仍是有很多需要注意的点,在实际开发中需要我们注意的。
事先声明,以下需要关注的点,是本人在迁移自己的小程序项目中遇到的切实存在 的问题,如果有更好的解决方案,欢迎讨论。

三、注意事项【踩坑】

  1. 对于wx.setData的处理:
    HBuilderX转换过来之后,会在app.js中加入setData的重写,但这个是通过vue中this.$set和this.$get来实现的,也就是说尽管小程序中的setData没有被转化成this.xxx =...的方式(vue的语法),但仍然可以生效。可是如果setData处于原来小程序组件的生命周期中如attached,这种生命周期会被转换为beforeMount 这时setData就会有报错,解决办法:(1)修改成this.xx = ...的方式 (2) 删除生命周期,因为vue中不支持attache等生命周期函数, 如果非要在组件挂载或者更新的时候将prop或其他变量存储到data中,可以尝试通过watch函数。
  2. 对于wx.selectComponent
    经过反复测试,uni-app不会转化这个api,但是转成vue组件后,这个api将不在生效,需要替换成ref获取节点。
    解决方案:这点可以使用ref替换,如果转换前微信组件的生命周期中,可以直接this.$refs.xxx.api()。如果转换前是在微信lifetimes中定义的生命周期,uni-app会转换成vue的生命周期,这时用ref不能直接调用组件的api需要加一层 .$vm, 既this.$refs.xxx.$vm.api()
  3. 由于小程序的语法不强制要求wxml必须最外层是单节点。组件中<page></page>节点下如果存在多节点,转换后会多一层dom节点, 当然如果在wxml中有最外层的container的话就忽略这个问题。
    • 原生小程序:
      <page>  
      1  
      2  
      3  
      </page>
    • 转换后
      <page>  
      <view>  
       1  
        2  
       3  
      </view>  
      </page>

      这在组件中会非常致命,会直接引起之前的样式如滚动条超出,高度的一些100%设置,会失效。导致样式错乱,要格外小心。

  4. 事件绑定
    (1)catchtap="true" 在小程序支持这种写法,可以理解为绑定一个空函数,转换之后变成@tap.stop="true" 这个在uni-app中会报错 需要改成 @tap.stop="emptyFunc".
    (2)很多catch方法会被自动转换,如catchtap ----> @tap.stop 但是有些方法如catchtouchmove,这个方法不会转为@touchmove.stop,需要手动修改 。
  5. 转换过程部分函数名会被修改,如果小程序中定义了xxx,以""开头得函数名都会被修改名字,在调用的时候就找不到对应的函数了,以及vue内置得hide、show等方法名字会被修改成hideFun:
  6. data中引用类型的状态,以“_”开头的状态均无法通过this访问,都是undefined,需要手动更改状态变量的名称。
  7. 通过ref获取子组件的data,需要去掉data,如this.$ref.modal.data.name 需要换成 this.$ref.modal.name
  8. 有时我们想要父组件访问子组件的属性和方法,会将子组件的this作用域回调给父组件,这种方式在转化为uni-app后不会直接报错,但是无法获取到,进行下一步操作会报错。解决方案:在父组件直接使用ref。
  9. 一些写死的属性数据转换过来可能会变成 字符串,如showTitle="{{true}}"转换之后变成 showTitle="true" 这个时候如果子组件还当作boolean类型处理,就会报错。
  10. 奇怪的input:如果需要动态绑定input的value,并针对用户输入做一些必要的过滤,如不允许输入数字。 需要在用户输入的时候更改对应的value值,同时在input事件中要把经过js处理的value值return,这样才可以成功过滤input的value,否则不管如果用正则拦截,input输入框上都会输入啥显示啥。
  11. . vue组件中data中的数据this.xxx去获取,小程序转过来极少的时候是this.data.xxx需要手动更改一下,这个问题具体什么时候出现还不太明确,但是需要注意一下。

四、小程序组件

pages.json文件,这块和小程序不同,不需要再页面级json中配置

{  
    "pages": [  
        {  
            "path": "slide-view/slide-view",  
            "style": {  
                "navigationBarTitleText": "slide-view",  
                "usingComponents": {  
                    "slide-view": "/wxcomponents/miniprogram-slide-view/index"  
                }  
            }  
        }  
    ]  
}

.vue:

<slide-view></slide-view>可直接使用

需要注意:

• 小程序组件需要放在项目特殊文件夹 wxcomponents(或 mycomponents、swancomponents)。HBuilderX 建立的工程 wxcomponents 文件夹在 项目根目录下。vue-cli 建立的工程 wxcomponents 文件夹在 src 目录下。可以在 vue.config.js 中自定义其他目录
• 小程序组件的性能,不如vue组件。使用小程序组件,需要自己手动setData,很难自动管理差量数据更新。而使用vue组件会自动diff更新差量数据。所以如无明显必要,建议使用vue组件而不是小程序组件。比如某些小程序ui组件,完全可以用更高性能的uni ui替代。

结论

ok,以上就是本人在项目转换迁移过程中遇到的问题。总体感受还是不错的,该说不说的,HBuilderX还是帮我们做了更大部分的事情,但仍需要我们针对各自的业务需求,做一些写法上的调整,做一切事物之前,需要头脑中有一个概念,“小程序中的代码在转换过程会被修改,小程序的实现逻辑,不具有普适性”。

继续阅读 »

本片文章将从以下三个方面进行讲解:

  • 背景
  • 使用转换
  • 注意事项

一、背景

之前一段时间,调研过跨平台开发一系列框架,最终筛选出uni-app这款可以适用多端的开源框架。一套代码可以同时生成ios,Android,H5,微信小程序,支付宝小程序,百度小程序等。拓展能力很强,封装了H5+,支持nvue,也支持原生Android,ios开发。可以将原有的移动应用和H5应用改成uni-app应用。基于此,恰好可以将我们之前早期的小程序项目,转化成基于vue.js的uni-app组件

这将带来很多优势:

  • 更优的组件渲染更新性能
  • 更强的拓展性、和可维护性
  • 更快的打包速度、和更便捷的打包方式
  • 一套代码多端适用
  • ...

二、使用转换

使用官方推荐的HBuilderX, github地址:https://github.com/zhangdaren/miniprogram-to-uniapp。该插件十分强大,通过简单的脚本命令,直接将我本地的小程序原生项目,clone生成了一份vue.js的项目。
诸如: 小程序setData的兼容、小程序生命周期的兼容,小程序各种api的封装,几乎涵盖了所有的小程序原生api。
但是尽管如此,仍是有很多需要注意的点,在实际开发中需要我们注意的。
事先声明,以下需要关注的点,是本人在迁移自己的小程序项目中遇到的切实存在 的问题,如果有更好的解决方案,欢迎讨论。

三、注意事项【踩坑】

  1. 对于wx.setData的处理:
    HBuilderX转换过来之后,会在app.js中加入setData的重写,但这个是通过vue中this.$set和this.$get来实现的,也就是说尽管小程序中的setData没有被转化成this.xxx =...的方式(vue的语法),但仍然可以生效。可是如果setData处于原来小程序组件的生命周期中如attached,这种生命周期会被转换为beforeMount 这时setData就会有报错,解决办法:(1)修改成this.xx = ...的方式 (2) 删除生命周期,因为vue中不支持attache等生命周期函数, 如果非要在组件挂载或者更新的时候将prop或其他变量存储到data中,可以尝试通过watch函数。
  2. 对于wx.selectComponent
    经过反复测试,uni-app不会转化这个api,但是转成vue组件后,这个api将不在生效,需要替换成ref获取节点。
    解决方案:这点可以使用ref替换,如果转换前微信组件的生命周期中,可以直接this.$refs.xxx.api()。如果转换前是在微信lifetimes中定义的生命周期,uni-app会转换成vue的生命周期,这时用ref不能直接调用组件的api需要加一层 .$vm, 既this.$refs.xxx.$vm.api()
  3. 由于小程序的语法不强制要求wxml必须最外层是单节点。组件中<page></page>节点下如果存在多节点,转换后会多一层dom节点, 当然如果在wxml中有最外层的container的话就忽略这个问题。
    • 原生小程序:
      <page>  
      1  
      2  
      3  
      </page>
    • 转换后
      <page>  
      <view>  
       1  
        2  
       3  
      </view>  
      </page>

      这在组件中会非常致命,会直接引起之前的样式如滚动条超出,高度的一些100%设置,会失效。导致样式错乱,要格外小心。

  4. 事件绑定
    (1)catchtap="true" 在小程序支持这种写法,可以理解为绑定一个空函数,转换之后变成@tap.stop="true" 这个在uni-app中会报错 需要改成 @tap.stop="emptyFunc".
    (2)很多catch方法会被自动转换,如catchtap ----> @tap.stop 但是有些方法如catchtouchmove,这个方法不会转为@touchmove.stop,需要手动修改 。
  5. 转换过程部分函数名会被修改,如果小程序中定义了xxx,以""开头得函数名都会被修改名字,在调用的时候就找不到对应的函数了,以及vue内置得hide、show等方法名字会被修改成hideFun:
  6. data中引用类型的状态,以“_”开头的状态均无法通过this访问,都是undefined,需要手动更改状态变量的名称。
  7. 通过ref获取子组件的data,需要去掉data,如this.$ref.modal.data.name 需要换成 this.$ref.modal.name
  8. 有时我们想要父组件访问子组件的属性和方法,会将子组件的this作用域回调给父组件,这种方式在转化为uni-app后不会直接报错,但是无法获取到,进行下一步操作会报错。解决方案:在父组件直接使用ref。
  9. 一些写死的属性数据转换过来可能会变成 字符串,如showTitle="{{true}}"转换之后变成 showTitle="true" 这个时候如果子组件还当作boolean类型处理,就会报错。
  10. 奇怪的input:如果需要动态绑定input的value,并针对用户输入做一些必要的过滤,如不允许输入数字。 需要在用户输入的时候更改对应的value值,同时在input事件中要把经过js处理的value值return,这样才可以成功过滤input的value,否则不管如果用正则拦截,input输入框上都会输入啥显示啥。
  11. . vue组件中data中的数据this.xxx去获取,小程序转过来极少的时候是this.data.xxx需要手动更改一下,这个问题具体什么时候出现还不太明确,但是需要注意一下。

四、小程序组件

pages.json文件,这块和小程序不同,不需要再页面级json中配置

{  
    "pages": [  
        {  
            "path": "slide-view/slide-view",  
            "style": {  
                "navigationBarTitleText": "slide-view",  
                "usingComponents": {  
                    "slide-view": "/wxcomponents/miniprogram-slide-view/index"  
                }  
            }  
        }  
    ]  
}

.vue:

<slide-view></slide-view>可直接使用

需要注意:

• 小程序组件需要放在项目特殊文件夹 wxcomponents(或 mycomponents、swancomponents)。HBuilderX 建立的工程 wxcomponents 文件夹在 项目根目录下。vue-cli 建立的工程 wxcomponents 文件夹在 src 目录下。可以在 vue.config.js 中自定义其他目录
• 小程序组件的性能,不如vue组件。使用小程序组件,需要自己手动setData,很难自动管理差量数据更新。而使用vue组件会自动diff更新差量数据。所以如无明显必要,建议使用vue组件而不是小程序组件。比如某些小程序ui组件,完全可以用更高性能的uni ui替代。

结论

ok,以上就是本人在项目转换迁移过程中遇到的问题。总体感受还是不错的,该说不说的,HBuilderX还是帮我们做了更大部分的事情,但仍需要我们针对各自的业务需求,做一些写法上的调整,做一切事物之前,需要头脑中有一个概念,“小程序中的代码在转换过程会被修改,小程序的实现逻辑,不具有普适性”。

收起阅读 »

uni-app UDP发送与接收

UDP
var DatagramSocket = plus.android.importClass("java.net.DatagramSocket");  
var DatagramPacket = plus.android.importClass("java.net.DatagramPacket");  
var InetAddress = plus.android.importClass("java.net.InetAddress");  
var String = plus.android.importClass("java.lang.String");  
var udp = new DatagramSocket();  

//UDP发送  
var sendBuffer = {  
    "test": 123  
}  
var SendBuff = JSON.stringify(sendBuffer)  
var data = new String(SendBuff).getBytes(); //发送中文需要指定编码  
console.log((new String(data)).toString())  
var packet = new DatagramPacket(data, data.length, InetAddress.getByName("10.10.100.254"), 9091);  
udp.send(packet);  

//UDP接收  
let charArr = new Array(100).fill(0) //100为接收数据长度,按需修改  
var receivePacket = new DatagramPacket(charArr, charArr.length);  
udp.setSoTimeout(5000); //新增,一定要设置超时,否则在没有收到返回数据的时候容易卡死  
udp.receive(receivePacket);  
var receiveData = (new String(receivePacket.getData())).toString().substring(0,receivePacket.getLength()); //receivePacket.getLength()可获取到接收数据长度,根据数据长度剪切出接收到的真实数据  
console.log(receiveData)  
udp.close();
继续阅读 »
var DatagramSocket = plus.android.importClass("java.net.DatagramSocket");  
var DatagramPacket = plus.android.importClass("java.net.DatagramPacket");  
var InetAddress = plus.android.importClass("java.net.InetAddress");  
var String = plus.android.importClass("java.lang.String");  
var udp = new DatagramSocket();  

//UDP发送  
var sendBuffer = {  
    "test": 123  
}  
var SendBuff = JSON.stringify(sendBuffer)  
var data = new String(SendBuff).getBytes(); //发送中文需要指定编码  
console.log((new String(data)).toString())  
var packet = new DatagramPacket(data, data.length, InetAddress.getByName("10.10.100.254"), 9091);  
udp.send(packet);  

//UDP接收  
let charArr = new Array(100).fill(0) //100为接收数据长度,按需修改  
var receivePacket = new DatagramPacket(charArr, charArr.length);  
udp.setSoTimeout(5000); //新增,一定要设置超时,否则在没有收到返回数据的时候容易卡死  
udp.receive(receivePacket);  
var receiveData = (new String(receivePacket.getData())).toString().substring(0,receivePacket.getLength()); //receivePacket.getLength()可获取到接收数据长度,根据数据长度剪切出接收到的真实数据  
console.log(receiveData)  
udp.close();
收起阅读 »

uni-app 获取当前连接的WiFi名

移动APP
getWifiName: function() {  
                var wifiManager, wifiInfo;  
                var Context = plus.android.importClass("android.content.Context");  
                var WifiManager = plus.android.importClass("android.net.wifi.WifiManager");  
                var WifiInfo = plus.android.importClass("android.net.wifi.WifiInfo");  
                wifiManager = plus.android.runtimeMainActivity().getSystemService(Context.WIFI_SERVICE);  
                wifiInfo = wifiManager.getConnectionInfo();  
                var ssid = wifiInfo.getSSID() || '';  
                if (ssid.length == 0) {  
                    return null;  
                }  
                //一些手机上获取SSID是有值的,但是实际IP为空,真实为未连接    
                var i = parseInt(wifiInfo.getIpAddress());  
                var ipStr = (i & 0xFF) + "." +  
                    ((i >> 8) & 0xFF) + "." +  
                    ((i >> 16) & 0xFF) + "." +  
                    (i >> 24 & 0xFF);  
                if (ipStr == "0.0.0.0") {  
                    console.log("NULL");  
                    return null;  
                }  

                if (ssid != "<unknown ssid>" && ssid.toUpperCase() != "0X") {  
                    console.log(ssid.replace(/\"/g, ""));  
                    return ssid.replace(/\"/g, "");  
                }  
                console.log("NULL");  
                return null;  
            },
继续阅读 »
getWifiName: function() {  
                var wifiManager, wifiInfo;  
                var Context = plus.android.importClass("android.content.Context");  
                var WifiManager = plus.android.importClass("android.net.wifi.WifiManager");  
                var WifiInfo = plus.android.importClass("android.net.wifi.WifiInfo");  
                wifiManager = plus.android.runtimeMainActivity().getSystemService(Context.WIFI_SERVICE);  
                wifiInfo = wifiManager.getConnectionInfo();  
                var ssid = wifiInfo.getSSID() || '';  
                if (ssid.length == 0) {  
                    return null;  
                }  
                //一些手机上获取SSID是有值的,但是实际IP为空,真实为未连接    
                var i = parseInt(wifiInfo.getIpAddress());  
                var ipStr = (i & 0xFF) + "." +  
                    ((i >> 8) & 0xFF) + "." +  
                    ((i >> 16) & 0xFF) + "." +  
                    (i >> 24 & 0xFF);  
                if (ipStr == "0.0.0.0") {  
                    console.log("NULL");  
                    return null;  
                }  

                if (ssid != "<unknown ssid>" && ssid.toUpperCase() != "0X") {  
                    console.log(ssid.replace(/\"/g, ""));  
                    return ssid.replace(/\"/g, "");  
                }  
                console.log("NULL");  
                return null;  
            },
收起阅读 »

HbuilderX 从命令行启动指定文件夹

HBuilder X

3.29 更新

  • 今天看HB更新了 多了个 cli命令;原本以为是path 结果不是

  • 我是这样用的 :
    alias cli="/Applications/HBuilderX.app/Contents/MacOS/cli"

  • 用法就变成这样:

额 感觉还是差点意思(小声)

命令行 指定HbuilderX 启动指定目录(历史内容)

  • 在VsCode 中 可以用code 加目录 快捷启动 编辑器
  • 我看了下 如果在 HbuilderX中 可以通过设置别名 达到这个效果 以Mac (zsh)为例
#首先打开 .zshrc  
#设置别名   
alias hbd='hbd(){open $1 -a /Applications/HBuilderX.app}; hbd'

source 后 就可以如下操作了

cd  某个目录  
git pull  

hbd .

windows10 下

#首先打开 $PROFILE  

function hbd([string]$_path){  
   {hbuilder.exe的路径} $_path  
}
  • 嗯 符合我的想法
继续阅读 »

3.29 更新

  • 今天看HB更新了 多了个 cli命令;原本以为是path 结果不是

  • 我是这样用的 :
    alias cli="/Applications/HBuilderX.app/Contents/MacOS/cli"

  • 用法就变成这样:

额 感觉还是差点意思(小声)

命令行 指定HbuilderX 启动指定目录(历史内容)

  • 在VsCode 中 可以用code 加目录 快捷启动 编辑器
  • 我看了下 如果在 HbuilderX中 可以通过设置别名 达到这个效果 以Mac (zsh)为例
#首先打开 .zshrc  
#设置别名   
alias hbd='hbd(){open $1 -a /Applications/HBuilderX.app}; hbd'

source 后 就可以如下操作了

cd  某个目录  
git pull  

hbd .

windows10 下

#首先打开 $PROFILE  

function hbd([string]$_path){  
   {hbuilder.exe的路径} $_path  
}
  • 嗯 符合我的想法
收起阅读 »