【山竹记账前端-react】16.手机上的 bug


大纲链接 §

[toc]


手机调试

  • 完成一个页面后就应该使用真手机查看

1. 手机调试步骤 ⇧

1.1. 防火墙开启端口 ⇧

  • 搜索 windows 防火墙开启端口
  • windows 搜索 高级安全 防火墙
    • 打开进入到 入站规则
    • 点击 新建规则
    • 点击 端口
    • 选择默认的 TCP;特定端口 输入 5173
    • 点击 下一步;选择允许连接;默认选择所有:域、专用、公用
    • 输入任意名称:5173;点击完成
  • 这样手机才能访问电脑 ip
  • 可自定义需要打开的端口,根据项目的运行使用的端口

1.2. 使用有线连接安卓手机 ⇧

  • 确保手机和电脑在同一网络下
  • 用 USB 连接手机和电脑(需要可以传文件的数据线,不可使用仅支持充电功能的线材)
  • 打开开发者选项
  • 手机开启 USB 调试(手机处于解锁状态、手机开启常亮状态)

1.3. 使用 Android 调试桥 (adb) ⇧

使用 platform-tools adb 工具

  • 下载 SDK 平台工具
  • 解压到任意目录(如 D:\software\adb)
  • 在该目录下 Shift + 右键,选择「在此处打开终端」
  • 运行 ./adb.exe kill-server
  • 运行 ./adb.exe devices,手机会弹出授权确认

    1
    2
    3
    4
    5
    
    XS@DESKTOP-B1B73BAFA /d/software/adb-platform-tools
    $ ./adb.exe devices
    * daemon not running; starting now at tcp:5037
    * daemon started successfully
    List of devices attached

也可以下载使用 Minimal ADB and Fastboot,效果一样

  • 下载
  • 双击执行 cmd-here.exe
  • 运行 adb kill-server
  • 运行 adb devices,手机会弹出授权确认

adb devices 确认手机是否与电脑连接

参考

取消授权,重启

  • 多次连接或者更换手机连接,有时候需要重新授权
  • 在手机「开发者选项」中找到「撤销 USB 调试授权」,关闭 USB 调试
  • 切换 USB 模式:将手机的 USB 连接方式从“仅充电”改为“文件传输 (MTP)”或“PTP”
  • 重启 ADB 服务:在电脑终端运行 adb kill-server 关闭服务,再重新连接设备
  • 删除 ~/.android 文件夹是所有密钥;重启电脑
  • 开关调试开关:在手机开发者选项中,关闭「USB 调试」然后再重新打开

1.4. 运行项目,手机访问 ⇧

在容器中启动项目 pnpm run dev

  • 电脑可以访问 http://localhost:5173/

使用命令行 ipconfig 显示网卡wifi访问ip 找到 IPv4 Address 192.168.31.251

  • 由于已自动映射到宿主机网络,电脑可以访问 http://192.168.31.251:5173
  • 确保手机和电脑在同一网络下
  • 用 USB 连接手机和电脑(需要可以传文件的数据线,不可使用仅支持充电功能的线材)
  • 手机开启 USB 调试(手机处于解锁状态、手机开启常亮状态)
  • 手机使用 chrome 或者 edge 浏览器打开要调试的页面
    • 在打开 chrome://inspect/#devices 页面,等待片刻,找到对应页面链接
    • 在打开 edge://inspect/#devices 页面,等待片刻,找到对应页面链接
  • 在 chrome://inspect 页面,勾选“Discover USB devices”
  • 点击 Inspect 按钮,打开调试工具,稍等片刻

chrome_inspect_devices

使用手机中的 chrome、edge 浏览器查看

  • 直接访问 http://192.168.31.251:5173
  • 点击对应的网址 inspect
  • 查看底部部分被遮挡,需要后续调试

底部遮挡问题 https_DevTools

使用微信浏览器查看

  • 发送信息给自己 http://debugxweb.qq.com/?inspector=true
  • 点击对应的网址 inspect
  • 然后将地址栏改为 http://192.168.31.251:5173
  • 点击网页链接
  • 可能出现空白页面,需要后续调试

weixin_DevTools


两个页面对比

三个方案

  • 将窗口高度变短
  • 用 JS 代替用户滚动
  • 改 UI

2. 方案一:vh-check.js ⇧

main.ts

1
2
3
import vhCheck from 'vh-check'

/* const test = */ vhCheck()

main.scss

1
2
3
4
5
6
main {
  height: 100svh;
  /* If you need to support browser without CSS var support (<= IE11) */
  height: calc(100vh - var(--vh-offset, 0px));
  /* enable vh fix */
}
  • 判断是否可以识别到 --vh-offset 否则就使用 0px
  • 并且将有 100vh 的地方都改为 100svh

也可以直接改 unocss.config.ts

1
2
3
rules: [
  ["h-screen", {height: 'calc(100vh - var(--vh-offset, 0px))'}]
]

问答

  • 不可使用 height: 100%; 代替;因为如果使用,需要从 body 到 容器组件一路所有的高度都设置一遍 height: 100%; 很麻烦

参考

自己实现 setRealVh.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function setRealVh() {
  // 计算 1vh 对应的实际像素
  let vh = window.innerHeight * 0.01;
  // 将其挂载到全局 CSS 变量中
  document.documentElement.style.setProperty('--vh', `${vh}px`);
}

// 初始化并监听变化
setRealVh();
window.addEventListener('resize', setRealVh);

对应 CSS 使用:

