Skip to content

1px 问题

  1. Flexible 方案(viewport initial-scale=0.5)
  2. @media (-webkit-min-device-pixel-ratio: 2) {}+0.5px 方案(iOS7 及以下浏览器 0.5px 表现为 0px
  3. border-image 方案
  4. background-image 方案
  5. postcss-write-svg 插件(border-image/background+svg)方案(推荐)
  6. box-shadow 方案(-webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);
  7. ::before/::after+transform 方案(transform: scale(0.5)

响应式布局

  1. rem + postcss-px2rem
  2. vh + vw (推荐)

参考资料:

iOS 滑动不流畅

iOS5 及以上版本 -webkit-overflow-scrolling 滑动属性定义有两个值 touch, auto,默认是 auto

css
body {
    /* 当手指从触摸屏上移开,滚动会立即停止 */
    -webkit-overflow-scrolling: auto;

    /* 当手指从触摸屏上移开,会保持一段时间的滚动 */
    -webkit-overflow-scrolling: touch;
}

iOS 上拉/下拉边界出现白色空白

在 iOS 中,手指按住屏幕上下拖动,会触发 touchmove 事件。这个事件触发的对象是整个 webview 容器,容器自然会被拖动,剩下的部分会成空白。

解决方案:

  1. 监听 touchmove事件 preventDefault 禁止滑动(需注意过滤具有滚动容器的元素)
  2. 将上拉/下拉作为一个功能性操作(比如:下拉刷新,上拉加载)

页面件放大或缩小不确定性行为

表现:双击或者双指张开手指页面元素,页面会放大或缩小

原因:iOS 早期版本为了兼容 PC 页面所引入的功能

解决方案:可以定义 viewport 属性 user-scalable=nomaximum-scale=1.0, minimum-scale=1.0 解决。

html
<meta name="viewport" content="width=device-width, user-scalable=no" />

click 点击穿透与延迟

移动端浏览器事件执行顺序:touchstart -> touchmove -> touchend -> click

为什么会有 300ms 延迟?

第一款 iPhone 发布初期,当时的网站都是为大屏设计,苹果公司为了解决移动端小屏幕浏览的问题,为移动端浏览器加入了「双击缩放的」功能,这也是鼠标单击事件有 300ms 延迟的主要原因。

解决这个问题,主要有 3 种方案:

  1. 禁用缩放
    html
    <meta name="viewport" content="user-scalable=no">
    <meta name="viewport" content="initial-scale=1,maximum-scale=1">
  2. 更改默认的视口宽度
    html
    <meta name="viewport" content="width=device-width">
  3. CSS touch-action

以上三种方案并不能提供很好的兼容性,对于方案 1&2 touchstart 无延迟,但是 click 依然还有 70+ms 的延迟(仍然会导致点击穿透),这是移动端浏览器事件执行顺序导致。

那这是不是说明我们在移动端只要使用 touchstart 世界就安静了?并不是,touchstart 某些场景下也可能会导致点击穿透现象。比如:假设页面上有两个元素 A 和 B,A 为一个链接,B 覆盖在 A 之上,若 B 监听到 touchstart 事件后隐藏,A 就可能触发 click 事件导致跳转。

并且,若全部使用 touchstart,拖拽缩放是存在问题的,并且手指一触碰就会立即响应,而且没有按下的效果。

Fastclick 解决点击穿透问题的原理:

正常用户在触发 touchstart -> touchmove -> touchend 事件之后,300/70+ms 后会触发 click 事件。

Fastclick 的做法是在监听到 touchend 事件后,会通过 DOM 事件立即触发模拟一个 click 事件,然后立即去响应,并把浏览器 300/70+ms 之后真正的 click 事件阻止掉。

js
const FastClick = (function () {
    function attach(root) {
        let targetElement = null;
        root.addEventListener('touchstart', function () {
            targetElement = event.target;
        });
        root.addEventListener('touchend', function (event) {
            event.preventDefault();
            let touch = event.changedTouches[0];
            let clickEvent = document.createEvent('MouseEvents');
            clickEvent.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
            clickEvent.forwardedTouchEvent = true;
            targetElement.dispatchEvent(clickEvent);
        });
    }

    return { attach };
})();

FastClick.attach(document.body);

移动端手势

touchstart -> touchmove -> touchend -> click

Android 键盘弹出导致页面样式错乱问题

表现:Android 手机中,点击 input 框时,键盘弹出,将页面顶起来,导致页面样式错乱。

原因:大部分 App 布局中会有个固定的底部 menubar。安卓部分版本中,键盘弹出会压缩 absolute, fixed 定位的元素,导致可视区域变小,布局错乱。

解决方案

监听页面高度变化,强制恢复成键盘弹出前的高度:

js
// 记录原有的视口高度
const originalHeight = document.body.clientHeight || document.documentElement.clientHeight;

window.onresize = function(){
    const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
    if(resizeHeight < originalHeight ){
        // 恢复内容区域高度
        // const container = document.getElementById("container")
        // 例如 container.style.height = originalHeight;
    }
}

iOS 键盘收起,键盘区域空白问题

表现:iOS input 移开焦点,键盘收起时,键盘区域空白,未回落。

原因:在 iOS 12+ 和 wechat 6.7.4+ 中,键盘弹出挤压 absolute, fixed 定位的元素

解决方案

判断版本类型,更改滚动的可视区域:

js
const wechatInfo = window.navigator.userAgent.match(/MicroMessenger\/([\d\.]+)/i);
if (!wechatInfo[0]) return;
const wechatVersion = wechatInfo[1];
const version = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);

 // 如果设备类型为 iOS12+ 和 wechat6.7.4+,恢复成原来的视口
if (+wechatVersion.replace(/\./g, '') >= 674 && +version[1] >= 12) {
    window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));
}

