本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。
本篇文章将介绍关于不同尺寸终端的自动适配,由于终端尺寸复杂多样,而且设备/系统/应用兼容性存在差异,导致适配工作经常百密一疏(其实往往是一密百疏),所以它一直是前端开发最头疼的问题之一。
为确保网站、应用或其他界面在各种设备上都能够良好的呈现和使用,开发人员经常使用以下方法进行适配:
- 流式布局:它使用百分比单位和自适应容器来创建流式布局,使页面元素根据屏幕尺寸自动调整大小
- 弹性布局:它使用相对单位(如百分比、vw、em、rem 等)来定义元素的相对尺寸,以便元素可以根据父容器的大小进行伸缩
- 移动优先:首先考虑移动设备的设计风格及操作交互,然后再尽量适配大屏幕
- 使用 Viewport 参数控制页面在移动设备上的缩放显示
- 使用第三方组件库:例如 Vant、Mint UI 等,这些组件库已经做好了移动端大部分的适配工作
在具体的实现上,以上方法一般会组合使用,原本设计好了统一的风格,但它不能百分百适配所有情况,只能发现一个问题解决一个问题,结果就像打补丁,补丁接补丁、补丁摞补丁。
本篇文章我将结合三国史诗馆网站的终端适配进行介绍,包括以下部分:
- 常用配置/元素/单位的基本概念
- 三国史诗馆网站 PC 端与手机端的显示差异
- 主要代码改动
常用配置/元素/单位的基本概念
下方表格列举了一些比较常见的适配相关的前端名词:
名词 | 解释 | 使用示例 |
---|---|---|
width/height | 定义元素的宽度/高度 | style="width:800px;height:600px;" |
scrollWidth/scrollHeight | 对象的内容的实际宽度/高度 | let scrollWidth = document.body.scrollWidth; |
clientWidth/clientHeight | 对象的内容的可视宽度/高度 | let clientWidth = document.body.clientWidth; |
offsetWidth/offsetHeight | 对象的整体的可视宽度/高度(包括滚动条等) | let offsetWidth = document.body.offsetWidth; |
innerWidth/innerHeight | 浏览器窗口的内部宽度/高度 | let innerWidth = document.body.innerWidth; |
font-size | 字体大小 | style="font-size:12px;" |
px | 以像素为单位定义元素大小 | style="font-size:12px;" |
pt | 以磅为单位定义元素大小 | style="font-size:12pt;" |
vw | 相对于视口宽度(Viewport Width)的百分比 | style="width:10vw;" // 相当于视窗宽度的 10% |
vh | 相对于视口高度(Viewport Width)的百分比 | style="width:10vh;" // 相当于视窗高度的 10% |
em | 相对于当前元素字体大小的相对长度单位 | style="width:10em;" |
rem | 相对于根元素字体大小的相对长度单位 | style="width:10rem;" |
% | 相对于父元素的百分比值 | style="width:10%;" |
@media | 媒体查询:根据不同的媒体类型和条件来区分各种设备 | media="screen" 或 @media screen |
calc() | 一个可以进行数值基本运算的函数 | height:calc(100vh-64px); |
span | 栅格布局中的栅格占位格数 | span="8" // 元素占父元素的 8/24 栅格 |
xs | 视窗宽度 <576px 时栅格占位格数 | xs="8" // 视窗宽度 <576px 时元素占父元素的 8/24 栅格 |
sm | 视窗宽度 ≥576px 时栅格占位格数 | sm="8" // 视窗宽度 ≥576px 时元素占父元素的 8/24 栅格 |
md | 视窗宽度 ≥768px 时栅格占位格数 | md="8" // 视窗宽度 ≥768px 时元素占父元素的 8/24 栅格 |
lg | 视窗宽度 ≥992px 时栅格占位格数 | lg="8" // 视窗宽度 ≥992px 时元素占父元素的 8/24 栅格 |
xl | 视窗宽度 ≥1200px 时栅格占位格数 | xl="8" // 视窗宽度 ≥1200px 时元素占父元素的 8/24 栅格 |
xxl | 视窗宽度 ≥1600px 时栅格占位格数 | xxl="8" // 视窗宽度 ≥1600px 时元素占父元素的 8/24 栅格 |
除常见名词外,其中我主要用到了以下几个:
- offsetWidth:使用它作为判断尺寸大小的依据
- vw:使用百分比宽度设置页面元素的 padding/margin,可以使元素间距在大小屏幕上都很协调
- vh:主要用于设置页面的最小高度,避免在宽高比不协调的设备中显示异常
- em:主要用于设置文字的字号与间距等
- calc():用于计算页面最小高度
显示差异及代码实现
整体布局
整体上主要是页面边距的差异,将在人物大全页面中进行说明。
在页面入口文件 App.vue 中获取页面可视宽度,将其存储到全局变量中,用于其他页面的样式选择判断,实现代码如下:
/src/App.vue
const storage = useStore();
// 更新页面宽度
const onResize = () => {
storage.commit('updateClientWidth', document.body.offsetWidth);
};
// 加载页面
onMounted(() => {
// 更新页面宽度
onResize();
window.addEventListener("resize", onResize);
});
// 销毁页面
onBeforeUnmount(() => {
window.removeEventListener("resize", onResize);
});
人物大全页面
- 左上角的 logo 图片在 PC 端距页面左侧较远,手机端较近,可对比下方两个图片,实现代码如下:
/src/layout/index.vue
...
<!-- 使用相对宽度 3.5vw 适配不同宽度页面 -->
<a-layout-header class="clear-fix" style="background-color: white; padding: 0 3.5vw">
...
-
页面标题的边距、内容区的边距,同见上图,实现方式基本相同,略
-
每行的卡片数量不同,在 PC 端有 6 个,而手机端只有 2 个(跟设备无关,跟页面尺寸有关),同见上图,实现代码如下:
/src/views/record/personage/index.vue
...
<!-- 使用 xs/sm/lg 适配不同宽度页面 -->
<a-col v-for="personage in personageList" :xs="12" :sm="8" :lg="4">
...
- 卡片中人物头像尺寸不同,同见上图,实现代码如下:
/src/views/record/personage/index.vue
...
<!-- 综合页面宽度、卡片个数、最大宽度/高度,得出最终的宽度/高度 -->
<a-avatar :src="personage.recordPersonageEntity?.avatar" :style="{width: scrollWidth() * cardProportion() * 0.32 + 'px', height: scrollWidth() * cardProportion() * 0.32 + 'px', 'max-width': '66px', 'max-height': '66px'}"/>
...
- 分页器样式不同,在 PC 端功能更丰富,可对比下方两个图片,实现代码如下:
/src/views/record/personage/index.vue
...
<!-- 根据页面宽度选择使用不同样式的分页器 -->
<a-pagination v-if="scrollWidth() >= 800" :current="query.pageNum" :page-size="query.pageSize" showSizeChanger style="margin-top: 2vh; text-align: center;" :total="personageTotal" :show-total="(total, range) => `${range[0]}-${range[1]} of ${total} items`" @change="paginationChange"/>
<a-pagination v-else :current="query.pageNum" :page-size="query.pageSize" style="margin-top: 2vh; text-align: center;" :total="personageTotal" simple @change="paginationChange"/>
...
人物详情页面
人物详情页面的锚点菜单,在 PC 端存在但手机端不存在,可对比下方两个图片,实现代码如下:
/src/views/record/personage/detail.vue
...
<!-- 根据页面宽度选择内容区所占列数 -->
<a-col :span="scrollWidth() >= 800 ? 20 : 24">
...
<!-- 根据页面宽度选择是否显示锚点菜单 -->
<a-col v-if="scrollWidth() >= 800" span="4" style="background-color: white;">
<a-anchor :affix="true" :offsetTop="30" style="margin-top: 8px; margin-left: 10px" :items="anchorItems"></a-anchor>
</a-col>
...
总结
目前我的网站的页面较少,而且页面较简单,所以做终端适配很容易,涉及到的技术也不多,但是其方法和核心思想是通用的,我的建议是:
- 尽量不使用 px 等固定单位,推荐使用百分比单位 (%、vw、vh)与相对单位(em、rem)
- 一定不要追求 PC 端与手机端的功能一致,手机端界面尽量简洁、操作尽量方便
- 一定不要追求技术实现方案统一,也一定不要反复打补丁,要设计先行、随机应变
本篇文章到这里就结束了,欢迎关注后续更新。