不二博客

V1

2022/11/04阅读:19主题:山吹

UniApp 开发小记

前言

UniApp 可以实现跨平台的打包和编辑,支持小程序和 App,方便开发的同时也带来了一些系列兼容性的问题,曾和 UniApp 官方的开发人员有过沟通,对于兼容性的问题很多时候官方也没有办法去解决,我们只能通过产品的角度去寻求另外的一种解决方式。

不过对于简单的应用,也不用同时兼容太多的平台,UniApp 也是企业和开发者的选择之一,今天这篇文章主要记录了我在使用 UniApp 的过程中遇到了一些问题和解决方案。

iOS 底部安全距离

/*兼容 IOS<11.2*/
padding-bottomconstant(safe-area-inset-bottom);
/*兼容 IOS>11.2*/
padding-bottomenv(safe-area-inset-bottom);

App 状态栏距离

<view class="page">
  <view class="status_bar"></view>
</view>

<style scoped>
.status_bar {
 heightvar(--status-bar-height);
 width100%;
}
</style>

textarea 组件的层级问题

  • 使用原生组件 cover-view 覆盖掉样式
  • 动态渲染原生组件

textarea 属于原生组件,层级高于其他组件,在小程序端处出现层级穿透的问题,对此官方提供了 cover-view 原生组件,不过非常鸡肋。

在这里我们更加推荐使用 动态渲染组件的形式。

<template>
 <view>
  <textarea placeholder="这是一个多行文本域" v-if="showPopup" />
  <uni-popup ref="popup" @change="handleChange">
      这是一个弹出框
    </uni-popup>
 </view>
</template>

<script>
 export default {
  data() {
   return {
    showPopupfalse
   }
  },
  onLoad() {
      this.$nextTick(() => {
        this.$refs.popup.open('top')
      })
  },
  methods: {
      handleChange(e) {
        this.showPopup = e.show
      }
  }
 }
</script>

App 实现热更新

App 热更新是 App 端常用的升级手段,但是因为 iOS 不允许 App 热更新上架应用市场,这里值介绍了 Android 端的使用。

同时需要注意的是如果热更新的代码包需要与 uniapp 云服务打包的版本号相同,否则会出现不兼容的问题。


var dtask = plus.downloader.createDownload(this.upgradeUrl, {}, (d, status) => {
  if(status === 200) {
    plus.runtime.install(d.filename, {}, () => {
      plus.nativeUI.closeWaiting();
      console.log("安装wgt文件成功!"this.isforce);
      // 重启App
      plus.runtime.restart();
    }, (e) => {
      console.log("安装wgt文件失败["+e.code+"]:"+e.message);
      plus.nativeUI.alert("安装wgt文件失败["+e.code+"]:"+e.message);
    });
  } else {
    console.log('网络异常');
    plus.downloader.clear(dtask);
  }
})

// 监听下载进度
dtask.addEventListener('statechanged', (task) => {
  let { totalSize, downloadedSize } = task;
  console.log('下载进度'Math.floor(downloadedSize / totalSize * 100) || 0)
})

App 被第三方调用

打开 manifest.json => App常用其它配置 => UrlSchemes; 设置 App 的 UrlSchemes

  • H5 打开 App
<a href="test://dl">打开App<a>
  • App 监听打开的参数
onLaunch: () => {
  // URLSchemes
  this.checkSchemes();
  // 重点是以下: 一定要监听后台恢复 !一定要
  plus.globalEvent.addEventListener("newintent", () => {
    this.checkSchemes();
  });
}
methods: {
  // APP 检查 URLSchemes
  checkSchemes() {
    let args = plus.runtime.arguments;
    console.log('获取到的参数', args)
  }
}

App 打开小程序

plus.share.getServices(res => {
  var sweixin = null;
  for (var i = 0; i < res.length; i++) {
    var t = res[i];
    if (t.id == 'weixin') {
      sweixin = t;
    }
  }
  if (sweixin) {
    sweixin.launchMiniProgram({
      id'xxxx'// 小程序原始id
      path'xxxxx'// 小程序路径
      type0 //  0-正式版; 1-测试版; 2-体验版。
    })
  }
})

跨组件通信

// componentA
// 添加监听关闭 splash 事件
uni.$on("closeSplashscreen", () => {
  plus.navigator.closeSplashscreen();
});

// componentB
uni.$emit("closeSplashscreen");

页面跳转通信

// pageA 监听被打开页面的事件
uni.navigateTo({
  url'xxxx',
  events: {
    event() => {
      console.log('enent')
    }
  }
})

// pageB 触发事件
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit && eventChannel.emit('event');

input 最大长度问题

在 iOS input 输入文字时的拼音的长度大于最大的长度无法输入的问题,当你遇见这个问题的时候,恭喜你无法解决,你只能通过修改产品方案,不限制用户输入的长度,在提交表单时在进行校验即可。

下拉刷新时数据重复的问题

这个问题是因为我们在对组件进行 for 循环时的 key 值问题,具体原因可能还是 uniapp 编译机制的问题吧,如果你出现了这个问题,建议通过 uid 来作为 for 循环的 key 值。


<template>
  <view>
    <view v-for="item in list" :key="item._uid"></view>
  </view>
</template>

