h5网页嵌套安卓苹果APP内 遇到的各种适配问题与解决方案汇总 --持续更新

iOS时间NaN的问题

iOS上的WebView的问题,iOS的Date不识别 “-”,解决思路就是 在 new Date(sz)之前,将字符串所有的“-”替换为”/"
但也有不少情况是不识别 “/”,所以最好的解决办法还是

  • 后端直接返回可展示得信息前端不处理
  • 需要时间判断的地方后端直接返回结果
安卓、IOS拉起本地APP并跳转特定页
  • 基础原理

  • 微信外唤起:
    用常规方式:通用的Scheme和IOS自己的Universal Links。都支持自定义参数。

    • IOS:Universal Links
      https://www.xxxx.com/xxx公司的Universal Links服务器地址 供IOS系统访问 重定向页面做转发
      https://www.xxxx.com/apple-app-site-association可以下载JSON配置文件,系统会通过规则匹配后,跳转APP
    • 安卓:直接自定义URL Scheme唤起APP,例如alipays://唤起支付宝APP
  • 微信内唤起:

IOS在APP内部网页跳转回到上一页时 为什么上一页没有被销毁而是被暂停 安卓却需要重新加载

在 iOS 和 Android 中,应用内嵌入的网页(通常通过 WKWebView 在 iOS 上和 WebView 在 Android 上实现)的行为有所不同,这主要是由于两个平台对 Web 视图的生命周期管理和内存管理策略不同。

iOS (WKWebView)
在 iOS 中,WKWebView 采用了一种更高效的内存管理机制。当用户从一个页面跳转到另一个页面时,WKWebView 并不会立即销毁前一个页面,而是将其暂停并保留在内存中。这种做法有几个优点:

性能优化:如果用户返回到前一个页面,该页面可以快速恢复,而不需要重新加载,从而提高了用户体验。
资源重用:保留前一个页面的状态和资源可以减少内存的频繁分配和释放,提高系统的整体性能。
状态保持:用户在前一个页面上的所有状态(如表单填写、滚动位置等)都可以被保留,提供了更好的连续性体验。
Android (WebView)
在 Android 中,WebView 的行为略有不同。默认情况下,当用户从一个页面跳转到另一个页面时,前一个页面会被销毁,并且当用户返回时需要重新加载。这是因为:

内存限制:Android 设备可能有不同的内存限制,尤其是在低端设备上。为了防止内存溢出,系统可能会选择销毁不再显示的页面。
进程管理:Android 系统对进程的管理更为严格,可能会根据内存压力和其他因素决定何时销毁或暂停进程。
默认配置:WebView 默认配置下,每个页面都是独立的,没有像 WKWebView 那样的内置缓存机制来保存页面状态。
当 iOS 设备呼出软键盘后,页面会自动滚动以确保输入框可见,导致浏览器可以上下拉动 导致页面效果不一致
window.addEventListener('keyboardWillShow',function(e){e.preventDefault();}); 
window.addEventListener('keyboardWillHide',function(e){e.preventDefault();}); 
IOS设备隐藏滚动条
    
::-webkit-scrollbar {
    width: 0px; /*滚动条宽度*/
    height: 0px; /*滚动条高度*/
    display: none;
}

-webkit-overflow-scrolling: touch;//这是 iOS 设备上模拟原生滚动效果的一个属性。添加这个属性可以让滚动条完全隐藏:


