前端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:定义项目的初始尺寸。
  • flexflex-growflex-shrinkflex-basis 的简写。
  • align-self:单个项目的交叉轴对齐。

Flex 布局属性总结

Flexbox(弹性盒子布局)提供了一套强大的布局工具,既适用于复杂的网页布局,又能处理响应式设计。其主要属性可以分为两类:

  • Flex 容器属性(控制容器的布局行为)
  • Flex 项目属性(控制容器内子元素的布局行为)
一、Flex 容器属性
  1. display

    • 定义容器的布局为 flexbox。
    • display: flex;display: inline-flex;(容器行为类似块级或行内元素)
    .container {
      display: flex;
    }
    
  2. flex-direction

    • 控制主轴(主方向)上项目的排列方向。
    • row(默认):水平,从左到右排列。
    • row-reverse:水平,从右到左排列。
    • column:垂直,从上到下排列。
    • column-reverse:垂直,从下到上排列。
    .container {
      flex-direction: row;
    }
    
  3. flex-wrap

    • 控制项目是否换行。
    • nowrap(默认):不换行,所有项目都在一行内。
    • wrap:换行,项目会换行并在多行中排列。
    • wrap-reverse:换行,但换行方向与 wrap 相反。
    .container {
      flex-wrap: wrap;
    }
    
  4. justify-content

    • 在主轴方向上对齐项目,控制项目之间的间隔。
    • flex-start(默认):项目靠容器的起始端对齐。
    • flex-end:项目靠容器的末尾对齐。
    • center:项目在主轴上居中对齐。
    • space-between:项目间的间隔相等,第一个项目靠近容器起始端,最后一个项目靠近容器末尾端。
    • space-around:项目之间的间隔相等,容器两侧的空白间隔也相等。
    • space-evenly:所有项目之间的间隔相等,包括容器两侧的空白。
    .container {
      justify-content: center;
    }
    
  5. align-items

    • 在交叉轴(垂直于主轴)上对齐项目。
    • flex-start:项目靠交叉轴起始端对齐。
    • flex-end:项目靠交叉轴末尾端对齐。
    • center:项目在交叉轴上居中对齐。
    • baseline:项目按文本基线对齐。
    • stretch(默认):项目拉伸以填充容器。
    .container {
      align-items: center;
    }
    
  6. align-content

    • 控制多行(有换行时)在交叉轴上的对齐方式。
    • flex-start:行靠交叉轴的起始位置对齐。
    • flex-end:行靠交叉轴的末尾位置对齐。
    • center:行在交叉轴上居中对齐。
    • space-between:行间隔相等,第一个和最后一个行与容器边缘对齐。
    • space-around:行间隔相等,行与容器之间也有间隔。
    • stretch(默认):行拉伸以填充容器。
    .container {
      align-content: space-between;
    }
    

二、Flex 项目属性
  1. order

    • 控制项目的排列顺序,数字越小,排列越靠前,默认为 0
    .item {
      order: 1;
    }
    
  2. flex-grow

    • 定义项目如何在主轴方向上扩展,以填充容器中剩余的空间。默认为 0,表示不扩展。
    • 如果所有项目的 flex-grow 值相同,空间会均等分配。
    .item {
      flex-grow: 1;
    }
    
  3. flex-shrink

    • 定义项目如何在容器空间不足时缩小。默认为 1,表示项目会缩小。
    • 如果设置为 0,则项目不会缩小。
    .item {
      flex-shrink: 0;
    }
    
  4. flex-basis

    • 定义项目在分配剩余空间之前的初始大小。默认值为 auto,即根据项目的内容或宽度来决定。
    • 可以设置为具体的像素值、百分比等。
    .item {
      flex-basis: 200px;
    }
    
  5. flex

    • flexflex-growflex-shrinkflex-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;
    }
    
  6. align-self

    • 控制单个项目在交叉轴上的对齐方式,覆盖 align-items 设置。
    • 可选值:autoflex-startflex-endcenterbaselinestretch
    .item {
      align-self: center;
    }
    

Flexbox 为响应式设计提供了非常强大的支持,尤其在复杂布局或动态调整时,能够通过这些属性轻松控制元素的排列、对齐和间距。

媒体查询

媒体查询详解