<script>
  export default {
    data(){
      return {
        list: []
      }
    },
    onLoad() {
      // 模拟请求后的数据
      this.list = [a,b,c].map((item) => {
        // 设置uid
        return { ...item, _uidthis.guid() }
      })
    },
    methods: {
      guid(len = 32, firstU = true, radix = null) {
        const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
        const uuid = [];
        radix = radix || chars.length;

        if (len) {
          // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
          for (let i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
        } else {
          let r;
          // rfc4122标准要求返回的uuid中,某些位为固定的字符
          uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
          uuid[14] = "4";

          for (let i = 0; i < 36; i++) {
            if (!uuid[i]) {
              r = 0 | (Math.random() * 16);
              uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
            }
          }
        }
        // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
        if (firstU) {
          uuid.shift();
          return `u${uuid.join("")}`;
        }
        return uuid.join("");
      }
    }
  }
</script>

<style>
  .content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }

  .logo {
    height200rpx;
    width200rpx;
    margin-top200rpx;
    margin-left: auto;
    margin-right: auto;
    margin-bottom50rpx;
  }

  .text-area {
    display: flex;
    justify-content: center;
  }

  .title {
    font-size36rpx;
    color#8f8f94;
  }
</style>

隐私合规问题

如果你的 App 需要上架应用市场,就必须的注重 App 申请权限的问题。对此我们需要注意一下几点:

  • 使用原生隐私政策提示框

    打开项目的 manifest.json 文件,切换到“App 启动界面配置”,在“Android 启动界面样式”中勾选“使用原生隐私政策提示框”

{
    "version" : "1",
    "prompt" : "template",
    "title" : "个人信息保护说明",
    "message" : "<font color='#000000' size='2'>\t\t\t\t欢迎您使用xxx平台,为帮助您注册使用,浏览推荐,发布投诉,互动交流,我们将手机您的部分必要信息;<br/><br/>\t\t\t\t我们手机的相关信息,未经您的同意,我们不会从第三方获取、共享或提供您的信息;<br/><br/>\t\t\t\t本协议有您与平台的经营者共同缔结,本协议具有合同效力。<br/><br/>\t\t\t\t请查看<a href=\"https://xxxx\">《用户服务协议》</a>及<a href=\"https://xxxx\">《隐私政策》</a></font>",
    "buttonAccept" : "同意并接受",
    "buttonRefuse" : "暂不同意",
    "styles" : {
        "borderRadius" : "5px",
        "buttonAccept" : {
            "color" : "#2cbe9a"
        }
    }
}
  • 详细的描述引用了什么 SDK 以及作用和时机

友盟统计(com.umeng)使用场景:统计 APP 下载量;使用目的:用于数据统计分析;涉及个人信息:设备信息(IMEI、ANDROID_ID、DEVICE_ID、IMSI)、应用已安装列表、网络信息;收集规则:APP 前台运行时获取 SIM 序列号、SD 卡数据和设备序列号用于数据统计分析;个人信息处理规则: http://xxxx

  • 关闭 App 在首次启动时申请存储、照片和拨打电话的权限
// 在"app-plus" -> "distribute" -> "android" 节点下添加 permissionExternalStorage 节点
// 禁止App在启动时存储、照片的权限
"permissionExternalStorage": {
    "request""always",
    "prompt""应用保存运行状态等信息,需要获取读写手机存储(系统提示为访问设备上的照片、媒体内容和文件)权限,请允许。"
 }

 // 禁止App在启动时获取拨打电话的权限
 "permissionPhoneState" : {
    "request" : "none",
    "prompt" : "为保证您正常、安全地使用,需要获取设备识别码(部分手机提示为获取手机号码)使用权限,请允许。"
}

键盘遮挡样式的问题

// pages.json 设置键盘的弹出模式
"app-plus":{
    "softinputMode""adjustResize"
}

打包体积过大

在 Uniapp 中 App 云打包有体积的限制,而在小程序中也存在体积大小的限制。

  • 小程序分包加载

"subPackages": [{
    "root""packageA",
    "pages": [{
        "path""pages/index",
    }]
  }, {
    "root""packageB",
    "pages": [{
        "path""pages/index",
    }]
  }
]

  • 静态资源 CDN

占用我们资源包大小的莫过于图片了,我们可以将图片上 CDN 存储减少打包体积大小。

当然在开发过程中为了方便,我们可以在本地建立服务,通过 ip 来访问图片。

<template>
  <image :src="getImageUrl('/public/logo.png')" mode=""></image>
</template>

<script>
  export default {
    methods: {
      getImageUrl(path) {
        return process.env.NODE_ENV === 'production' ? `http://CDN地址 ${path}` : `http://本地服务地址 ${path}`
      }
    }
  }
</script>

总结

如果你决定了使用 UniApp 做为当前项目的框架并且适配多平台,那么需要开始处理让人头疼的兼容性问题。

很多时候我们依赖一些第三方的云插件,云插件的更新或者云插件的 Bug 可能会影响你的 App 打包失败。

很多时候这些兼容性的问题不是必现的,出现的可能有很多的因素,比如 UniApp 更新了版本。

最后

感谢你的阅读~

如果你有任何的疑问欢迎您在后台私信,我们一同探讨学习!

如果觉得这篇文章对你有所帮助,点赞、在看是最大的支持!

分类:

前端

标签:

小程序

作者介绍

不二博客
V1