返回

移动端浏览器 IFrame/JavaScript 不工作?问题排查与解决方案

javascript

移动端浏览器 IFrame/JavaScript 不工作?看这里!

最近遇到一个头疼的问题:一段用于获取用户地理位置并通过 AWS Lambda 记录到 DynamoDB 的 JavaScript 代码,在桌面浏览器(Chrome、Edge)上跑得好好的,但在手机浏览器(Android 和 iOS)上却完全没反应,只显示一个空白页。

之前只用了一个 modal 相关的代码,在手机和电脑上都能正常显示。

我一度怀疑是不是 addEventListener 用了两次导致了问题。但排查了好久都没找到症结所在,让人很是抓狂。

下面咱们就来好好分析一下这个问题,并给出几种靠谱的解决方法。

一、问题原因分析

导致 JavaScript 在移动端浏览器不工作的原因有很多,根据你提供的代码和,我们重点关注以下几个可能性:

  1. 兼容性问题: 你用到的某些 JavaScript API 或特性在部分移动端浏览器上可能不支持,或者存在差异。 比如ipapi.co服务不稳定, 还有某些ES6特性。

  2. 代码错误: 虽然在桌面浏览器上没问题,但细微的语法错误或者逻辑漏洞,在移动端浏览器严格的解析机制下,可能就暴露出来了。例如,未捕获的异常可能导致整个脚本停止执行。

  3. 网络请求问题: 代码中涉及到多个网络请求(ipapi.co、api-bdc.net、AWS Lambda),移动端网络环境不稳定,或者存在跨域问题时,可能导致请求失败,进而影响到代码的执行。

  4. 框架/库冲突: bootstrap.Modal 可能会和你的逻辑有冲突,尤其是在移动端这种资源受限的环境中。

  5. S3 静态网站配置问题: 虽然可能性较低,但也有必要检查一下 AWS S3 静态网站的配置,确认是否正确设置了相关的权限和 CORS 规则。

  6. 事件监听冲突: 代码中使用了两个 DOMContentLoaded 事件监听器。尽管这通常不会直接导致页面空白,但有可能引发其他难以预料的问题, 特别是在移动浏览器环境中。

二、解决方案

针对上述可能的原因,我们可以采取以下措施来解决问题:

  1. 调试与错误排查:

    • 远程调试: 这是最有效的排查手段。通过将手机连接到电脑,使用 Chrome DevTools(或其他浏览器的类似工具)进行远程调试,可以实时查看移动端浏览器的控制台输出、网络请求、元素等,帮助你快速定位错误。

      • Android 设备: 启用开发者选项和 USB 调试,用 USB 线连接到电脑,然后在 Chrome 地址栏输入 chrome://inspect,即可看到你的设备。
      • iOS 设备: 需要使用 Safari 浏览器,并开启 Web 开发者工具。具体步骤可以参考苹果官方文档。
    • 添加 alert()console.log() 在关键位置添加 alert()console.log() 来输出变量值或状态,虽然比较原始,但在某些情况下可以帮助你判断代码执行到了哪里,哪些数据出了问题。
      例如, 在fetch之前和成功和catch里面加上打印信息。

  2. 简化代码,排除干扰:

    • 暂时移除 bootstrap.Modal 先把弹窗相关的代码注释掉,看是否能正常获取和显示地理位置信息。排除弹窗本身的逻辑问题,或者它对其他代码的影响。

    • 合并addEventListener 将两个DOMContentLoaded 事件合并成一个,可以提高代码可读性,也有助于减少可能的冲突,简化代码如下:

window.addEventListener('DOMContentLoaded',function () {
    const myModal = new bootstrap.Modal('#load-modal');
    myModal.show();

    // Fetch latitude and longitude based on IP address
    fetch("https://ipapi.co/json")
        .then(response => response.json())
        .then(data => {
            console.log(data.latitude)
            console.log(data.longitude)
            const bdcAPI = `https://api-bdc.net/data/reverse-geocode-client?latitude=${data.latitude}&longitude=${data.longitude}`
        getAPI(bdcAPI)
        })
        .catch(error => {
            console.error("Error fetching IP address:", error);
        });
});

