前端Vue3项目响应式布局实战——基于Flex布局和媒体查询的响应式布局设计
效果演示
Desktop端
iPad/Mobile端
Flex布局
Flexbox 布局总结
1. Flex 容器属性:
- display:定义为 flex 容器。
- flex-direction:主轴方向(行或列)。
- flex-wrap:控制换行行为。
- justify-content:主轴对齐方式(左右或上下)。
- align-items:交叉轴对齐方式(纵向对齐)。
- align-content:多行对齐方式(仅当有换行时有效)。
2. Flex 项目属性:
- order:改变项目的顺序。
- flex-grow:控制项目扩展。
- flex-shrink:控制项目收缩。
- flex-basis:定义项目的初始尺寸。
- flex:flex-grow、flex-shrink 和 flex-basis 的简写。
- align-self:单个项目的交叉轴对齐。
Flex 布局属性总结
Flexbox(弹性盒子布局)提供了一套强大的布局工具,既适用于复杂的网页布局,又能处理响应式设计。其主要属性可以分为两类:
- Flex 容器属性(控制容器的布局行为)
- Flex 项目属性(控制容器内子元素的布局行为)
一、Flex 容器属性
-
display
- 定义容器的布局为 flexbox。
- display: flex; 或 display: inline-flex;(容器行为类似块级或行内元素)
.container { display: flex; }
-
flex-direction
- 控制主轴(主方向)上项目的排列方向。
- row(默认):水平,从左到右排列。
- row-reverse:水平,从右到左排列。
- column:垂直,从上到下排列。
- column-reverse:垂直,从下到上排列。
.container { flex-direction: row; }
-
flex-wrap
- 控制项目是否换行。
- nowrap(默认):不换行,所有项目都在一行内。
- wrap:换行,项目会换行并在多行中排列。
- wrap-reverse:换行,但换行方向与 wrap 相反。
.container { flex-wrap: wrap; }
-
justify-content
- 在主轴方向上对齐项目,控制项目之间的间隔。
- flex-start(默认):项目靠容器的起始端对齐。
- flex-end:项目靠容器的末尾对齐。
- center:项目在主轴上居中对齐。
- space-between:项目间的间隔相等,第一个项目靠近容器起始端,最后一个项目靠近容器末尾端。
- space-around:项目之间的间隔相等,容器两侧的空白间隔也相等。
- space-evenly:所有项目之间的间隔相等,包括容器两侧的空白。
.container { justify-content: center; }
-
align-items
- 在交叉轴(垂直于主轴)上对齐项目。
- flex-start:项目靠交叉轴起始端对齐。
- flex-end:项目靠交叉轴末尾端对齐。
- center:项目在交叉轴上居中对齐。
- baseline:项目按文本基线对齐。
- stretch(默认):项目拉伸以填充容器。
.container { align-items: center; }
-
align-content
- 控制多行(有换行时)在交叉轴上的对齐方式。
- flex-start:行靠交叉轴的起始位置对齐。
- flex-end:行靠交叉轴的末尾位置对齐。
- center:行在交叉轴上居中对齐。
- space-between:行间隔相等,第一个和最后一个行与容器边缘对齐。
- space-around:行间隔相等,行与容器之间也有间隔。
- stretch(默认):行拉伸以填充容器。
.container { align-content: space-between; }
二、Flex 项目属性
-
order
- 控制项目的排列顺序,数字越小,排列越靠前,默认为 0。
.item { order: 1; }
-
flex-grow
- 定义项目如何在主轴方向上扩展,以填充容器中剩余的空间。默认为 0,表示不扩展。
- 如果所有项目的 flex-grow 值相同,空间会均等分配。
.item { flex-grow: 1; }
-
flex-shrink
- 定义项目如何在容器空间不足时缩小。默认为 1,表示项目会缩小。
- 如果设置为 0,则项目不会缩小。
.item { flex-shrink: 0; }
-
flex-basis
- 定义项目在分配剩余空间之前的初始大小。默认值为 auto,即根据项目的内容或宽度来决定。
- 可以设置为具体的像素值、百分比等。
.item { flex-basis: 200px; }
-
flex
- flex 是 flex-grow、flex-shrink 和 flex-basis 的简写。
- 常见的值:
- flex: 1 等价于 flex-grow: 1; flex-shrink: 1; flex-basis: 0%,让项目在可用空间中均等分配。
- 5:3:2分->flex值分别设为5、3、2
- flex: auto 等价于 flex-grow: 1; flex-shrink: 1; flex-basis: auto。
- flex: none 等价于 flex-grow: 0; flex-shrink: 0; flex-basis: auto,不允许扩展或收缩,保持初始大小。
.item { flex: 1; }
-
align-self
- 控制单个项目在交叉轴上的对齐方式,覆盖 align-items 设置。
- 可选值:auto、flex-start、flex-end、center、baseline、stretch。
.item { align-self: center; }
- 控制项目的排列顺序,数字越小,排列越靠前,默认为 0。
Flexbox 为响应式设计提供了非常强大的支持,尤其在复杂布局或动态调整时,能够通过这些属性轻松控制元素的排列、对齐和间距。
媒体查询
媒体查询详解
媒体查询(Media Queries)是 CSS3 提供的一种功能,用于根据设备的特性(如屏幕大小、分辨率、方向等)应用不同的样式,从而实现响应式设计。
媒体查询主要用于适配不同的设备,如桌面、平板、手机等。
1. 基本语法
媒体查询的基本语法如下:
@media media-type and (media-feature: value) { /* CSS规则 */ }
- media-type:媒体类型,例如 screen、print。
- media-feature:媒体特性,例如屏幕宽度、分辨率。
- value:具体值,用于定义某个媒体特性的范围或条件。
2. 媒体类型
媒体查询支持以下媒体类型:
媒体类型 描述 all 默认值,适用于所有设备。 screen 针对屏幕设备,例如电脑、平板、手机等。 print 针对打印设备。 speech 针对屏幕阅读器等语音设备。 示例:
@media screen { body { background-color: lightblue; } }
3. 常用媒体特性
媒体特性用于检测设备的具体属性,以下是常用媒体特性及其说明:
特性 描述 width 视口(viewport)的宽度。 height 视口的高度。 min-width 视口的最小宽度。 max-width 视口的最大宽度。 min-height 视口的最小高度。 max-height 视口的最大高度。 aspect-ratio 视口的宽高比,例如 16/9。 orientation 设备方向:portrait(竖屏)或 landscape(横屏)。 resolution 屏幕分辨率,例如 300dpi 或 2dppx。 prefers-color-scheme 用户的首选颜色模式:light 或 dark。 4. 媒体查询的逻辑运算符
媒体查询支持以下逻辑运算符,允许结合多个条件:
4.1 and
- 用于同时满足多个条件。
@media screen and (min-width: 768px) and (orientation: landscape) { body { background-color: lightgreen; } }
4.2 not
- 用于排除某些条件。
@media not screen and (max-width: 768px) { body { background-color: pink; } }
4.3 ,(逗号,等价于“or”)
- 用于满足任一条件即可生效。
@media (max-width: 768px), (orientation: landscape) { body { font-size: 14px; } }
5. 媒体查询的使用方式
5.1 在 CSS 文件中
直接在 CSS 文件中使用 @media:
@media (max-width: 768px) { body { background-color: lightyellow; } }
5.2 在 标签中
在 HTML 文件的 标签中使用:
@media (max-width: 768px) { body { background-color: lightyellow; } }
5.3 在 HTML 的 标签中
通过 media 属性为外部 CSS 文件指定条件:
6. 响应式设计中的常用断点
响应式设计中,为不同设备设置“断点”是常见做法。以下是常见设备的屏幕宽度断点:
设备类型 常用断点范围 超小屏幕(手机) max-width: 576px 小屏幕(平板) min-width: 577px 和 max-width: 768px 中等屏幕(桌面) min-width: 769px 和 max-width: 992px 大屏幕(大桌面) min-width: 993px 和 max-width: 1200px 超大屏幕 min-width: 1201px 示例:
/* 手机样式 */ @media (max-width: 576px) { body { font-size: 14px; } } /* 平板样式 */ @media (min-width: 577px) and (max-width: 768px) { body { font-size: 16px; } } /* 桌面样式 */ @media (min-width: 769px) { body { font-size: 18px; } }
7. 媒体查询的最佳实践
-
优先考虑移动优先设计(Mobile First)
- 先为小屏幕(如手机)编写样式,再为更大的屏幕覆盖样式。
- 使用 min-width 编写媒体查询:
body { font-size: 14px; /* 默认样式 */ } @media (min-width: 768px) { body { font-size: 16px; /* 平板样式 */ } } @media (min-width: 992px) { body { font-size: 18px; /* 桌面样式 */ } }
-
保持断点清晰
- 确保不同断点之间没有冲突,避免重复规则。
-
避免过多的媒体查询
- 尽量保持规则简单,并利用 flexbox、grid 等现代布局方法减少依赖媒体查询的需求。
-
测试多设备
- 使用浏览器的开发者工具测试不同的屏幕尺寸和方向,确保样式正常工作。
8. 示例:完整的响应式布局
/* 默认样式(移动优先) */ body { font-size: 14px; background-color: lightblue; } /* 平板设备 */ @media (min-width: 768px) { body { font-size: 16px; background-color: lightgreen; } } /* 桌面设备 */ @media (min-width: 992px) { body { font-size: 18px; background-color: lightcoral; } }
9. 新特性:容器查询(Container Queries)
容器查询是媒体查询的补充(CSS Container Queries),它允许根据容器的大小而不是视口大小来应用样式。虽然媒体查询以设备为基础,但容器查询更关注组件的自适应。
目前,容器查询还在发展中,部分现代浏览器已经支持。
实现
Pinia全局状态管理
管理当前设备类型(deviceType)、导航条是否需折叠(isCollapse)
Pinia
import { ref, computed } from 'vue' import { defineStore } from 'pinia' export const useDeviceStore=defineStore('device',{ state:()=>({ deviceType:"desktop" as deviceType, //初始默认值 isCollapse:false, // deviceType:DeviceTypeEnum.Desktop, }), actions:{ toggleDevice(type:deviceType){ this.deviceType=type; }, toggleCollapse(flag: boolean){ this.isCollapse=flag; } } })
监测设备变化
监测视口变化,改变全局状态(deviceType、isCollapse)
Vue2中借助mixin
import store from '@/store' const { body } = document const WIDTH = 992 // refer to Bootstrap's responsive design export default { watch: { $route(route) { if (this.device === 'mobile' && this.sidebar.opened) { store.dispatch('app/closeSideBar', { withoutAnimation: false }) } } }, beforeMount() { window.addEventListener('resize', this.$_resizeHandler) }, beforeDestroy() { window.removeEventListener('resize', this.$_resizeHandler) }, mounted() { const isMobile = this.$_isMobile() if (isMobile) { store.dispatch('app/toggleDevice', 'mobile') store.dispatch('app/closeSideBar', { withoutAnimation: true }) } }, methods: { // use $_ for mixins properties // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential $_isMobile() { const rect = body.getBoundingClientRect() return rect.width - 1
mixins: [ResizeMixin],
Vue3中借助组合式API
import { onMounted, onBeforeUnmount, watch } from 'vue'; import { useDeviceStore } from '@/stores/layout'; // 引入 Pinia store const WIDTH = 768; // 参考 Bootstrap 的响应式设计 //常见断点:576mobile\768iPad\992MidDesktop\1200LargeDeasktop\ export function useDevice() { const deviceStore = useDeviceStore(); // 使用 Pinia store const body = document.body; // 判断当前屏幕是否为移动端 const isMobile = () => { const rect = body.getBoundingClientRect(); return rect.width - 1 { if (!document.hidden) { const isMobileDevice = isMobile(); deviceStore.toggleDevice(isMobileDevice ? 'mobile' : 'desktop'); } }; // 在组件挂载时进行初始化 onMounted(() => { if (isMobile()) { deviceStore.toggleDevice('mobile'); deviceStore.toggleCollapse(true); } window.addEventListener('resize', resizeHandler); // 监听窗口大小变化 }); // 在组件销毁时移除事件监听 onBeforeUnmount(() => { window.removeEventListener('resize', resizeHandler); }); // 监听设备类型变化,移动设备时关闭侧边栏 watch(() => deviceStore.deviceType, (newDevice) => { if (newDevice === 'mobile' && !deviceStore.isCollapse) { deviceStore.toggleCollapse(true); }else { deviceStore.toggleCollapse(false); } }); return { isMobile, // 返回判断当前是否为移动设备的方法 }; }
import {useDevice} from '@/layout/mixin/ResizeHandler' useDevice();
媒体查询
不同设备下布局样式调整,调整导航条布局
设备为中小设备时,导航条就折叠且放在最右侧
LOGO///* 响应式布局 */ @media (min-width: 769px) { .logo { font-size: 24px; font-weight: bold; flex:5; } .nav{ background-color: transparent; border-bottom: none; flex: 3.5; } .info{ flex:1.5; margin-left: 20px; //background-color: white; } } @media (max-width: 768px) { .logo { font-size: 24px; font-weight: bold; flex:5; order:0//排序顺序,order属性 } .nav{ background-color: transparent; border-bottom: none; flex:1; //设备为中小设备时,导航条就折叠且放在最右侧! order:2 } .info{ flex:4; margin-left: 20px; order:1 } }
导航条状态绑定
导航条绑定全局状态(isCollapse)
import {useDeviceStore} from '@/stores/layout' import { watch } from 'vue' //根据当前窗口大小,判断是否折叠导航条 const deviceStore=useDeviceStore(); watch(()=>deviceStore.isCollapse,(newValue)=>{ console.log(newValue) })
-
- 用于满足任一条件即可生效。
- 用于排除某些条件。
- 用于同时满足多个条件。
-