• UniApp 踩坑日记


    前言

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

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

    iOS 底部安全距离

    /*兼容 IOS<11.2*/
    padding-bottom: constant(safe-area-inset-bottom);
    /*兼容 IOS>11.2*/
    padding-bottom: env(safe-area-inset-bottom);
    
    • 1
    • 2
    • 3
    • 4

    App 状态栏距离

    <view class="page">
      <view class="status_bar">view>
    view>
    
    <style scoped>
    .status_bar {
    	height: var(--status-bar-height);
    	width: 100%;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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 {
    				showPopup: false
    			}
    		},
    		onLoad() {
          this.$nextTick(() => {
            this.$refs.popup.open('top')
          })
    		},
    		methods: {
          handleChange(e) {
            this.showPopup = e.show
          }
    		}
    	}
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    App 被第三方调用

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

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

    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', // 小程序路径
          type: 0 //  0-正式版; 1-测试版; 2-体验版。
        })
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    跨组件通信

    // componentA
    // 添加监听关闭 splash 事件
    uni.$on("closeSplashscreen", () => {
      plus.navigator.closeSplashscreen();
    });
    
    // componentB
    uni.$emit("closeSplashscreen");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    页面跳转通信

    // pageA 监听被打开页面的事件
    uni.navigateTo({
      url: 'xxxx',
      events: {
        event: () => {
          console.log('enent')
        }
      }
    })
    
    // pageB 触发事件
    const eventChannel = this.getOpenerEventChannel();
    eventChannel.emit && eventChannel.emit('event');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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, _uid: this.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 {
        height: 200rpx;
        width: 200rpx;
        margin-top: 200rpx;
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 50rpx;
      }
    
      .text-area {
        display: flex;
        justify-content: center;
      }
    
      .title {
        font-size: 36rpx;
        color: #8f8f94;
      }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    隐私合规问题

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

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

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

    {
        "version" : "1",
        "prompt" : "template",
        "title" : "个人信息保护说明",
        "message" : "\t\t\t\t欢迎您使用xxx平台,为帮助您注册使用,浏览推荐,发布投诉,互动交流,我们将手机您的部分必要信息;

    \t\t\t\t我们手机的相关信息,未经您的同意,我们不会从第三方获取、共享或提供您的信息;

    \t\t\t\t本协议有您与平台的经营者共同缔结,本协议具有合同效力。

    \t\t\t\t请查看《用户服务协议》《隐私政策》
    "
    , "buttonAccept" : "同意并接受", "buttonRefuse" : "暂不同意", "styles" : { "borderRadius" : "5px", "buttonAccept" : { "color" : "#2cbe9a" } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 详细的描述引用了什么 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" : "为保证您正常、安全地使用,需要获取设备识别码(部分手机提示为获取手机号码)使用权限,请允许。"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    键盘遮挡样式的问题

    // pages.json 设置键盘的弹出模式
    "app-plus":{
        "softinputMode": "adjustResize"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    打包体积过大

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

    • 小程序分包加载
    
    "subPackages": [{
        "root": "packageA",
        "pages": [{
            "path": "pages/index",
        }]
      }, {
        "root": "packageB",
        "pages": [{
            "path": "pages/index",
        }]
      }
    ]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 静态资源 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    总结

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

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

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

    最后

    感谢你的阅读~

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

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

  • 相关阅读:
    css 优惠券
    计算机网络
    kibana配置文件中明文密码加密
    GoogleTest部署实践--测试用例代码
    Linux:生产消费模型 & 读者写者模型
    docker-elasticsearch集群
    Solidity迁移Flow Cadence指南13-Flow 1000+合约大数据分析
    不同的度量调整效果
    Amazon云计算AWS(四)
    Python学习 day02(注意事项)
  • 原文地址:https://blog.csdn.net/qq_39157944/article/details/127681265