document.body.style.overflow = 'hidden'; // 隐藏滚动条  或指定元素
软键盘弹出时,在Android系统下,H5所在容器(即WebView)高度会被压缩
onMounted(async () => {    
    window.addEventListener('resize', resizeReview);   
}

function resizeReview() {//动态监听屏幕变化来改变元素尺寸  系数根据需求自己算这里约为17
    let vhValue = window.innerHeight/17
    document.getElementById('reviewDom').style.height=`${vhValue}vh`
}

禁止 iOS 浏览器中的连点放大
  • 你可以通过设置 meta 标签来禁用双击缩放:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  • 还可以给按钮添加逻辑
document.querySelector('button').addEventListener('click', function(event) {
  event.preventDefault();
  // 其他逻辑
});
  • 或是在index.html中直接禁止相关操作
  /*禁止ios缩放,双击和双指*/
        window.onload = function() {
            document.addEventListener("touchstart", function(event) {
                if (event.touches.length > 1) {
                    event.preventDefault();
                }
            });
            var lastTouchEnd = 0;
            document.addEventListener(
                "touchend",
                function(event) {
                    var now = new Date().getTime();
                    if (now - lastTouchEnd <= 300) {
                        event.preventDefault();
                    }
                    lastTouchEnd = now;
                },
                false
            );
            document.addEventListener("gesturestart", function(event) {
                event.preventDefault();
            });
        };
IOS和安卓客户端加载H5流程:
原生页面触发事件后 准备webview容器与对应的跳转链接 再进行加载。
IOS和安卓客户端切换页面(例如h5支付场景):
IOS的webview内部跳转时会暂停上一个页面的状态  甚至是loading动画。
	返回后可以顺延播放非常丝滑。  
安卓反而会销毁页面并跳转,类似PC浏览器同一页签下的跳转。
	返回后会重新加载页面。
IOS和安卓与H5前端的通信的主要方式
1.链接传参
2.userAgent通用参数 userAgent.navigator //一个常见浏览器属性,包含当前浏览器的各种信息。格式并没有标准。不建议过度依赖userAgent,因为可能被篡改伪造。
3.window方法调用
      window.JSInterface.putToken();//和客户端协商的方法名putToken  安卓
      window.webkit.messageHandlers.putToken.postMessage({});//IOS

4.Bridge传参
-------------------------------------------------------------组件内调用  安卓IOS通用
  	  import Bridge from '@/utils/jsBridge.js'
  
      Bridge.callhandler("imgClick_ext", {//和客户端协商的方法名imgClick_ext
        index: index,
        fileArr: arr,
      }, responseData => {
        let res = JSON.parse(responseData);
        console.log(res, 'res');
      });
-------------------------------------------------------------jsBridge.js
function setupWebViewJavascriptBridge(callback) {
    console.log("callback", callback)
    if (window) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
        document.documentElement.appendChild(WVJBIframe);
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }

}

//初始化
setupWebViewJavascriptBridge(function (bridge) {
    console.log("setupWebViewJavascriptBridge")
    try {
        bridge.init(function (message, callback) {
            callback(null);
        })
    } catch (e) { }
});

export default {
    //js调APP方法 (参数分别为:app提供的方法名  传给app的数据  回调)
    callhandler: function (method, params, callback) {
      //  console.log("callhandler", method)
        setupWebViewJavascriptBridge(function (bridge) {
            bridge.callHandler(method, params, callback)
        })
    },

    // APP调js方法 (参数分别为:js提供的方法名  回调)
    registerHandler(method, callback) {
      //  console.log("callhandler", method)
        setupWebViewJavascriptBridge((bridge) => {
            bridge.registerHandler(method, (data, responseCallback) => {
                callback(data, responseCallback)
            })
        })
    }
}

安卓IOS的webView或H5浏览器拿到的像素宽高,比手机屏幕高度差很多?
1.设备像素比(Device Pixel Ratio, DPR)不匹配:
	手机屏幕的 DPR 通常大于 1,比如 23。这意味着物理像素与 CSS 像素并不相等。
	H5 应用通过 JavaScript 获取的高度是基于 CSS 像素的,而不是物理像素。所以获取的高度会小于实际屏幕高度。
2.浏览器视口(Viewport)设置不当:
	H5 应用需要通过 <meta name="viewport" ...> 标签正确设置视口参数,如宽度、缩放比例等。
	如果视口设置有问题,就会导致 H5 应用获取的高度与实际屏幕高度不符。
3.滚动条高度未考虑:
	有时浏览器会根据内容显示垂直滚动条,这部分高度需要单独计算。
	如果 H5 应用没有正确处理滚动条高度,获取的高度就会小于屏幕实际高度。
4.浏览器差异:
	不同的浏览器在获取高度方面可能会有一些差异,需要做兼容性处理。
怎么修改h5软键盘的文字,将右下角的回车换行更改为前往或发送?