function getAPI(bdcAPI){
    fetch(bdcAPI)
    .then(response => response.json())
    .then(data => {
        console.log(data.countryName)
        console.log(data.city)

        functionURL(data.continentCode,data.continent,data.countryCode,
            data.countryName,data.principalSubdivisionCode,data.principalSubdivision,data.city,data.locality)
    })
    .catch(error => {
        console.error("Error fetching country and city name", error);
    });
}

function functionURL(continentCode,continent,countryCode, countryName,
                    principalSubdivisionCode,principalSubdivision,city,locality){

    const functionurl = `aws lambda function url`

    console.log(functionurl)

    fetch(functionurl)
    .then(response => response.json())
    .then(data => {
        console.log('Location logged successfully!!')
    })
    .catch(error => {
        console.error("Error calling function url:", error);
    });
}

  1. 网络请求优化与处理:

    • 检查跨域设置: 如果你的 S3 静态网站和 AWS Lambda 函数不在同一个域名下,需要确保 Lambda 函数正确配置了 CORS 响应头,允许来自 S3 网站的跨域请求。可以在AWS Lambda 函数控制台设置HTTP API, 或者直接修改代码的返回头部。

      # 示例 (Python Lambda 函数):
      def lambda_handler(event, context):
          # ... 你的处理逻辑 ...
          return {
              'statusCode': 200,
              'headers': {
                  'Access-Control-Allow-Origin': '*', # 允许所有来源,生产环境建议更精细的控制
                  'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
                  'Access-Control-Allow-Headers': 'Content-Type'
              },
              'body': json.dumps({'message': '成功!'})
          }
      
      
    • 添加请求超时处理:fetch 请求设置超时时间,避免因网络延迟过长导致用户等待太久。

     // 超时辅助函数
        function timeoutPromise(ms, promise) {
        return new Promise((resolve, reject) => {
                const timeoutId = setTimeout(() => {
                    reject(new Error("请求超时"))
                }, ms);
                promise.then(
                    (res) => {
                        clearTimeout(timeoutId);
                        resolve(res);
                    },
                    (err) => {
                        clearTimeout(timeoutId);
                        reject(err);
                    }
                );
            })
        }
    
       // 使用 timeoutPromise 包装 fetch 请求 (设置5秒超时)
        timeoutPromise(5000, fetch("https://ipapi.co/json"))
        .then(response => response.json())
        .then(data => {
          // ... 你的代码逻辑 ...
        })
        .catch(error => {
          //处理请求超时或者正常网络错误.
            console.error( error);
        });
    
    
    • 检查 URL 的正确性: 确保你在代码中拼接的 URL(bdcAPI 和 AWS Lambda 的 URL)是正确的,没有多余的空格、换行等字符。

    • 考虑备用方案: 如果 ipapi.co 服务不稳定,可以考虑换用其他免费的 IP 地理位置服务,或者自己在后端实现一个简单的 IP 查询功能。

  2. 逐步测试与验证:
    每一次修改之后,都要记得进行测试, 要在电脑和手机浏览器上分别进行测试,确保代码在两个环境下都能正常工作。

三、进阶使用技巧

针对稳定性, 可以使用polyfill 优化老旧浏览器,使用try...catch提高代码鲁棒性:

  1. 使用 Polyfill

    • 如果你的目标用户可能使用较旧的浏览器,考虑引入 Polyfill 来弥补 JavaScript API 的兼容性问题。例如,使用 whatwg-fetch 来代替原生的 fetch

      npm install whatwg-fetch --save
      

      在你的代码顶部引入:

      import 'whatwg-fetch'; // 或 <script src="path/to/whatwg-fetch.js"></script>
      
    • 使用Babel把ES6代码转化成更兼容的ES5.

  2. 使用 try...catch

       try {
           fetch("https://ipapi.co/json")
           .then(response => {
                   if (!response.ok) { // 检查HTTP 状态.
                     throw new Error(`HTTP error! status: ${response.status}`);
                   }
                  return  response.json();
           })
             .then(data => {
                    console.log(data); //打印整个返回的json对象,方便观察。
                   //.. 你的逻辑 ...
             })
           .catch(error => {
                console.error("Error fetching IP address:", error);
            });
       }catch (error) {
            console.error('代码块发生错误:', error);
       }
    
    

通过仔细排查和逐步优化,相信你一定能解决这个问题。记住,调试是开发中不可避免的一环,耐心和细致是解决问题的关键!