近期由于公司业务扩展,组织架构调整,小程序需要进行重构。而之前的小程序区分了微信端和支付宝端,分别采用原生语言进行开发。虽然开发起来很方便,各自使用各自的api,但是最大的问题就是几乎所有的逻辑都需要cv两遍,同时如果有需求迭代,两边都需要进行修改,改动多的话很有可能会出现遗漏的问题。因此这次重构就决定使用多端转换,一套代码搞定。
在框架选择上,开始定位了两个框架,taro和uni。taro在之前公司使用过,是偏向react语法,当时在开发过程中遇到了某些卡死流程的问题,最后不得不用原生重新开发了一遍,因此本人对于taro没有太大的好感。而uni采用vue语法,文档和社区也都相对活跃,因此决定采用uni开发,直接转微信和支付宝两个小程序。
具体开发流程就不细说了,相关api也都和微信小程序的差不太多,只不过前缀换成了uni.
,不太了解的小伙伴可以查看文档uni-app文档,本文主要说一些开发过程中的小问题点。
一、关于环境判断
这个内容基本是开发过程中不可或缺的内容,任何项目和框架开发最基本都会区分开发和生产环境,uni的区分也很大众化,直接用 NODE_ENV 就可以处理。
const isDev = process.env.NODE_ENV === 'development'
二、应用类型判断
就是区分当前环境是在微信小程序、支付宝小程序还是在APP内部,因为个别方法可能会有区别,开发过程中通常是需要进行环境区分。这里分为两种方法(此处主要以微信、支付宝小程序为例):
方法一:比较基础的写法,注意前边的 // 是必备的
// #ifdef MP-WEIXIN
consoel.log('这是微信小程序')
// #endif
// #ifdef MP-ALIPAY
console.log('这是支付宝小程序')
// #endif
方法二:整合法,通常开发中经常会用到区分环境,如果都像方法一那样就很麻烦,可以直接定义一个变量去判断
export function checkMp(){
let mpType = ''
// #ifdef MP-WEIXIN
mpType = 'wx'
// #endif
// #ifdef MP-ALIPAY
mpType = 'ali'
// #endif
return mpType
}
三、关于小程序自定义导航栏
在需求中设计小姐姐经常会设计沉浸式的样式,看上去特别炫,这就需要小程序端设置沉浸式导航,同时可能需要自己开发导航栏。
// 微信小程序只需要设置: "navigationStyle":"custom" 即可。
// 支付宝小程序需要单独配置:
"mp-alipay":{
"titlePenetrate":"YES",
"transparentTitle":"auto",
"allowsBounceVertical": "NO"
}
按照上述配置,可以实现沉浸式和自定义导航的效果。但是支付宝小程序让人🤮的问题又出现了,跳转进入页面后原自带的返回箭头是不会隐藏也没办法去掉的!!!对此,支付宝官方给出的方法是,使用my.hideBackHome()
隐藏掉,当然也没有更好的办法。
顺便贴一下自定义导航栏代码,有需要的小伙伴们可以cv大法走起~
<template name="Nav">
<view class="nav-wrap" :class='navbarData.isBg ? "bgWhite" : ""' :style="{height: height + 'px'}">
<!-- 不用原生状态栏,状态栏占位符 -->
<view :style="{height: statusHeight + 'px'}"></view>
<!-- 标题栏 -->
<view class="title_wrap">
<!-- 导航栏标题 -->
<view class='nav-title' :class="navbarData.isTitleWhite ? 'nav-title-white' : ''" :style="{height: titleHeight + 'px', lineHeight: titleHeight + 'px'}">{{navbarData.title}}</view>
<view :style="{height: titleHeight + 'px'}">
<!-- 导航栏 左上角的返回按钮 和home按钮 -->
<view class='nav-capsule' :style="{height: titleHeight + 'px'}" v-if='navbarData.showCapsuleBack'>
<!-- 从分享进入小程序时 返回上一级按钮不应该存在 -->
<view @click='_navback' v-if='!navbarData.share'>
<image v-if="navbarData.isTitleWhite" src='../../static/nav_back_white.jpg' mode='aspectFill' class='back-pre' />
<image v-else src='../../static/nav_back.png' mode='aspectFill' class='back-pre' />
</view>
</view>
<view class='nav-capsule' :style="{height: titleHeight + 'px'}" v-if='navbarData.showCapsuleNav'>
<view @click='_backhome' class="nav_home_icon">
<image v-if="navbarData.isTitleWhite" src='../../static/nav_home_white.png' mode='aspectFill' class='back-home' />
<image v-else src='../../static/nav_home.png' mode='aspectFill' class='back-home' />
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "Nav",
props: {
//默认值 默认显示左上角
navbarData: {
isBg: false, // 是否有背景色
isTitleWhite: false, // 是否为白色标题
title: '', // 导航标题
showCapsuleNav: 0, // 是否是首页按钮,默认false
showCapsuleBack: 0, // 是否有返回箭头,默认false
share: false // 默认不是分享
}
},
data() {
return {
height: 0,
titleHeight: 0,
statusHeight: 0,
}
},
mounted() {
this.height = getApp().globalData.navHeight
this.titleHeight = getApp().globalData.titleBarHeight
this.statusHeight = getApp().globalData.statusBarHeight
// 支付宝小程序自定义头部,返回按钮不会隐藏
// #ifdef MP-ALIPAY
setTimeout(()=>{
uni.hideBackHome()
}, 500)
// #endif
},
methods: {
// 返回上一页面
_navback() {
uni.navigateBack()
},
//返回到首页
_backhome() {
uni.redirectTo({
url: '../../pages/index/index'
})
}
}
}
</script>
<style scoped>
.nav-wrap {
position: fixed;
width: 100%;
top: 0;
color: #000;
z-index: 9999999;
}
.bgWhite{
background: #FFCA00;
}
.title_wrap{
width: 100%;
position: relative;
}
/* 标题要居中 */
.nav-title {
position: absolute;
text-align: center;
max-width: 400rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
font-size: 36rpx;
color: #2c2b2b;
font-weight: 600;
}
.nav-title-white{
color: #fff;
}
.nav-capsule {
display: flex;
align-items: center;
margin-left: 30rpx;
width: 140rpx;
height: 100%;
}
.back-pre, .back-home {
width: 32rpx;
height: 36rpx;
margin-top: 4rpx;
}
.nav-capsule .back-home {
width: 40rpx;
height: 40rpx;
margin-top: 3rpx;
}
</style>
四、关于小程序授权手机号
uni开发小程序授权手机号,微信和支付宝可以使用同一个方法,直接上代码
<button hover-class="btn-hover" class='phone_re_btn' open-type="getPhoneNumber" @getphonenumber="getPhoneRecharge">点击授权</button>
;; methods方法:
getPhoneRecharge(e){
//需要获取当前选择的权益
if (e.detail.errMsg == 'getPhoneNumber:ok') {
// 同意授权逻辑
} else {
// 拒绝授权
console.log('充值拒绝授权====>', e)
}
}
五、关于小程序授权用户信息
uni开发小程序,授权用户信息,针对微信和支付宝小程序,需要区分开来进行触发。
<button v-if="checkMp === 'wx'" class="index_head_name" hover-class="btn-hover" @click="getUserInfo">微信授权用户信息</button>
<button v-else-if="checkMp === 'ali'" class="index_head_name" hover-class="btn-hover" scope="userInfo" open-type="getAuthorize" @getAuthorize="onGetAuthorize" @error="onAuthError">支付宝授权用户信息</button>
// methods方法:
// 支付宝小程序需要调用按钮方法,里边调用授权方法
// #ifdef MP-ALIPAY
onGetAuthorize(){
this.getUserInfo()
},
onAuthError(err){
console.log('支付宝拒绝授权===>', err)
},
// #endif
getUserInfo(res){
let that = this
if(this.checkMp === 'wx'){
if(wx.canIUse('getUserProfile')){
uni.getUserProfile({
lang: 'zh_CN',
desc:'微信用户信息',
success(res){
console.log('wx userinfo success', res)
if (res.errMsg == 'getUserProfile:ok') {
// 拿到用户信息逻辑
}
},
fail(err){
console.log('wx err', err)
}
})
} else {
// 不出现授权层直接获取
uni.getUserInfo({
lang: 'zh_CN',
desc:'微信用户信息',
success(res){
console.log('wx userinfo success', res)
if (res.errMsg == 'getUserProfile:ok') {
// 拿到用户信息逻辑
}
},
fail(err){
console.log('wx err', err)
}
})
}
} else if(this.checkMp === 'ali') {
uni.getUserInfo({
lang: 'zh_CN',
success(res){
console.log('zfb userinfo success', res)
let userInfoData = JSON.parse(res.response).response;
// 解析两层 response,获取用户信息
// 注意支付宝的用户性别获取的是m、f的字符串,而非0、1值
},
fail(err){
console.log('zfb userinfo err', err)
}
})
}
}
以上就是对uni-app的初体验,整体开发完觉得并没有太大的问题,如果是熟悉vue和小程序原生的小伙伴们开发起来会相当便捷。同时代码一键转换,只需要处理个别平台间的差异即可。完全可以实现一套代码多端使用。