window.scrollTo(x-coord, y-coord),其中 window.scrollTo(0, clientHeight) 恢复成原来的视口

iPhoneX 底部栏适配问题

表现:头部刘海两侧区域或者底部区域,出现刘海遮挡文字,或者呈现黑底或白底空白区域。

原因:iPhone X 以及它以上的系列,都采用刘海屏设计和全面屏手势。头部、底部、侧边都需要做特殊处理才能适配 iPhone X 的特殊情况。

解决方案

设置安全区域,填充危险区域,危险区域不做操作和内容展示。

css
padding-top: constant(safe-area-inset-top); // ios < 11.2
padding-top: env(safe-area-inset-top); // ios >= 11.2

h5 在全屏 iphonex 中的适配

保存页面为图片和二维码问题和解决方案

二维码:开源库 QRCode

Html 生成图片:开源库 html2canvas

注意,移动端生成的图片会比较模糊,可以使用一个新的 canvas 多倍生成,放入一倍容器中,达到清晰的效果:

js
import html2canvas from 'html2canvas';

const scaleSize = 2;
const newCanvas = document.createElement('canvas');
const target = document.querySelector('div');
const width = parseInt(window.getComputedStyle(target).width);
const height = parseInt(window.getComputedStyle(target).height);
newCanvas.width = width * scaleSize;
newCanvas.height = height * scaleSize;
newCanvas.style.width = width + "px";
newCanvas.style.height = height + "px";
const context = newCanvas.getContext("2d");
context.scale(scaleSize, scaleSize);
html2canvas(
    document.querySelector('.demo'),
    { canvas: newCanvas }).then(function(canvas) {
        // 简单的通过超链接设置下载功能
        document.querySelector(".btn").setAttribute('href', canvas.toDataURL());
}

微信公众号 H5 分享问题

添加一层蒙层,做分享引导

H5 调用 SDK 相关问题及解决方案

H5 调试相关方案与策略

  • vconsole
  • Charles + spy-debugger

小程序开发和普通 H5 开发有什么区别?

  1. 小程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整的浏览器对象,因而也缺少相关的 DOM API 和 BOM API
  2. 普通网页开发渲染线程和脚本线程是互斥的,JS 的执行会阻塞页面渲染,而在小程序中,二者是分开的,分别运行在不同的线程中
  3. 小程序开发需申请帐号、安装开发者工具、发布审核等,网页开发则不需要
  4. 小程序的执行环境:
    • iOS: 逻辑层 -> JavascriptCore, 渲染层 -> WKWebview
    • Android: 逻辑层 -> V8, 渲染层 -> Chromium 定制内核
    • 开发者工具: 逻辑层 -> NWJS, 渲染层 -> Chrome Webview

参考资料

小程序「同层渲染」?

通过一定技术手段把原生组件直接渲染到 webview 层级上

原生组件:video, map, canvas, picker

参考资料:

小程序性能优化

主要的优化策略可以归纳为两个方向:

  1. 提升加载性能
    • 压缩精简代码,降低 WXML 结构和 JS 代码的复杂性
    • 图片压缩,上传 CDN
    • 采用分包策略
    • 配置周期性更新,数据预拉取(能够在小程序冷启动的时候通过微信后台提前向第三方服务器拉取业务数据,当代码包加载完时可以更快地渲染页面,减少用户等待时间,从而提升小程序的打开速度)
  2. 提升渲染性能
    • 减少线程间通信的数据量:减少 setData 数据量
    • 降低线程间通信频次:合并 setData 请求,减少数据通信次数
    • 减少 WXML 节点数量

小程序的冷启动和热启动

  • 冷启动:如果用户首次打开,或小程序销毁后被用户再次打开,此时小程序需要重新加载启动,即冷启动
  • 热启动:如果用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时小程序并未被销毁,只是从后台状态进入前台状态,这个过程就是热启动

小程序销毁时机:

通常,只有当小程序进入后台一定时间,或者系统资源占用过高,才会被销毁。具体而言包括以下几种情形:

  • 当小程序进入后台,可以维持一小段时间的运行状态(大概5分钟),如果这段时间内都未进入前台,小程序会被销毁
  • 当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收

小程序的性能为什么会比 H5 好?

  1. 运行环境: H5 的运行环境是浏览器及 webview,而小程序的运行环境是微信开发团队基于浏览器内核完全重构的一个内置解析器,针对小程序专门做了优化
  2. 兼容性:官方定义了一套开发语言标准,不需要担心兼容性问题,开发成本比 H5 还要低
  3. 系统级权限:小程序能获取到微信提供一系列系统权限,而 H5 则有各种各样的限制
  4. 运行流畅度:一旦首次打开小程序,可以直接缓存很多资源,且退出后一段时间内重新打开,无需二次加载,可以明显感觉到很流畅,接近原生 APP 体验

小程序A webview 页面中的 H5 是否可以跳转小程序B?

几种可行方案:

  1. 小程序A webview H5 -> postMessage 通信,立即 navigateBack 触发 postMessage 事件执行 -> 小程序A页面自动弹窗确认是否跳转 -> 小程序B √
  2. 小程序A webview H5 -> 小程序码 -> 小程序B √
  3. 小程序A webview H5 -> 公众号文章 -> 小程序B √

以上方案是基于 iOS14.3, 微信7.0.21, 小程序基础库版本2.10.4+