H5页面前端开发注意指南

567 阅读6分钟

移动端的页面开发上一般容易遇到的问题主要有三块:

  1. app应用级问题(如页面嵌入app)。
  2. 操作系统及客户端适配上问题(ios、安卓、华为、小米等)。
  3. 知名第三方sdk/库接入问题。

接下来我会把遇到的问题按照以上分类进行分类讲述。

1. 操作系统

1. 解决移动端浏览器 vh 单位异常问题

当你在使用height: 100vh想做一个满屏的元素时,会发现错位。 原因是移动端下浏览器对 100vh 的定义不考虑 URL 栏的高度(无论 URL 栏显示还是隐藏),可以用下面这张图直观地体现问题:

左侧是我们期望的100vh“全屏”效果,但右侧是URL栏显示状态下的“全屏”效果,100vh此时已经超出了“全屏”高度。

解决方案:使用vh-check第三方库帮我们计算出ios浏览器中URL栏的高度,计算全屏高度时减去这部分的高度。

npm install vh-check

main.js中:

import VhCheck from 'vh-check'
vhCheck()

*.css中:

main {
height: 100vh;
/** 兼容不支持var变量的浏览器(<=IE 11) */
height: calc(100vh - var(--vh-offset, 0px));
/*修正后的 100vh */
}

2. 在ios中z-index无效的问题,bug现象:vant-ui中的action-sheet弹窗会被覆盖

原因 ios系统下当设置-webkit-overflow-scrolling: touch 属性的元素发生弹性滚动时会导致 z-index 失效

解决方法:

  1. 修改-webkit-overflow-scrolling 属性值为 auto;(ios滑动卡顿,不推荐)

  2. 将popup与底部导航元素放到同级,或者放在body下;(无法封装组件维护不友好)

  3. 在封装组件内部的popup上添加  get-container="body"  属性 (推荐)

  4. 每个action-sheet设置不同高度,保证可以关闭(看业务场景)

<van-popup
      v-model="showPicker"
      :close-on-click-overlay="false"
      position="bottom"
      get-container="body"
    >
      <van-datetime-picker
        v-model="selectedValue"
        type="time"
        :min-hour="6"
        @confirm="onConfirm"
        @cancel="onCancel"
        cancel-button-text="清空"
      />
    </van-popup>

3. 在页面中下载文档的方法

1.如果后端直接提供文件的下载地址可以使用

window.open('文件地址')

或者

 const token = getCookie('satoken')
const downloadlink = document.createElement('a')
downloadlink.href = '下载地址'
downloadlink.download = '文件名.docx'
downloadlink.style.display = 'none'
downloadlink.click()
downloadlink.remove()

2.如果后端提供的是post请求,返回的是二进制流,可以使用

const data = await request(params);

let blob = new Blob([data], { type: "application/zip" });
let url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `下载文件.zip`;
link.click();
URL.revokeObjectURL(url);

注意:该方法需要设置axios的config属性,reponseType:blob

以上方法在手机中都可以下载文档,注意的是window.open在ios safari中会打开新的页面后再下载,有丢失缓存数据的风险,例如拿不到vuex的数据,可以通过以下方法进行规避

    const downloadlink = document.createElement('a')
    downloadlink.href = `${下载地址}`
    downloadlink.download = `${文件名}`
    downloadlink.style.display = 'none'
    downloadlink.click()
    downloadlink.remove()

其中如果在微信app/浏览器中,由于微信的安全问题禁止了文档和软件的下载,无法通过创建a标签进行下载,可以采用使用clipboard库拷贝下载链接,让用户的使用手机中其他浏览器app下载的方案

    const clipboard = new Clipboard('.id', {
      text: () => url
    })
    clipboard.on('success', () => {
      this.$toast('已复制链接,请在浏览器打开此网页来下载文件')
      clipboard.destroy()
    })
    clipboard.on('error', () => {
      this.$toast('可在浏览器打开此网页来下载文件')
      clipboard.destroy()
    })

4. ios端中使用vant ui输入框不灵敏,点击多次输入框才能获取焦点的问题。

1.在vue项目中使用fastclick可以安装fastclick插件,安装使用方法如下

2.在main.js中使用

