项目地址(持续迭代中):github.com/jyliyue/vit…
系列文章:
前言
上一篇已经就项目结构和常用插件的配置给大家做了简单的介绍,本篇主要会从移动端页面布局和常见兼容问题展开做讲解,废话不多,马上开始!
页面容器组件
思路
移动端页面一般采用最外层容器固定宽高,内部使用 弹性布局flex 处理,移动端的样式布局建议大家能使用 flex 弹性就使用 flex ,尽量少使用 calc(XX) 计算属性,一方面使用 calc 会有一定的 性能损耗,另一方面看过很多案例使用 calc 通常是配合 100vh 计算页面某一区域的高度,这两者加起来很多时候就会让页面在不同的屏幕下表现不一致,所以为了降低日后的维护负担,还是尽量强迫自己学着用 flex 花式解决布局问题 ^_^
为了保持所有页面布局的一致性,也为了日后维护时能统一处理一些兼容性问题,该项目统一封装 组件作为页面容器
关于 100 vh 与 html, body { height: 100% }
首先,这两种方法大家应该都很熟悉,都可以让我们获取一个全屏元素,大家在写代码时也时不时会用到,但是在一些场景下却会产生一些不可预料的问题,所以在选择方案上很重要,这是移动端布局的基础,打好地基很重要,这里我们先看下淘宝的做法
这里大家应该就有疑问了,为什么不使用 100vh 而是使用 html, body { height: 100% }
- 首先我们来了解下 100vh
1vh单位代表了屏幕可视区域的1% ,vh 获取的是视窗高度,100vh 在开发中浏览器预览时页面占据整个可视区域没有任何问题
但是,核心问题是移动浏览器(Chrome和Safari)有一个 “ 特性 ” ,地址栏有时可见,有时隐藏,改变了视口的可见大小。这些浏览器没有将100vh
的高度调整为视口高度变化时屏幕的可见部分,而是将100vh
设置为隐藏地址栏的浏览器高度。结果是,当地址栏可见时,屏幕的底部部分将被切断,从而破坏了100vh
的初衷
- html, body { height: 100% }
首先 html 设置 height: 100% 表示可视区域窗口的大小,而 html 的高度会随着窗口可用区的高度增大而增大,减小而减小,这样我们就不用怕地址栏影响可视区域了
然后需要注意用 body 继承下 html 的高度属性就可以生效了,一般项目中这样配置就可以
html, body, #app { width: 100%; height: 100%; } 复制代码
页面组件结构设计
组件实现
<template> <div class="app-page"> <div class="app-main"> <slot></slot> </div> </div> </template> <style lang="scss" scoped> .app-page { position: relative; width: 100%; height: 100%; display: flex; flex-direction: column; .app-main { flex: 1; overflow: auto; } } </style> 复制代码
标题栏实现
标题栏我们希望它能根据我们路由里定义的标题动态显示页面标题,其次支持可以动态的控制标题栏的显示与隐藏,毕竟有的时我们的页面是不需要显示标题栏的,因为我们项目使用的是 ui 框架是 vant-ui,这里就直接拿 进行改造
<script setup> // This starter template is using Vue 3 <script setup> SFCs defineProps({ navBar: { type: Boolean, default: true }, leftArrow: { type: Boolean, default: true }, onClickLeft: { type: Function, default: () => history.back() } }) const route = useRoute() </script> <template> <div class="app-page"> <van-nav-bar v-if="navBar" :title="route.meta.title" :left-arrow="leftArrow" @click-left="onClickLeft" /> <div class="app-main"> <slot></slot> </div> </div> </template> 复制代码
这样就完工了,后续所有的页面都用 组件作为根组件包裹,来保障所有页面结构统一,后续有兼容调整的话,也只要在组件中处理就好了
页面应用
<template> <app-page> <div> ... 开始开发 ... </div> </app-page> </template> 复制代码
移动端兼容
ios 网页消除阻尼回弹效果
通过固定应用内容区域,消除最外层的拖拽界面抖动,让我们整个应用的功能都在 #app 这个固定宽高的盒子内完成,然后关键点是让 #app 容器隐藏溢出元素 overflow: hidden
html, body, #app { width: 100%; height: 100%; // 解决 ios 界面回弹 overflow: hidden; } 复制代码
ios 底部安全区域样式处理
使用 ios 提供的 safe-area-inset-top 和 safe-area-inset-bottom 做上下安全区域的兼容处理
#app { // 顶部安全区 padding-top: constant(safe-area-inset-top); padding-top: env(safe-area-inset-top); // 底部安全区 padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); } 复制代码
需要注意H5网页 meta 要设置 viewport-fit=cover 的时候才会生效
移动端 click 延时问题
移动端之所以会有点击 300ms 的延时,原因是移动端屏幕双击会缩放,需要一个判定时间,所以解决这个问题简单粗暴点只要禁用掉屏幕的缩放功能就好了
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover"> 复制代码
此外,也可以借助 fastclick 插件解决300ms延迟
ios 时间格式的坑
这个算是顺便给大家提个醒,在做移动端项目时不要使用 new Date().getTime() 去处理 2022-10-14 之类格式的时间为时间戳,特别是用到 datePicker 之类的组件时要格外注意,例如
new Date("2022-01-17 10:00").getTime() // NaN 复制代码
这样你在 ios 下会获得一个 NaN 的异常返回,原因是 ios 下对数字中间的横杆 - 转化会有问题,需要转化为斜杆处理 /
const timestamp = (new Date('2021-07-28 18:00'.replace(/-/g, '/'))).getTime() 复制代码
目前本项目对移动端的兼容处理还比较简单,主要是针对 ios 端一些常见问题的处理,后续想到什么会不断完善补充,仓库也会定期更新,大家多多关注
项目地址(持续迭代中):github.com/jyliyue/vit…
好了,本篇对移动端项目的页面容器介绍和一些常见兼容问题的科普告一段落,下篇开始会对该项目的状态缓存管理体系 pinia 和 vue3 中 keep-alive 的应用做介绍,并带大家一起封装一个移动端常用的列表组件,支持接口配置化以及进入详情返回列表时记录位置等常用功能,敬请大家期待!