外观
响应式布局的主要实现方式(原生方式)
没有任何一种布局方案是完美的,根据不同的场景和需求,选择不同的技术组合、互相弥补缺点,才是最优的解决方案。
固定布局 (Fixed Layout)
在“响应式设计”这个概念出现之前,开发者们已经在尝试解决多设备浏览的问题。通常大家会将页面的主要内容部分设置为固定宽度(如 960px、1200px),并设置居中显示。
局限性
在不同设备上,要么显示不全出现横向滚动条,要么两侧有大片空白。这是最早期的、只为桌面端设计的方案。
移动端与桌面端分离
在移动互联网诞生的初期,移动端 和 桌面端 确实是两个完全不同的东西,当时的主流做法是为手机单独制作移动版站点,判断用户是用的设备是移动端还是桌面端,重定向到对应的站点。
例如:百度首页,https://m.baidu.com/
const userAgent = navigator.userAgent;
// 典型的UA检测跳转代码
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
userAgent
)
) {
deviceType = "移动设备";
isMobile = true;
location.href = "https://m.baidu.com/";
} else {
location.href = "https://www.baidu.com/";
}
液态布局、百分比布局 (Liquid Layout / Fluid Layout)
在使用文档流布局的年代,我们一般使用 百分比(%),而不是 像素(px) 来规定每个模块的宽度,让整个页面的布局能随着屏幕大小,进行 平滑伸缩。
其 原理 就是,将 父元素的宽度和高度,按照百分比分配给子元素。父元素的尺寸变化时,子元素会自动变化,从而实现响应式布局。
<template>
<div class="container" style="margin-bottom: 20px;">
<span>给下边两个元素添加 1px 的 border:</span>
<el-radio-group v-model="selectedOption">
<el-radio label="1">添加</el-radio>
<el-radio label="0">移除</el-radio>
</el-radio-group>
</div>
<div class="container">
<div :class="coulumnClass" style="background: lightblue">
width: 50%
</div>
<div :class="coulumnClass" style="background: lightgreen">
width: 50%
</div>
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
const selectedOption = ref('0')
const coulumnClass = computed(() => {
return selectedOption.value === '1' ? 'border column' : 'column'
})
</script>
<style lang="scss" scoped>
.container {
box-sizing: border-box;
width: 100%;
margin: 0 auto;
.column {
width: 50%;
float: left;
height: 40px;
}
.border {
border: 1px solid red;
}
}
</style>
缺点
百分比布局的缺陷很明显,就是需要进行 复杂的手动计算,以及处理 预期之外的错位。
百分比的值是基于父元素的宽度来计算,这里边就牵扯到了盒模型和元素尺寸计算。如果使用统一的百分比还好,一旦使用的非百分比的单位(如 px),用来描述元素、文字的尺寸、边框、间距等,就可能会出现预期之外 换行、错位 等情况。
一般我们会针对这种场景多包裹一层 不含边框、间距 的容器,来解决这个问题。
弹性图片/媒体 (Flexible Images)
width
、max-width
、min-width
等属性给的元素一定的弹性范围,可以使用固定值或者百分比在一定程度上实现响应式布局。
例如:
div {
width: 100%;
min-width: 320px;
max-width: 1200px;
}
以上代码的作用:
- 在父元素小于 320px 时,div 的宽度为 320px(因为 min-width 生效)
- 在父元素介于 320px 和 1200px 之间时,div 的宽度为 100%,即随着视口宽度变化而变化
- 在父元素大于 1200px 时,div 的宽度为 1200px(因为 max-width 生效)
媒体查询 (Media Queries)
媒体查询是 CSS 中一种非常重要的技术,它可以根据设备特性(如屏幕宽度、分辨率)应用不同的 CSS 样式。一般上,遵循 移动优先 的设计思路,先编写移动设备的样式,然后使用媒体查询,根据设备宽度,应用不同的样式。
/* 移动优先样式 (默认) */
.container {
width: 100%;
}
/* 平板及以上 */
@media (min-width: 768px) {
.container {
width: 750px;
}
}
/* 桌面及以上 */
@media (min-width: 992px) {
.container {
width: 970px;
}
}
rem 方案(rem、em 单位)
Rem、Em 方案是基于 字体大小 来实现响应式布局的。通过对当前设备的宽度,动态地设置 HTML 根元素的字体大小,从而实现响应式布局。
rem
单位是相对于 根元素 的字体大小em
单位是相对于 当前元素 的字体大小
淘宝的 lib-flexible 方案
在 Rem 时代,最主流的实现方案是淘宝的 lib-flexible 方案,它的主要实现步骤:
- 重置视口的宽度和缩放比例,禁止用户手动缩放
- 默认设计稿的宽度为 750px,按照设备宽度平均分配为 10 等份,每等份对应 75px,来生成根元素的字体大小。比如设备为 1000px,那么根元素的字体大小为 100px。
- 添加事件监听,当页面尺寸重置、窗口变化、加载时,动态计算根元素的字体大小
- 使用 rem、em 单位来描述元素、文字的尺寸、边框、间距等
现代进阶
现代项目中一般会使用 PostCSS 插件、Sass/Less 函数,来将 px 单位转换为 rem 单位。
- 优势: 我们无需进行手动计算,直接使用 px 来描述样式
- 缺陷: 无法转换内联样式、JS 中设置的样式,可以使用 JS 对这些场景进行补充处理
rem 方案的优势
整体来说,Rem 方案的优势在于 简单、方便,只需要根据设计稿的宽度,计算出根元素的字体大小,然后使用 rem、em 单位来描述元素、文字的尺寸、边框、间距等,就可以实现响应式布局。
rem 方案的缺点
Rem 方案的虽然简单、方便,但是它同样存在一些缺点:
- 可能存在小数点导致的 计算误差,导致在不同设备上的显示效果和预期不符
- 如果设备的比例和设计图的比例不一致,可能导致屏幕上下、左右方向上 出现留白,无法 1:1 按照设计图还原
vw、vh 方案(vw、vh、vmin、vmax 单位)
vw、vh 方案是基于 视口宽度 和 视口高度 来实现响应式布局的。主要有四个单位:
vw
单位是相对于 视口宽度 的百分比vh
单位是相对于 视口高度 的百分比vmin
单位是相对于 vw 和 vh 中 较小 的那个的百分比vmax
单位是相对于 vw 和 vh 中 较大 的那个的百分比
我们可以通过手动计算,或者使用 Sass 函数、js 脚本,把 CSS、内联样式、js 中的 px 单位转换为 vw、vh 单位。
同样,也可以使用一些 PostCSS 插件,通过设置一些参数,如设计稿的尺寸、转换的目标单位等,来控制自动转换的行为和结果。以下是一个配置示例:
postcss.config.js
module.exports = {
plugins: {
// 其他 PostCSS 插件,例如 autoprefixer
"postcss-px-to-viewport": {
unitToConvert: "px", // 要转换的单位,默认为"px"
viewportWidth: 375, // 设计稿的视口宽度,例如 iPhone 6/7/8 是 375
unitPrecision: 5, // 转换后保留的小数位数
propList: ["*"], // 指定要转换的属性,['*'] 表示所有属性都转换
viewportUnit: "vw", // 转换后的视口单位
fontViewportUnit: "vw", // 字体转换后的视口单位
selectorBlackList: [], // 指定不转换的类,可以传入字符串或正则
minPixelValue: 1, // 最小的转换像素值,小于等于此值的不转换
mediaQuery: false, // 是否允许在媒体查询中转换 px
exclude: [/node_modules/], // 忽略 node_modules 下的文件
// 其他配置...
},
},
};
需要注意一点,PostCSS 无法处理内联样式、JS 中设置的样式,需要额外处理。
vw、vh 方案的优势
vw、vh 方案解决了 rem 方案留白的问题,可以完美适配各种不同尺寸比例的设备,包括移动端、平板、桌面端等,是当前比较主流的响应式布局方案。
弹性布局(Flexible Layout)
弹性布局(Flexible Layout),也被称为 flexbox,是一种用于 一维布局 的 CSS 布局模型。它允许你在容器中 灵活的分配 和 对齐 子元素,而无需使用 float 或 position 等传统的布局技术。整体来说,flexbox 提供了一种更简单、更强大的方式来创建响应式布局,是当下响应式布局的首选方案之一。
<template>
<div class="wrapper">
<div class="demo-panel lightBlue">1</div>
<div class="demo-panel lightyellow">2</div>
</div>
</template>
<style lang="scss" scoped>
.wrapper {
display: flex;
height: 250px;
.demo-panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
flex: 1;
}
.lightBlue {
background-color: lightblue;
}
.lightyellow {
background-color: lightyellow;
}
}
</style>
缺点
flexbox 布局的缺点主要体现在 兼容性 上。虽然当下 flexbox 已经被主流设备和浏览器支持,但不排除有些用户的设备存在 兼容性问题。为了确保在所有浏览器中都能正常工作,可能需要使用 Babel、polyfill 或 flexibility.js 等工具来添加对旧版浏览器的支持。
此外,flexbox 布局应对一些 二维布局 的场景,可能存在一些问题,可能需要结合其他一些技术解决。
表格布局(<table>
)
曾几何时,在那个没有网格布局的年代,表格布局是前端开发中一种常用的二维布局方式。表格布局通过使用 HTML 中的 <table>
元素和相关的 CSS 属性,将页面内容组织为 行 和 列 的网格结构。每个单元格可以包含文本、图片、表单元素等内容。
<template>
<table border="1">
<tr>
<td>第一行第一列</td>
<td>第一行第二列</td>
<td>第一行第三列</td>
<td>第一行第四列</td>
</tr>
<tr>
<td>第二行第一列</td>
<td>第二行第二列</td>
<td>第二行第三列</td>
<td>第二行第四列</td>
</tr>
<tr>
<td>第三行第一列</td>
<td>第三行第二列</td>
<td>第三行第三列</td>
<td>第三行第四列</td>
</tr>
</table>
</template>
<script>
export default {
name: 'TableLayout'
}
</script>
<style scoped>
table {
width: 100%;
border-collapse: collapse;
border: 2px solid #fff;
}
td {
padding: 8px;
text-align: center;
background: var(--el-color-warning);
}
</style>
缺点
由于前端需要通过 js 来组装表格,在处理复杂的表格布局时,如动态控制行、列的生成、动态控制单元格数据、不规则异形表格等,可能需要非常复杂的 js 代码来处理。逐渐的语义化更强、灵活性更强的网格布局(Grid)代替。
网格布局(Grid Layout)
网格布局(Grid Layout),也被称为 表格布局,是一种用于 二维布局 的 CSS 布局模型。它允许你将页面划分为 行 和 列,并在这些网格中放置和对齐元素。网格布局提供了一种强大的方式来创建复杂的响应式布局,是当下响应式布局的另一种主流方案。
<template>
<div class="wrapper">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
<div class="item">Item 8</div>
</div>
</template>
<style lang="scss" scoped>
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
.item {
height: 40px;
display: flex;
align-items: center;
justify-content: center;
color: var(--vp-c-text-1);
background-color: var(--el-color-warning);
}
}
</style>
缩放(CSS3 transform)
在前端还有一种比较特殊的布局方式,就是 缩放(Viewport Scaling)。缩放布局是指根据设备的视口(viewport)大小,动态调整页面的缩放比例,以适应不同屏幕尺寸的设备。
通过使用 CSS3 中的 transform
属性,如 scale
函数,来实现元素的缩放。
// 使用JavaScript实现缩放响应式
function updateScale() {
const container = document.getElementById("scaleContainer");
const viewportWidth = window.innerWidth;
const baseWidth = 1200; // 设计基准宽度
const scale = Math.min(1, viewportWidth / baseWidth);
container.style.transform = `scale(${scale})`;
container.style.transformOrigin = "top center";
}
window.addEventListener("resize", updateScale);
window.addEventListener("load", updateScale);
优势
- 实现简单:只需几行代码即可实现基本的响应式效果,无需编写复杂的媒体查询。
- 保持布局比例:缩放可以完美保持原始设计的比例和布局,不会导致元素错位。
- 开发速度快:对于固定尺寸的设计稿,可以快速实现响应式效果,节省开发时间。
- 一致性:在所有设备上保持完全一致的外观和用户体验。
缺点
由于 css 的缩放只是视觉上的缩放,并没有改变元素的实际尺寸,所以在缩放时可能会导致页面的一些交互事件失效,如点击、滚动等。
更新日志
2025/9/27 17:16
查看所有更新日志
1eb9b
-docs(Javascript): 更新Generator生成器函数文档内容于