import  FastClick  from  'fastclick'
FastClick.prototype.focus = function (targetElement) {
  let length;
  //这是因为这些元素并没有selectionStart和selectionEnd的整型数字属性,所以一旦引用就会报错,因此排除这些属性才使用setSelectionRange方法
  if (targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {
    length = targetElement.value.length;
    targetElement.focus();
    targetElement.setSelectionRange(length, length);//修复bug ios 11.3不弹出键盘,这里加上聚焦代码,让其强制聚焦弹出键盘
  } else {
    targetElement.focus();
  }
};
FastClick.attach(document.body);

2.应用级问题

在微信app中开发中注意事项:

1.微信的安全问题禁止了文档和软件的下载


android版的微信:点击下载回弹出显示可在浏览器打开此网页来下载文件 或者 没反应
ios版的微信:下载文件会变成预览(升级微信版本可在手机的云文档中找到文件)
2022/11/09

2.H5的应用场景在微信中需要考虑分享场景

H页面是可以通过右上角···进行分享的,如果不提前做处理,可能会导致分享出去的页面无法正常显示的问题。如果要自定义分享的路径,需要通过微信开放平台的微信sdk自定义分享链接(需要注入配置信息,例如公众号appId,timestamp等信息)

3.第三方能力接入问题

接入高德地图路线导航功能开发踩坑:

      async function geo_success(position) {
        // 获取经纬度
        let latitude = position.coords.latitude
        let longitude = position.coords.longitude
        

        const { locations } = await getAmapCoordinate({
          locations: `${longitude},${latitude}`
          coordsys: 'gps'
        })

        _this.locations = locations
      }

      // 位置获取失败,一般显示在日志里
      function geo_error(error) {
        console.error(error)
        console.log('error message:', error.message)
        console.log('error code:', error.code)
      }

      // 可选参数
      let geo_options = {
        timeout: 5000,
        enableHighAccuracy: true
      }

      // 获取静态地址
      navigator.geolocation.getCurrentPosition(
        geo_success,
        geo_error,
        geo_options
      )

1.首先在PC端Chrome浏览器中调试geolocation的getCurrentPosition接口会一直进入geolocationError,code值为3,提示timeout expired,geolocation定位功能只能在手机上展示。ps:一定得是localhost或者https(Secure安全域名)下才会返回用户当前经纬度。

2.通过手机拿到的经纬度直接在高德地图上显示的坐标会与实际坐标有一到两公里的误差,需要通过高德地图提供的坐标转换接口。 原因:包括但不限于高德、百度、腾讯自己绘制的地图经纬度与GPS地图中的的经纬度不是一一对应。

3.每个用户,自己的key值需要妥善,不要放置在前端文件中直接明文传输,可以根据官方提供的反向代理方案进行规避。参考 JS API 安全密钥使用

4.一个好看的vue loading加载进度条效果代码,即粘即用。

<style>
	.loader {
	    background: #000;
	    background: radial-gradient(#222, #000);
	    bottom: 0;
	    left: 0;
	    overflow: hidden;
	    position: fixed;
	    right: 0;
	    top: 0;
	    z-index: 99999;
	}
	
	.loader-inner {
	    bottom: 0;
	    height: 60px;
	    left: 0;
	    margin: auto;
	    position: absolute;
	    right: 0;
	    top: 0;
	    width: 100px;
	}
	
	.loader-line-wrap {
	    animation:
	        spin 2000ms cubic-bezier(.175, .885, .32, 1.275) infinite;
	    box-sizing: border-box;
	    height: 50px;
	    left: 0;
	    overflow: hidden;
	    position: absolute;
	    top: 0;
	    transform-origin: 50% 100%;
	    width: 100px;
	}
	
	.loader-line {
	    border: 4px solid transparent;
	    border-radius: 100%;
	    box-sizing: border-box;
	    height: 100px;
	    left: 0;
	    margin: 0 auto;
	    position: absolute;
	    right: 0;
	    top: 0;
	    width: 100px;
	}
	
	.loader-line-wrap:nth-child(1) {
	    animation-delay: -50ms;
	}
	
	.loader-line-wrap:nth-child(2) {
	    animation-delay: -100ms;
	}
	
	.loader-line-wrap:nth-child(3) {
	    animation-delay: -150ms;
	}
	
	.loader-line-wrap:nth-child(4) {
	    animation-delay: -200ms;
	}
	
	.loader-line-wrap:nth-child(5) {
	    animation-delay: -250ms;
	}
	
	.loader-line-wrap:nth-child(1) .loader-line {
	    border-color: hsl(0, 80%, 60%);
	    height: 90px;
	    width: 90px;
	    top: 7px;
	}
	
	.loader-line-wrap:nth-child(2) .loader-line {
	    border-color: hsl(60, 80%, 60%);
	    height: 76px;
	    width: 76px;
	    top: 14px;
	}
	
	.loader-line-wrap:nth-child(3) .loader-line {
	    border-color: hsl(120, 80%, 60%);
	    height: 62px;
	    width: 62px;
	    top: 21px;
	}
	
	.loader-line-wrap:nth-child(4) .loader-line {
	    border-color: hsl(180, 80%, 60%);
	    height: 48px;
	    width: 48px;
	    top: 28px;
	}
	
	.loader-line-wrap:nth-child(5) .loader-line {
	    border-color: hsl(240, 80%, 60%);
	    height: 34px;
	    width: 34px;
	    top: 35px;
	}
	
	@keyframes spin {
	
	    0%,
	    15% {
	        transform: rotate(0);
	    }
	
	    100% {
	        transform: rotate(360deg);
	    }
	}
</style>
<div class="loader">
    <div class="loader-inner">
        <div class="loader-line-wrap">
            <div class="loader-line"></div>
        </div>
        <div class="loader-line-wrap">
            <div class="loader-line"></div>
        </div>
        <div class="loader-line-wrap">
            <div class="loader-line"></div>
        </div>
        <div class="loader-line-wrap">
            <div class="loader-line"></div>
        </div>
        <div class="loader-line-wrap">
            <div class="loader-line"></div>
        </div>
    </div>
</div>