文档中很多都是修改为前往,搜索,提交等。修改的属性,其实还是有另外一个属性的:enterkeyhint

<input enterkeyhint="enter">
<input enterkeyhint="done">
<input enterkeyhint="go">
<input enterkeyhint="next">
<input enterkeyhint="previous">
<input enterkeyhint="search">
<input enterkeyhint="send">
输入发送完成收回软键盘
            const field = document.getElementById('live-field');//获取input控件DOM
            field.blur(); // 收回软键盘  失去焦点
图片自适应

宽度100%,高度自适应

  background-image: url(../assets/image.png);
  background-repeat: no-repeat;
  background-size: 100% auto;

微信开启了大字体模式,可能会导致页面布局失效。

  • 这是因为微信会在 HTML 页面的根元素上添加一个 wx-wx-root 类名,并且会设置一些特殊的样式。

0. 微信官方文档


<script>
// 微信网页字体大小自定义适配 Demo
document.addEventListener("WeixinJSBridgeReady", function (params) {
    // 1-1.设置禁止 Android 微信内网页字体大小默认缩放
    WeixinJSBridge.invoke("setFontSizeCallback", {
        fontSize: '2' // 默认档位 2
    });
    // 2.监听网页字体大小的事件,页面加载完成或用户手动修改字号会触发该事件
    WeixinJSBridge.on('menu:setfont', function (e) { // e.fontSize 档位  e.fontScale 放大比例
        // 3-1.业务逻辑
        // rem方式,根据档位或放大比例设置根字号大小
        // 非rem方式,根据档位或放大比例设置根节点命名空间className(如:wechat_fontsize_6)
    });
}, false);
</script>

<style>
/* 1-2.设置禁止 iOS 微信内网页字体大小默认缩放 */
body {
    -webkit-text-size-adjust: 100% !important;
    text-size-adjust: 100% !important;
}
/* 3-2.业务逻辑 */
.wechat_fontsize_6 .title { }
</style>

或是自己判断再微信内 直接将全局字体大小写死

    var u = navigator.userAgent;
    var isWeixin = u.toLowerCase().indexOf("micromessenger") !== -1; // 微信内

1. 可以使用 CSS 媒体查询 检测是否在大字体模式下, 并针对这种情况进行特殊的样式调整:

/* 检测大字体模式 */
@media (max-width: 375px) and (-webkit-min-device-pixel-ratio: 3) {
  /* 在大字体模式下的样式调整 */
  body {
    font-size: 16px !important; /* 重置字体大小 */
  }
  /* 其他需要调整的样式 */
}

2. 使用 JavaScript 动态修改 在页面加载时检测是否在大字体模式下, 并动态修改 DOM 元素:

// 检测是否在大字体模式
function isWeixinBigFont() {
  return document.documentElement.classList.contains('wx-wx-root');
}

// 在页面加载时进行样式调整
window.addEventListener('load', function() {
  if (isWeixinBigFont()) {
    // 在大字体模式下进行样式调整
    document.body.style.fontSize = '16px';
    // 其他需要调整的样式
  }
});

3. 使用第三方库进行适配 您可以使用一些第三方库,如 vant, cube-ui 等,它们提供了针对微信大字体模式的适配方案。这些库会自动检测当前环境,并提供相应的样式调整。

reolaceAll低版本浏览器不兼容问题

如果 replaceAll 在某些浏览器中不兼容,可以使用正则表达式配合 replace 方法来实现相同的功能:

const input = "some text to replace";
const output = input.replace(/text/g, "word");
console.log(output);

IOS的PT 安卓的DP

PT

  1. pt 在 iOS 开发中就是代表分辨率的最小单位。和 px 一样,也是一种「相对长度」。
  2. pt 这个单位的缘由是苹果的 Retina 屏的分辨率较高,为了更好的定义和展现自己的产品,苹果就自己定义了一个设计单位 pt,用来给 iOS 系统的设计师做基础设计,并可以根据公式变换成 px 以适应不同 PPI 的手机。
  3. pt 与 px 的转换方式是:pt = ppi / 163 px 。ppi 不能被 163 整除时,可以进行四舍五入。比如如果 ppi 是 350,那么 pt = 350 / 163 = 2.14px,就是说 1pt 约等于 2px。
  4. 以上公式中 163 这个数字的来源是:iPhone3 的 3.5 英寸屏幕的分辨率是 320*480px,它的 ppi 按照我们上文提到过的 ppi 计算公式,等于 163。苹果规定:ppi = 163 时,1pt = 1px。