媒体查询(Media Queries)是 CSS3 提供的一种功能,用于根据设备的特性(如屏幕大小、分辨率、方向等)应用不同的样式,从而实现响应式设计。

媒体查询主要用于适配不同的设备,如桌面、平板、手机等。


1. 基本语法

媒体查询的基本语法如下:

@media media-type and (media-feature: value) {
  /* CSS规则 */
}
  • media-type:媒体类型,例如 screenprint
  • 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屏幕分辨率,例如 300dpi2dppx
prefers-color-scheme用户的首选颜色模式:lightdark

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 在 <style> 标签中

在 HTML 文件的 <style> 标签中使用:

<style>
  @media (max-width: 768px) {
    body {
      background-color: lightyellow;
    }
  }
</style>
5.3 在 HTML 的 <link> 标签中

通过 media 属性为外部 CSS 文件指定条件:

<link rel="stylesheet" href="style.css" media="screen and (max-width: 768px)">

6. 响应式设计中的常用断点

响应式设计中,为不同设备设置“断点”是常见做法。以下是常见设备的屏幕宽度断点:

设备类型常用断点范围
超小屏幕(手机)max-width: 576px
小屏幕(平板)min-width: 577pxmax-width: 768px
中等屏幕(桌面)min-width: 769pxmax-width: 992px
大屏幕(大桌面)min-width: 993pxmax-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. 媒体查询的最佳实践

  1. 优先考虑移动优先设计(Mobile First)

    • 先为小屏幕(如手机)编写样式,再为更大的屏幕覆盖样式。
    • 使用 min-width 编写媒体查询:
    body {
      font-size: 14px; /* 默认样式 */
    }
    
    @media (min-width: 768px) {
      body {
        font-size: 16px; /* 平板样式 */
      }
    }
    
    @media (min-width: 992px) {
      body {
        font-size: 18px; /* 桌面样式 */
      }
    }
    
  2. 保持断点清晰

    • 确保不同断点之间没有冲突,避免重复规则。
  3. 避免过多的媒体查询

    • 尽量保持规则简单,并利用 flexboxgrid 等现代布局方法减少依赖媒体查询的需求。
  4. 测试多设备

    • 使用浏览器的开发者工具测试不同的屏幕尺寸和方向,确保样式正常工作。

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 < WIDTH
    },
    $_resizeHandler() {
      if (!document.hidden) {
        const isMobile = this.$_isMobile()
        store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')

        if (isMobile) {
          store.dispatch('app/closeSideBar', { withoutAnimation: true })
        }
      }
    }
  }
}

  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 < WIDTH;
  };

  // 响应式处理函数
  const resizeHandler = () => {

    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,  // 返回判断当前是否为移动设备的方法
  };
}

<script setup lang="ts">
import {useDevice} from '@/layout/mixin/ResizeHandler'
useDevice();
</script>

媒体查询

不同设备下布局样式调整,调整导航条布局
设备为中小设备时,导航条就折叠且放在最右侧

<div id="app">
    <!-- 顶部导航栏 -->
    <el-header class="header">
      <div class="logo">LOGO</div>
      <div class="nav">
        <Nav></Nav>
      </div>
      <div class="info">
        <el-row :gutter="20">
          <el-col :span="8">
              <theme></theme>
          </el-col>
          <el-col :span="8">
            <el-icon :size="40">
                <Git></Git>
            </el-icon>
          </el-col>
          <el-col :span="8">
            <Translation></Translation>
          </el-col>
        </el-row>
      </div>
    </el-header>

    <!-- 内容区域 -->
    <el-main class="main">
      <div class="content">
        <!-- 动态显示内容 -->
          <RouterView></RouterView>
      </div>
    </el-main>
  </div>
///* 响应式布局 */
@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)

<template>
  <el-menu
    class="menu"
    mode="horizontal"
    :popper-offset="16"
    style="max-width: 600px"
    :collapse="deviceStore.isCollapse"
  >
  </el-menu>
  <script setup lang="ts">
import {useDeviceStore} from '@/stores/layout'
import { watch } from 'vue'
//根据当前窗口大小,判断是否折叠导航条
const deviceStore=useDeviceStore();
watch(()=>deviceStore.isCollapse,(newValue)=>{
  console.log(newValue)
})
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GISer_Jinger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值