技术选型
公司需求要做app.微信小程序,支付宝小程序三端的东西,查阅了一些资料,最终选择用uni-app,以下是一些其他前辈技术选型的文章,各位可以学习一下。
uni-app官方文档:
掘金上选型文章:
对比和参照之后,再结合我司情况,技术总监选择了uni-app
我认为的坑?
其实我觉得自己的水平不能够去评价一款框架的好坏,只是简单的说一下开发体验,我认为坑大概分三种。
1:该框架确实没有的功能,存在不能实现的东西。
2:明显的bug,导致程序出现问题,影响开发进度和结果
3:在开发过程中,自己凭借直觉和经验在开发,结果和预期不同,阅读文档过后才发现和之前经验积累的写法不一样,会不由自主的说一句真坑,或者说在不同端有的不同写法,但自己没有做好兼容,就出现了不同端效果不一致,当再次阅读文档找到问题后也会来一句真坑。
总的来讲,这次Uni-app的开发之旅还是比较通畅的。
踩坑之旅
前言:我只说我遇见过的问题难点,和一些注意点,以及怎么去解决,不会讲uni-app怎么用,怎么用大家还是去阅读文档吧,文档上写的更清楚
像素单位
使用upx/rpx 而不是 px
修改内容(评论区大佬的订正): 1px = 2upx是不准确的,upx和rpx是响应式单位,以750px为基准宽度,根据设备屏幕宽度自动调整
引入vuex
常规的引入方式在uniapp中是不行的,需要在mian.js
中,vue原型上挂载一次
4月20日更新:评论区有朋友提出现在以这样的形式可以引入vuex了,我想是uniapp团队优化之后的内容
路由
uni-app的路由全部配置在page.json
这个文件中,问题就在于多人开发的时候,路由无法拆分,如果处理的不好,经常发生冲突。至于其中的一些配置项,就请见官方文档。
在页面中没有专门的 $route
和 $router
对象 仅能在页面的生命周期里面接受路由传参,详情见文档。
路由传参方式
let url = `/pages/shopManagement/sonPage/billDetails?StoreID=${StoreID}`
路由接参方式:
onLoad(route){
this.getData.StoreID=route.StoreID
this.getCurryInfo()
},
onLoad接收到一个参数对象
DOM操作
如果你的项目仅是h5,那可以放心大胆的使用dom操作,但如果要在小程序和app跑,就不要做dom操作了,不生效。
不过ref还是可以用的,一样可以获取到这个节点,该干啥干啥。
生命周期
说到ref我就要提一下生命周期
具体的生命周期在文档中可以看详情
大致上和vue的差不多,分成页面生命周期和应用生命周期,页面生命周期就是针对单页面的,应用生命周期就是针对整个小程序/app的,不过我提出在开发时的一些情况
在组件中,没有生命周期,对,你没看错!比如页面a引用了组件b 在组件b中,onLoad,onShow,onReady全部失效,不过用created和mounted是生效的,但是我在开发的时候还是没有用created和mounted,毕竟文档明确写到
所以我在组件中规避使用原vue的生命周期,另外,在上面说了ref,如果要在初始化使用ref要注意生命周期,在onload和show的钩子中,内部如果是同步操作是用不了的,拿不到$refs
,我不知道怎么解释这个问题,在vue中很好解释,在created拿不到ref是因为dom还没有渲染出来,只有在mounted时dom渲染出来了才能拿到ref,但是uniapp中不是没得dom嘛。。。。。我也没深究过,如果要用,只能异步,可以加setTimeout 或者 放在某个请求后用,这个时候是可以拿到ref的
201912.2更新
最近几个月又开了个新项目,赶得很紧,不过有了第一个app的基础,开发起来很顺手。
同时也在重构后台系统,有时候div和view傻傻分不清老是整错。
好了,言归正传,今日我在交流群里看见有朋友问起了组件内生命周期的问题,我也讲了自己的疑惑,我就直接贴图给大家看看。
也就是说其实在非页面组件里面使用vue自带的生命周期是可行的,之前我没有尝试过,所以一直规避了这种用法,把很多东西都放在页面的生命周期里面搞。
关于请求
我们最开始的时候是自己简单的封装了一下发送的请求
export const HttpRequest_ = {
config: function(name) {
var info = null;
if (name) {
var name2 = name.split("."); //字符分割
if (name2.length > 1) {
info = configdata[name2[0]][name2[1]] || null;
} else {
info = configdata[name] || null;
}
if (info == null) {
let web_config = cache.get("web_config");
if (web_config) {
if (name2.length > 1) {
info = web_config[name2[0]][name2[1]] || null;
} else {
info = web_config[name] || null;
}
}
}
}
return info;
},
post: function(url, data, header) {
header = header || "application/x-www-form-urlencoded";
//url = this.config("APIHOST")+url;
return new Promise((succ, error) => {
showLoading_()
uni.request({
url: url,
data: data,
method: "POST",
header: {
"content-type": header
},
success: function(result) {
hidLoading_()
succ.call(self, result.data)
},
fail: function(e) {
hidLoading_()
error.call(self, e)
}
})
}).then(res=>{
console.log(res)
return res
},err=>{
console.log('err:',err)
})
},
get: function(url, data, header) {
header = header || "application/x-www-form-urlencoded";
//url = this.config("APIHOST")+url;
return new Promise((succ, error) => {
showLoading_()// 加载中
uni.request({
url: url,
data: data,
method: "GET",
header: {
"content-type": header
},
success: function(result) {
hidLoading_() //关闭加载中
succ.call(self, result.data)
},
fail: function(e) {
hidLoading_() //关闭加载中
error.call(self, e)
}
})
}).then(res=>{
console.log(res)
return res
},err=>{
uni.showToast({
duration:2000,
title:'数据异常,请稍后再试',
icon:'none'
})
console.log('err:',err)
})
}
}
之前我以为在Uniapp中发送请求只能用他们请求方法,后来同事说也可以用其他的。
我们便引入其他的库。这是uniapp插件市场别人封装好的,用起来还是比较舒服
导航栏
导航栏注意的一个问题就是不同端的不同展示形式,所以需要处理兼容问题。
导航栏,可以用自定义的,可以用框架提供的,也有一些插件,都还是可以用的,就是注意显示的区别,如果有多端需求,一定要在不同端跑一下看看效果,不然到时候会很烦。我们一直用的colorUi的导航栏,自己稍微改动了一下
在支付宝小程序中,原生的导航栏是取消不了,所以不能用自定义的插件,就需要在page.json
中配置原生的导航栏
随便找了一个页面,我们的头部导航是条件编译的:
动态的class style
具体的文档在这里:
我一开始是没有注意到这一点,按照我的一些常规习惯写并且一直在用h5调试,没有任何问题,后来上真机和小程序开发工具之后才发现全部失效。
打开第三方的网址或app
在app端想要打开第三方的网址或者程序,一定要区分ios和安卓端。
首先,ios和安卓唤起第三方app的地址是不一样的。
不管是在调试还是打包,要唤起第三方程序,在ios端要配置白名单
三方登录
以微信登录位例:
在app端,uni-app集成的几个方法,可以很顺利的拿到unionId,openid等一些列信息
uni.getProvider({//获取uniapp支持的第三方数据
service:'oauth'
}).then(data=>{
var [err,res] = data
var providers=res.provider//类型(微信,新浪,小米,qq)
var flagIndex=providers.indexOf(provider)
if(flagIndex>-1){
return providers[flagIndex] /
}
}).then(res=>{
return uni.login({//登陆接口(可以获取用户信息)
provider:res,
scopes:'auth_base',
timeout:20000,
})
}).then(data=>{//返回一系列登陆信息
var [err,res] = data
if(res.errMsg==="login:ok"){
self.authResult=res.authResult
return res.authResult
}
}
}).then(res=>{//获取用户的信息 头像,地址,等等等
return uni.getUserInfo({
provider:provider,
timeout:20000,
withCredentials:true
})
}).then(data=>{//得到一些列用户信息
var [err,res] = data
console.log(res)
if(res.errMsg==="getUserInfo:ok"){
return res
}
})
但是如果在小程序端,很多方法就失效了,因为小程序有一套自己的三方登录交互策略。
还记得当时刚在app上测成功微信三方登陆后,领导过来看进度,问小程序怎么样,我给他放了个体验版,让他看看,他问我这个微信登录也可以吗?我拍拍胸脯说没得问题,随便登,结果。。。。。。。。。。。。。。。脸疼
uni.login({//登陆接口
provider:'weixin',
scopes:'auth_base',
timeout:20000,
}).then(data=>{//返回一系列登陆信息
let [err,res] = data
if(res.code){
let data ={//这个code很重要,需要拿到code向后台去换unionid等
js_code: res.code
}
return this.$Request.get(this.$store.state.getopenidUrl,data)
}else{
setTimeout(()=>{
this.$api.msg('数据异常')
},500)
uni.switchTab({
url:'/pages/index/index'
})
}
}).then(res=>{
let res_ = JSON.parse(res.Data)
if('unionid' in res_){
this.getIsBindData.openid=res_.unionid
this.getDataWX.openid = res_.unionid
this.getDataWX.unionid = res_.openid
}else{
this.getIsBindData.openid=res_.openid
this.getDataWX.openid = res_.openid
this.getDataWX.unionid = res_.openid
}
return this.WXuserInfo
})
其他的三方登录我没有试过,但是一定要注意各端之间的差异性
另外,支付宝三方登录uni-app没有集成,要是自己想做,就用原生来写,理论上是可以做的。由于我们团队没有会原生的,我们试过用webview做支付宝的三方登录,最后还是卡在了授权这一块,不得而终,遂阉了该需求。
2019 8.20 更新:
在插件市场已经有了安卓端和ios端授权登录的插件(是付费插件)详情:
canvas
写成组件在小程序上失效,仅能在index页面上使用。在h5生效,封装成组件也可以,渲染效果也不是太好,app端没有试过,因为后来看效果不好就暂时搁置这个需求了。
评论区朋友的订正: canvas在组件中使用时,记得传递第二个参数组件实例对象:uni.createCanvasContext(canvasId, this)
我试过了,封装成组件的情况下,传入this,在小程序是生效的.之前是没有传入的
注意权限模块与sdk的配置
我之前从未写过app和小程序,对一些东西不够敏感
在我调试地图,导航等功能的时候用的很顺畅,后来打包就失效了,就是因为没配好,原来在我们调试的时候用的是Uniapp的。
另外需要用到一些权限的时候记得也要去manifest.json中去配置,总的来讲,图形化界面配置还是比较友好,源码配置也没有那么复杂,基本上都能查到
个人建议只要是真机调试就用自定义基座,尽量不要用uniapp给的基座,这样可以避免一些干扰,在标准基座没问题,一打包就出问题,用自定义基座会好一些。
nvue
weex魔幻的东西太多了,我就不一一列举了
比如weex中自带flex布局,但是排布顺序是竖着的,你说这个是不是头疼。
我用原生插件拼样式的时候真滴是痛苦,好几处觉得分分钟能解决的东西,半天搞不定,我当时还在想不会是记忆出错了?不是这么用的,还查了半天,结果依旧不生效,突然反应过来,别在weex里面就不是这么用,一看文档,果然如此!
个人建议先把weex的文档通读一下,或者用某个东西之前先去查查看怎么用。别一根筋去根据自己脑海里的css知识来对抗另一套标准的css,这样永远都对不准。
2019 8.20 更新:
评论区朋友补充: nvue要支持upx,需要使用uni-app编译模式,而不是weex编译模式
开屏引导
uni-app没得开屏这个配置项,只能用一些策略来做。
实质上是是用swiper组件+本地缓存做的模拟开屏引导,注意的是,如果产品一开始就定位了要做引导页,那就考虑好index的怎么写,我们是后期才打算加的,如果要把index改掉成本有点大,所以用了另一种策略,但如果在性能不好的手机上会出现尴尬的事情,就不细说怎么尴尬了,如果用上面的策略,再尴尬也不过是白屏,在上面的demo中,index仅是一个中转页面,什么都没有写。所以个人建议还是用以上的策略。
路径别名
至今我没有我没有找到如何配置路径别名的文件或者方法。
在uniapp中 @ 是表示根路径,不过整个文件结构还是很清晰的,没有那么多的配置文件,整个根文件很像常规的src目录,用@基本上也很舒服。
插件市场和生态
总的来讲,Uniapp的插件市场还是不错的,大多数能用到的插件都可以找到,找不到的也可以从个别相似的插件中找到灵感,自己再魔改一下,论坛也还行,基本上自己遇到的问题都能找到答案,但是也有找不到的或者有人提出却无人回答的,官方qq群也还是比较热闹,有些东西自己没遇见的看看别人的问题和解决方案也算是一种成长,自己遇见过的给别人解答,换来一声谢谢也是很开心的。
压缩图片
2019 8.16更新
文档:
我不知道为什么 uni.compressImage(OBJECT) 这个接口在app端失效微信小程序生效,报错信息也很简单,就是说压缩失败,没有其他的提示,我在官方群问了一圈没人给我回答,看了下社区也没有太有价值的回答。
后来自己慢慢琢磨了一下,也参考了这位老哥的文章(文章中也包含了转换base64的方法),简单的封装了个函数。
参考文章:
/* src:图片的本地路径 quality:压缩范围 0-100 */
function zipImage(src,quality=30){
// #ifdef APP-PLUS app
let index = src.lastIndexOf(".")
let imgDirname = src.substring(0,index)//图片的原始地址
let imgName = new Date().getTime();//压缩后的文件
let imgType = src.substring(index+1,src.length);//图片的类型
return new Promise((resolve,rej)=>{
plus.zip.compressImage({
src,
dst: imgDirname+imgName+'.'+imgType,
quality,
}, res => {
resolve(res.target)
}, err => {
console.log(err);
rej(err)
});
})
// #endif
// #ifdef MP-WEIXIN 微信小程序
return new Promise((resolve,rej)=>{
uni.compressImage({
src,
success:(res)=>{
console.log(res,1)
let path = res.tempFilePath
resolve(path)
// this.imgList.push(path)
// this.imgList.push
},
fail:(e)=>{
uni.showToast({
title:'上传失败,请重新上传',
duration:2000
})
},
complete:(val)=>{
console.log(val,3)
}
})
})
// #endif
}
调用的时候,直接会吐出来压缩后的图片路径
uni.chooseImage({
success:(res)=>{
res.tempFiles.forEach(async (it)=>{
let localPath =it.path
localPath = await this.$api.zipImage(localPath) //await一下就可以接住了
this.imageList.push({
value:localPath
})
})
}
});
20120 2.19更新
最近又写了两个uniapp的项目 基本上没踩什么坑
倒是加入了几个新功能,在这里稍微总结一下
第一个是app推送 第二个是语音播报 第三个是直播
app推送
这是官方的说明 按照配置配置结束以后 就可以开始测试了,但是测试之前需要拿到一个cid 拿cid的函数就是这个
const clientInfo = plus.push.getClientInfo()
let pushInfo = {
clientid: clientInfo.clientid,
appid: clientInfo.appid,
appkey: clientInfo.appkey
};
//这端代码可以拿到需要的一些信息,包括cid
//拿到cid就可以开始发送测试的推送了
至于透传和通知栏信息的区别是什么,在百度或者谷歌一搜会有很多讲的详细的文章,比我这个前端菜鸡说的仔细的多,所以我就不多加赘述了。
在收到推送之前,也可以检测一下用户是否看起了推送权限,直接上代码吧
async function pushPower(phoneType){
if(phoneType==='android'){//如果是安卓
console.log('安卓')
let main = plus.android.runtimeMainActivity();
let pkName = main.getPackageName();
let NotificationManagerCompat plus.android.importClass("android.support.v4.app.NotificationManagerCompat");
let packageNames = NotificationManagerCompat.from(main);
// console.log(JSON.stringify(packageNames));
if (packageNames.areNotificationsEnabled()) {
console.log('已开启通知权限');
}else{
/*这段函数是自己封装的弹框询问*/
let res = await this.$api.showModal({
title: '是否开启推送权限',
content: '您还未开启推送权限,是否开启?不开启推送权限部分功能将不完善。'
})
/*这段函数是自己封装的弹框询问*/
if(res){
let Intent = plus.android.importClass('android.content.Intent');
let intent = neIntent('android.settings.APP_NOTIFICATION_SETTINGS');//可设置表中所有Action字段
intent.putExtra('android.provider.extra.APP_PACKAGE', pkName);
main.startActivity(intent);
}
}
}else if(phoneType==='ios'){//如果是ios
let UIApplication = plus.ios.import("UIApplication");
let app = UIApplication.sharedApplication();
let enabledTypes = 0;
if (app.currentUserNotificationSettings) {
let settings = app.currentUserNotificationSettings();
enabledTypes = settings.plusGetAttribute("types");
} else {
//针对低版本ios系统
enabledTypes = app.enabledRemoteNotificationTypes();
}
plus.ios.deleteObject(app);
if ( 0 == enabledTypes ) {
/*这段函数是自己封装的弹框询问*/
let res = await this.$api.showModal({
title: '是否开启推送权限',
content: '您还未开启推送权限,是否开启?不开启推送权限部分功能将不完善。'
})
/*这段函数是自己封装的弹框询问*/
if(res){
let UIApplication = plus.ios.import("UIApplication");
let NSURL = plus.ios.import("NSURL");
let setting = NSURL.URLWithString("app-settings:");
let application = UIApplication.sharedApplication();
application.openURL(setting);
plus.ios.deleteObject(setting);
plus.ios.deleteObject(application);
}
}
}
}
反正做不做权限检查也看各位的需求
最后一步就是监听推送信息了,在这里我就不放我的代码了,给大家提供一个文章,这个文章里面详细的撰写了怎么去监听推送等。
ask.dcloud.net.cn/article/366… 前端这块只需要看他的【8】 里面 对接收不同信息做了详细代码演示。
具体发透传信息还是通知栏信息,接到后怎么处理等,就看各位和后端怎么协商了。
语音播报
这个就很简单了。
就用这个插件,该配置的一配置 对着文档写一下 啥都出来了
直播
直播的话 我觉得大概分成两个方面
第一就是自己的后端自己的搭建推送服务器
1,app的推送:
可以看这位掘金朋友的文章,我当时是请教他了,给我讲了很,但是最后我们没有做自己的直播服务器用的第三方的方案
2,小程序的直播:
我只是看了下文档,后面没有做,他的拉流组件与app用的不是同一个
第二是用现成的直播插件与服务,主要是腾讯云
1,app的直播方案:
我们是花钱买的原生插件,这位作者售后很好,有问必答,而且也是永久维护。
第一款上手比较快,但不算是真正意义的直播,是实时音视,不能做到录屏等功能。但是单纯的播放画面和接收声音是完全没有问题的,连麦,美颜,闭麦静音等都可以。具体的逻辑可以根据自己的业务来做,我们的业务主要是讲课软件,不仅要直播,也需要连麦的功能,这个就很符合我们的需求。
第二个款组件我没有用过,但是也放出来大家可以参考一下。
2,小程序的直播 目前我们没打算在小程序做直播,因为资格不够,但是我在看直播插件的时候,看了个很好的例子,放出来大家也可以看看做做参考。
他是把腾讯视频云小程序的功能做成了uniapp版本的,大家可以参考着来做。
说一点感受,就是uniapp对针对三方SDK的集成支持度不是很好,或者说第三方的sdk针对uniapp的sdk集成不是很好?
20120 3.2更新 :
App权限
昨日在打包的时候,发现我的摄像头权限从来没有被询问过是否使用,直接就给我反应说未授权,我琢磨是哪里没配置,我才去配置项里面找到并配置了,但是这些配置项很多也不太熟悉,索性在论坛里面搜索了很多相关的文章看了看。就发现一个好东西,是关于权限的判断和提示的。
上面推送的内容中,我写到了app推送权限的判断。我觉得还不如人家这个写得好,所以干脆我都改成了这个。
这里面不仅把权限名称都放出来了,就算自己配置打包,也可以拿来做参考,并且也封装成了模块,调用查询用户的功能权限非常的方便!
插件市场里面我看过or用过比较好的组件
先后推荐不分排名顺序
colorUI 我们四个项目,都是以colorUI做的基础样式,他不是一个组件库,是个css样式库,唯一的缺点就是没有太详细的文档,不过源码都在,过一遍基本上也都熟悉了。
===================================
uCharts 高性能跨全端图表,基本上所有类型的图表都包含了,而且文档也比较清楚,demo也全面 源码也可以根据自己的需求做改动,我自己在开发的时候也改动过源码,用起来很顺手。
===================================
手写签名组件,方便易操作。
===================================
答题模版,这个答题模板是基于colorUI做的,我当时自己懒得写了,就直接搬来用。
ext.dcloud.net.cn/plugin?id=4…
===================================
ThorUI组件库 这个库是个正儿八经的组件库,里面包含了常规的各种组件,我们也买了graceUI,其实对比一下,大体上不相上下的,有不同的需求也可以直接改源码啥的。
===================================
侧边导航 不过侧边导航也实现起来也比较简单,这个是我看到的一个组件,看了下源码,如果是拿来练手实现的话,是个比较好的参照对象。
===================================
O2O本地生活模版 这个模板用来快速开发电商项目很好,作者组件封装的很完善,有需求的只要自己改一下就可以用了。
===================================
mescroll的uni版本, 是在 uni-app 运行的下拉刷新和上拉加载的组件 第一个项目用到了他 不过后来我自己封装了一个就没用了。
===================================
基于flyio接口封装
===================================
腾讯云小程序音视频通讯Uniapp版
最后还是一句话
注意各端的差异性,很多东西,h5对着的,上真机就错了,真机好着的,换小程序就错了,不同小程序之间还有差异,总之就是考虑好不同的情况,重点是仔细阅读文档。
虽然可能一些原生可以实现的功能uniapp实现不了,不过整体开发下来还是比较愉快,很多的坑还是因为多端不兼容,除了写起来麻烦一点,基本上都还是有可以解决的策略。
也希望uniapp越做越好,各方面越来越完善,为我这种搬砖码农增加一份生存的筹码。
今天的文章uni-app的开发经历分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/20952.html