DP

  1. dp 全称 Density-independent pixel,可以理解为是一种独立于 px 之外的设计单位,是 Andriod 系统用来给设计师做基础设计使用的,也可以根据公式变换成 px。
  2. 同 pt 一样,1dp 在任何设备上的大小都应该是一样的,但是代表几个 px 却是不固定的,以此来应对不同 ppi 的设备。
  3. dp 和 px 的转换方式是:dp = ppi / 160 px。原理同上文,但分母取了整数,被规定为 160,结果同样可四舍五入。
H5项目的Rem适配

前端还原设计稿很重要,目前最泛用的还是rem。
postcss-pxtorem 能够直接按照设计图的尺寸开发,并且能自动编译转换成rem
npm install postcss-pxtorem -D

autoprefixer 能够自动为你的CSS代码添加必要的浏览器前缀,以确保在各个现代和旧版浏览器中的兼容性。通过这个项目,开发者可以节省大量手动添加前缀的时间,从而更加专注于代码逻辑和设计。
npm install postcss-pxtorem -D

vite.config.js

export default defineConfig({
  ...
  css: {
    postcss: {
      plugins: [
        autoprefixer({
          overrideBrowserslist: [
            "Android 4.1",
            "iOS 7.1",
            "Chrome > 31",
            "ff > 31",
            "ie >= 8",
          ],
        }),
        postCssPxToRem({
          // 自适应,px>rem转换
          rootValue: 37.5, // 75表示750设计稿,37.5表示375设计稿
          propList: ["*"], // 需要转换的属性,这里选择全部都进行转换
          selectorBlackList: ["norem"], // 过滤掉norem-开头的class,不进行rem转换
        }),
      ],
    },
  },
  ...
});

H5网页嵌在APP内部 手机锁屏后再打开 setInterval会重复执行

在H5网页嵌入到APP内部时,如果手机锁屏后再打开,可能会遇到setInterval重复执行的问题。这是因为当应用进入后台或屏幕锁定时,浏览器的定时器可能会暂停或重置,导致重新激活时定时器被重新启动。

问题原因
系统休眠:当手机锁屏时,操作系统可能会将应用置于休眠状态,暂停所有活动,包括JavaScript的定时器。
WebView恢复:当用户解锁手机并返回应用时,WebView可能需要重新加载或恢复页面,这会导致setInterval函数重新执行。
解决方法

  1. 使用 visibilitychange 事件
    你可以监听 visibilitychange 事件来检测页面是否可见,并根据页面的可见性来控制 setInterval 的行为。
let intervalId;

function startInterval() {
    if (intervalId) {
        clearInterval(intervalId);
    }
    intervalId = setInterval(() => {
        // 你的定时任务代码
        console.log('定时任务执行');
    }, 1000);
}

function stopInterval() {
    if (intervalId) {
        clearInterval(intervalId);
        intervalId = null;
    }
}

// 监听页面可见性变化
document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'visible') {
        startInterval();
    } else {
        stopInterval();
    }
});

// 页面初始加载时启动定时器
if (document.visibilityState === 'visible') {
    startInterval();
}
  1. 使用 pagehide 和 pageshow 事件
    你也可以使用 pagehide 和 pageshow 事件来处理页面隐藏和显示的情况。
let intervalId;

function startInterval() {
    if (intervalId) {
        clearInterval(intervalId);
    }
    intervalId = setInterval(() => {
        // 你的定时任务代码
        console.log('定时任务执行');
    }, 1000);
}

function stopInterval() {
    if (intervalId) {
        clearInterval(intervalId);
        intervalId = null;
    }
}

// 监听页面隐藏和显示
window.addEventListener('pagehide', stopInterval);
window.addEventListener('pageshow', startInterval);

// 页面初始加载时启动定时器
startInterval();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

跳动的世界线

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值