H5(vue)与APP混合开发遇到的问题

897 阅读6分钟

菜鸡前端的混合开发之旅

最近做的项目是混合开发的项目,遇到了很多坑,都填起来后分享一下

1.ios图片设置了固定宽高无法显示

解决方法:给图片外层套一层div,给div设置宽高,然后图片设置宽高100% 或者引入svg(小图标,网上很多方法)

<div style="width:10px;height:10px;">
    <img src="xxx" style="width:100%;height:100%;">
</div>

2.ios时间显示NaN

原因大概是ios时间中间如果不是/,放入new data会变NAN

解决办法: (因为我们数据库存的都是-的格式,所以只能我自己改) utils:

export function format(date, pattern) {
    if (!date) {
        return '';
    }
    if (typeof (date) === 'string') {
        let s = '';
        if (/\./.test(date)) {
            let arr = date.split(' ');
            s = [arr[0], arr[1].split('.')[0]].join(' ');
        } else {
            s = date;
        }
        s = s.replace(/-|\./g, '/');
        date = new Date(s);
    } else if (typeof (date) === 'number') {
        date = new Date(date);
    }
    pattern = pattern || DEFAULT_PATTERN
    return pattern.replace(SIGN_REGEXP, function ($0) {
        switch ($0.charAt(0)) {
            case 'y':
                return padding(date.getFullYear(), $0.length)
            case 'M':
                return padding(date.getMonth() + 1, $0.length)
            case 'd':
                return padding(date.getDate(), $0.length)
            case 'w':
                return date.getDay() + 1
            case 'h':
                return padding(date.getHours(), $0.length)
            case 'm':
                return padding(date.getMinutes(), $0.length)
            case 's':
                return padding(date.getSeconds(), $0.length)
        }
    })
}

然后在需要用到地方引入, 使用方法:

format(时间, 'yyyy-MM-dd hh:mm:ss') 

ps:第二个参数是想要的格式

3.调用原生的方法

混合app肯定会涉及各种互相调用 解决办法:

export function x(id) { //自己用的方法名,参数
    let u = navigator.userAgent
    let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端
    
    //非app内没有方法就不会调用,不然一直报错很烦
    if (isAndroid && window.AndroidJs) { 
    
    //特别注意安卓必须得JSON.stringify才能才能传
        window.AndroidJs.xxxxx(JSON.stringify({//xxxxx调原生的方法名
            Id: id
        }))
    } else {
        if (window.webkit) {
            window.webkit.xx.xxxxx.postMessage({
                Id: id
            })
        }
    }
}

4.ios安卓调用h5方法

因为vue所有方法都是在组件内部调用,所以要暴露在window上才能给原生调用到

methods: {
    内部的方法(){
        xxxxx
    }
}

mounted () {
    window['暴露的方法名'] = () => {
      this.内部的方法()
    }
}

5.计算精度问题

这个可能不止混合开发会遇到,最近解除的和数字有关的东西多了,还要自己计算, 然后就遇到了很多精度问题 解决方法: utils:

// 加法: accAdd(0.1, 0.2)  // 得到结果:0.3
export function accAdd(num1, num2) {
    num1 = Number(num1);
    num2 = Number(num2);
    var dec1, dec2, times;
    try {
        dec1 = countDecimals(num1) + 1;
    } catch (e) {
        dec1 = 0;
    }
    try {
        dec2 = countDecimals(num2) + 1;
    } catch (e) {
        dec2 = 0;
    }
    times = Math.pow(10, Math.max(dec1, dec2));
    // var result = (num1 * times + num2 * times) / times;
    var result = (accMul(num1, times) + accMul(num2, times)) / times;
    return getCorrectResult("add", num1, num2, result);
    // return result;
}
// 减法: accSub(1, 0.9)    // 得到结果:0.1
export function accSub(num1, num2) {
    num1 = Number(num1);
    num2 = Number(num2);
    var dec1, dec2, times;
    try {
        dec1 = countDecimals(num1) + 1;
    } catch (e) {
        dec1 = 0;
    }
    try {
        dec2 = countDecimals(num2) + 1;
    } catch (e) {
        dec2 = 0;
    }
    times = Math.pow(10, Math.max(dec1, dec2));
    // var result = Number(((num1 * times - num2 * times) / times);
    var result = Number((accMul(num1, times) - accMul(num2, times)) / times);
    return getCorrectResult("sub", num1, num2, result);
    // return result;
}
// 除法: accDiv(2.2, 100)  // 得到结果:0.022
export function accDiv(num1, num2) {
    num1 = Number(num1);
    num2 = Number(num2);
    var t1 = 0,
        t2 = 0,
        dec1, dec2;
    try {
        t1 = countDecimals(num1);
    } catch (e) {}
    try {
        t2 = countDecimals(num2);
    } catch (e) {}
    dec1 = convertToInt(num1);
    dec2 = convertToInt(num2);
    var result = accMul((dec1 / dec2), Math.pow(10, t2 - t1));
    return getCorrectResult("div", num1, num2, result);
    // return result;
}
// 乘法: accMul(7, 0.8)    // 得到结果:5.6
export function accMul(num1, num2) {
    num1 = Number(num1);
    num2 = Number(num2);
    var times = 0,
        s1 = num1.toString(),
        s2 = num2.toString();
    try {
        times += countDecimals(s1);
    } catch (e) {}
    try {
        times += countDecimals(s2);
    } catch (e) {}
    var result = convertToInt(s1) * convertToInt(s2) / Math.pow(10, times);
    return getCorrectResult("mul", num1, num2, result);
    // return result;
}

6.全局loading多次请求会多次loading闪烁

菜鸡前端的loading,用的全局,虽然应该有更好的办法, 勉强解决部分:

(没考虑请求.then后再调用一个请求的情况)

Axiossetting里

let loadingall=0; //loading总数
然后每次请求loadingall++,
请求结束的时候loadingall--,
If(loadingall<0){ //防止异常情况
    loadingall=0
},

然后设置一个0秒的定时器, 当loadingall===0时关闭loading菊花

考虑一部分的话定时器设置一个300毫秒,但是用户体验会差很多,这部分还没有特别完美的解决方法

7.百度地图定位问题

解决办法:

var geolocation = new BMap.Geolocation();
                let that=this //function会改变this指向
                geolocation.getCurrentPosition(function (r) {
                    // console.log(this.getStatus())
                    if(this.getStatus() == BMAP_STATUS_SUCCESS){
                        // alert(JSON.stringify(r))
                        // getCurrentPosition 但是用户拒绝或者允许获取地理位置,this.getStatus()都是0;
                        //解决办法:当用户拒绝该网站使用浏览器位置时,此时其精度则为null,通过此值判断用户是否拒绝网站获取浏览器位置信息

                        if(r.accuracy==null){
                            // 精度失败xxxxx
                        }else{
                            // 成功xxxxxx
                        }
                    }
                    else {
                        //定位失败xxxxx
                    }
                })

但是安卓部分机型一直无法获取准确精度(不管开不开启定位),

所以我们安卓是用的原生的定位方法,然后数据传给我

8.清除app的h5缓存的刷新页面,但是安卓部分机型无效

解决办法:

window.location.href = '地址?time=' + ((new Date()).getTime());

9.用户手机手动调整字体大小,h5和app样式均出错

解决办法: text-size-adjust无效,要原生端控制禁止

10.安卓键盘不会自动顶起input

我试了下网上的resize也没用(大概安卓是全屏的webview的关系?)。。没办法只能自己边找边写写了一个,大概是没问题的(自己用没问题) 解决办法: 根据input在页面的大概位子让页面的top改变,最底下上移1/2,在屏幕上半部分的input点击页面不上移,应该还有一些优化空间

utils:

export function inputup(rootelement, rootelement2) {
 
    let u = navigator.userAgent
    let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端
    
    if (isAndroid) { // android统一处理,不影响ios的自身处理

        let body = document.getElementsByTagName('body')[0] // 兼容获取body
        let regDom = document.querySelector(rootelement) // 获取页面内层元素
        let regDom2 = document.querySelector(rootelement2) // 获取页面外层根元素

        let clientHeight = body.clientHeight + 41 //可见高
        let fixHeight3 = clientHeight / 8   // 定位高,弹出键盘时input距浏览器上部的距离,自己定义合适的
        // 符合需弹出键盘的元素query
        let queryStr = 'input[type=text],input[type=tel],input[type=number],input[type=password], textarea'
        let inputs = document.querySelectorAll(queryStr)
        for (let i = 0; i < inputs.length; i++) {
            let item = inputs[i]
            item.addEventListener('focus', () => {
                // 改变top上移页面
                let offsetTopArr = Array.prototype.map.call(inputs, item => {
                    return getElementOffsetTop(item) // offsetTop只能获取到顶部距它的offsetParent的距离,需此方法获取到元素距顶部的距离
                })
                if ((offsetTopArr[i] - regDom2.scrollTop) > (7 * fixHeight3)) { //元素距离页面顶部的距离-浏览器的滚动条距离页面顶部距离>屏幕的一半
                    // regDom.style.top = '-' + (offsetTopArr[i] - fixHeight) + 'px'

                    regDom.style.top = '-' + 4 * fixHeight3 + 'px'
                } else if ((offsetTopArr[i] - regDom2.scrollTop) > (6 * fixHeight3)) {
                    regDom.style.top = '-' + 3 * fixHeight3 + 'px'

                } else if ((offsetTopArr[i] - regDom2.scrollTop) > (5 * fixHeight3)) {
                    regDom.style.top = '-' + 2 * fixHeight3 + 'px'

                } else if ((offsetTopArr[i] - regDom2.scrollTop) > (4 * fixHeight3)) {
                    regDom.style.top = '-' + fixHeight3 + 'px'

                } else if ((offsetTopArr[i] - regDom2.scrollTop) > (3 * fixHeight3)) {
                    regDom.style.top = '-' + fixHeight3 / 2 + 'px'

                }

            })
            item.addEventListener('blur', () => {
                // 恢复top
                if (regDom.style.top !== 0) {
                    regDom.style.top = 0
                }
            })
        }
//这里是安卓写给我的一个方法,因为我监听不到用户点击键盘的下,这是用户点击键盘的下时安卓端调我的方法
        window.inputdown = function inputdown() {
            if (regDom.style.top !== 0) {
                regDom.style.top = 0
            }
            for (let i = 0; i < inputs.length; i++) {
                let item = inputs[i]
                item.blur()
            }
        }
    } else {
        let queryStr = 'input[type=text],input[type=tel],input[type=number],input[type=password], textarea'
        let inputs = document.querySelectorAll(queryStr)
        inputs.forEach((item) => { //循环给input添加监听
            item.addEventListener('blur', () => {
                setTimeout(function () {
                    var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0;
                    window.scrollTo(0, Math.max(scrollHeight - 1, 0));
                }, 100)


            })
        })
    }
}

function getElementOffsetTop(el) {
    let top = el.offsetTop
    let cur = el.offsetParent
    while (cur != null) {
        top += cur.offsetTop
        cur = cur.offsetParent
    }
    return top
}

页面结构大概是这样的:

复制一份移除监听的,2个方法都引入

<template>
 <div class="rootelement" style="position:absolute">//很重要
   	<div  class="rootelement2">//input所在的里面
                xxxxx
        </div>
</div>
</template>
  mounted()
      inputup('.rootelement2','.rootelement')

  },
  beforeDestroy(){ //不要忘了复制一份移除监听
    inputup2('.rootelement2','.rootelement')
  },