1
2
3
4
.full-screen {
  height: 100svh; /* 兜底方案 */
  height: calc(var(--vh, 1vh) * 100);
}

3. 方案二:用 JS 代替用户滚动 ⇧

main.ts

1
2
3
setTimeout(() => {
  window.scrollTo(0, 9999)
}, 1000)
  • 无法完全模拟用户滚动

4. 方案三:改需求 ⇧

遇到的bug:有 CSS 动画会影响fix定位

  • 外部容器有 transform 动画,会将固定定位元素限制在该容器上作为相对定位的父容器
  • 固定定位元素会根据该容器的位置进行定位
  • 取消 transform 动画可以查看固定定位元素相对于更外部的容器定位了

@/layouts/WelcomeMainLayout.tsx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// import ...

export const WelcomeMainLayout: React.FC = () => {
  return (
    <div className={cn(
      'overflow-clip',
      'bg-[#5f34bf]',
      'h-screen pb-16px',
      'flex flex-col items-stretch',
    )}>
      <MangosteenHeader />

      <SwiperGuide />

      <ToStartPage />
    </div>
  )
}

5. 回顾 ⇧

两种手机查看方式

  • 使用 Windows 安卓子系统
    • 方便,但内存要求较高,不方便调试,无法查看控制台信息
    • 方便模拟任何尺寸,无需购买额外手机,成本低
  • 使用真手机
    • 性能好,方便远程调试,可以查看控制台
    • 需要接 USB 线麻烦
    • 成本高

解决 100vh 和真实手机屏幕不一样高被遮挡的问题

  • 放弃全屏
    • 使用 vh-check 调整高度,伪全屏,使页面刚好填满屏幕
  • 使用 JS 帮用户滚一下
    • 效果不好
  • 改造 UI,尽量不被遮挡
    • 设计师、产品经理不一定愿意帮你

6. 手机大BUG:微信页面崩溃 ⇧

解决微信页面不显示页面的问题

  1. 打开 Chrome 远程调试
  2. 打开微信调试页面
  3. 用大脑解决它

fix(vite)!: drive global HMR through Vite's hotUpdate hook, drop Vite 5 support- #5335 #5335

可以使用本地 preview 时预览调试

  • 打包预览时无需热更新模块,无 top-level-await
  • pnpm run build
    • 注意去掉 "build": "tsc && vite build --base=/react-mangosteen-1-preview/", 中的 --base=/react-mangosteen-1-preview/
  • pnpm run preview
  • //localhost:4173 也需要添加到防火墙白名单中

7. BUG:链接不能点击 ⇧

链接不能点击了

  • 记录解决bug的过程
  • 由于 useSwipe(main, { onTounchStart: e => e.preventDefault() }) 阻止默认行为,导致点击下一页失效
  • 去除即可
  • 之前添加这句时因为手势滑动屏幕触发手机上弹性动画,body 上添加 overflow-x: hidden;

判断手势滑动方向,是否为左右滑动还是上下滑动

  • y 方向上的距离大于 x 方向上的绝对值距离时就判定为上下滑动,否则就是左右滑动

@/hooks/useSwipe.tsx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import type { RefObject } from 'react'
import { useEffect, useRef, useState } from 'react'

interface Config {
  onTouchStart?: (e: TouchEvent) => void
  onTouchMove?: (e: TouchEvent) => void
  onTouchEnd?: (e: TouchEvent) => void
}

type SwipeDirection = '' | 'left' | 'right' | 'up' | 'down'
const SWIPE_THRESHOLD = 3

function getSwipeDirection(distanceX: number, distanceY: number): SwipeDirection {
  if (Math.abs(distanceX) > Math.abs(distanceY)) {
    if (Math.abs(distanceX) < SWIPE_THRESHOLD) { return '' }
    return distanceX > 0 ? 'right' : 'left'
  } else {
    if (Math.abs(distanceY) < SWIPE_THRESHOLD) { return '' }
    return distanceY > 0 ? 'down' : 'up'
  }
}

// eslint-disable-next-line antfu/top-level-function
export const useSwipe = (
  elementRef: RefObject<HTMLElement | null>,
  config?: Config,
) => {
  const [direction, setDirection] = useState<SwipeDirection>('')
  const coordinateXRef = useRef(-1)
  const coordinateYRef = useRef(-1)

  const onTouchStart = (e: TouchEvent) => {
    config?.onTouchStart?.(e)
    coordinateXRef.current = e.touches[0].clientX
    coordinateYRef.current = e.touches[0].clientY
  }

  const onTouchMove = (e: TouchEvent) => {
    config?.onTouchMove?.(e)
    const tempX = e.touches[0].clientX
    const tempY = e.touches[0].clientY
    const distanceX = tempX - coordinateXRef.current
    const distanceY = tempY - coordinateYRef.current
    setDirection(getSwipeDirection(distanceX, distanceY))
  }

  const onTouchEnd = (e: TouchEvent) => {
    config?.onTouchEnd?.(e)
    setDirection('')
  }

  useEffect(() => {
    const el = elementRef.current
    if(!el) { return }
    const aborter = new AbortController()
    const opts = { signal: aborter.signal }
    el.addEventListener('touchstart', onTouchStart, opts)
    el.addEventListener('touchmove', onTouchMove, opts)
    el.addEventListener('touchend', onTouchEnd, opts)
    return () => { aborter.abort() }
  }, [])

  return ({
    direction,
  })
}

·未完待续·

参考文章

相关文章

  • 无

  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名