Files
official_network/docs/响应式设计指南.md
张成 c4148d9eb6 1
2025-08-25 15:24:23 +08:00

11 KiB

响应式设计指南 - 不使用媒体查询

概述

本指南介绍如何使用 JavaScript 检测屏幕尺寸来替代传统的 CSS 媒体查询,实现更灵活、更可控的响应式设计。

解决方案架构

1. CSS 容器查询 (Container Queries)

  • 文件: src/assets/css/responsive_without_media.css
  • 优势: 最新的 CSS 标准,基于容器而非视口
  • 兼容性: 现代浏览器支持

2. JavaScript 检测 + 类名控制

  • 文件: src/static/responsive_helper.js
  • 优势: 完全兼容,可编程控制
  • 兼容性: 所有浏览器支持

3. Vue2 组件化响应式

  • 文件: src/components/ResponsiveLayout.vue
  • 优势: 组件化,易于维护
  • 兼容性: Vue2 项目

使用方法

基础使用

1. 引入响应式助手

<!-- 在 index.html 中引入 -->
<script src="/static/responsive_helper.js"></script>

2. 使用响应式布局组件

<template>
  <ResponsiveLayout :showDebug="true" @screenSizeChange="handleScreenChange">
    <div class="page-content">
      <!-- 你的内容 -->
    </div>
  </ResponsiveLayout>
</template>

<script>
import ResponsiveLayout from '@/components/ResponsiveLayout.vue';

export default {
  components: { ResponsiveLayout },
  methods: {
    handleScreenChange(event) {
      console.log('屏幕尺寸变化:', event.detail);
      // 根据屏幕尺寸执行不同逻辑
    }
  }
};
</script>

3. 使用响应式 CSS 类

<!-- 根据屏幕尺寸显示/隐藏 -->
<div class="responsive-hidden-xs">超小屏幕隐藏</div>
<div class="responsive-hidden-sm">小屏幕隐藏</div>
<div class="responsive-hidden-md">中等屏幕隐藏</div>
<div class="responsive-hidden-lg">大屏幕隐藏</div>
<div class="responsive-hidden-xl">超大屏幕隐藏</div>

<!-- 根据屏幕尺寸显示 -->
<div class="responsive-visible-xs">超小屏幕显示</div>
<div class="responsive-visible-sm">小屏幕显示</div>
<div class="responsive-visible-md">中等屏幕显示</div>
<div class="responsive-visible-lg">大屏幕显示</div>
<div class="responsive-visible-xl">超大屏幕显示</div>

高级使用

1. 自定义断点

<template>
  <ResponsiveLayout :customBreakpoints="customBreakpoints">
    <!-- 内容 -->
  </ResponsiveLayout>
</template>

<script>
export default {
  data() {
    return {
      customBreakpoints: {
        xs: 320,    // 超小屏幕
        sm: 576,    // 小屏幕
        md: 768,    // 中等屏幕
        lg: 992,    // 大屏幕
        xl: 1200,   // 超大屏幕
        xxl: 1400   // 超超大屏幕
      }
    };
  }
};
</script>

2. 响应式内容控制

<template>
  <div>
    <!-- 移动端内容 -->
    <div v-if="isMobile" class="mobile-content">
      <h2>移动端优化内容</h2>
      <p>触摸友好的界面设计</p>
    </div>
    
    <!-- 桌面端内容 -->
    <div v-if="isDesktop" class="desktop-content">
      <h2>桌面端完整内容</h2>
      <p>鼠标和键盘友好的界面</p>
    </div>
    
    <!-- 平板端内容 -->
    <div v-if="isTablet" class="tablet-content">
      <h2>平板端适配内容</h2>
      <p>触摸和鼠标混合操作</p>
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    isMobile() {
      return this.$refs.responsiveLayout?.isMobile || false;
    },
    isTablet() {
      return this.$refs.responsiveLayout?.isTablet || false;
    },
    isDesktop() {
      return this.$refs.responsiveLayout?.isDesktop || false;
    }
  }
};
</script>

3. 动态样式调整

<template>
  <div :class="dynamicClasses">
    <button @click="adjustLayout">调整布局</button>
  </div>
</template>

<script>
export default {
  computed: {
    dynamicClasses() {
      const classes = ['base-layout'];
      
      if (this.$refs.responsiveLayout) {
        const size = this.$refs.responsiveLayout.currentScreenSize;
        classes.push(`layout-${size}`);
        
        if (this.$refs.responsiveLayout.isMobile) {
          classes.push('mobile-optimized');
        }
      }
      
      return classes;
    }
  },
  methods: {
    adjustLayout() {
      const element = this.$el.querySelector('.content');
      if (this.$refs.responsiveLayout) {
        // 根据断点显示/隐藏元素
        this.$refs.responsiveLayout.responsiveShow(element, 'md');
      }
    }
  }
};
</script>

CSS 类名系统

响应式断点类名

/* 屏幕尺寸类名 */
.screen-xs { /* 超小屏幕样式 */ }
.screen-sm { /* 小屏幕样式 */ }
.screen-md { /* 中等屏幕样式 */ }
.screen-lg { /* 大屏幕样式 */ }
.screen-xl { /* 超大屏幕样式 */ }
.screen-xxl { /* 超超大屏幕样式 */ }

/* 容器尺寸类名 */
.container-xs { /* 超小容器样式 */ }
.container-sm { /* 小容器样式 */ }
.container-md { /* 中等容器样式 */ }
.container-lg { /* 大容器样式 */ }
.container-xl { /* 超大容器样式 */ }
.container-xxl { /* 超超大容器样式 */ }

响应式工具类

/* 显示/隐藏 */
.responsive-hidden-xs { display: none !important; }
.responsive-visible-xs { display: block !important; }

/* 布局 */
.responsive-grid { display: grid !important; }
.responsive-flex { display: flex !important; }
.responsive-block { display: block !important; }

/* 间距 */
.responsive-m-0 { margin: 0 !important; }
.responsive-p-3 { padding: 1rem !important; }

/* 文本 */
.responsive-text-center { text-align: center; }
.responsive-text-primary { color: #3498db; }

/* 阴影 */
.responsive-shadow { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); }
.responsive-shadow-lg { box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); }

JavaScript API

全局方法

// 响应式显示元素
window.responsiveShow(element, breakpoint);

// 响应式隐藏元素
window.responsiveHide(element, breakpoint);

// 响应式切换类名
window.responsiveToggleClass(element, className, breakpoint);

// 获取当前屏幕信息
const screenInfo = window.responsiveHelper.getCurrentScreenInfo();

事件监听

// 监听屏幕尺寸变化
window.addEventListener('screenSizeChange', (event) => {
  const { size, width, height, breakpoint } = event.detail;
  
  if (size === 'xs' || size === 'sm' || size === 'md') {
    // 移动端逻辑
    console.log('切换到移动端');
  } else if (size === 'lg') {
    // 平板端逻辑
    console.log('切换到平板端');
  } else {
    // 桌面端逻辑
    console.log('切换到桌面端');
  }
});

断点检测

// 检查是否匹配特定断点
const isMobile = window.responsiveHelper.matchesBreakpoint('md');
const isTablet = window.responsiveHelper.matchesBreakpoint('lg');
const isDesktop = !window.responsiveHelper.matchesBreakpoint('lg');

最佳实践

1. 性能优化

// 使用防抖优化 resize 事件
let resizeTimeout;
window.addEventListener('resize', () => {
  clearTimeout(resizeTimeout);
  resizeTimeout = setTimeout(() => {
    // 执行响应式逻辑
  }, 100);
});

2. 渐进增强

// 检测功能支持
if (CSS.supports('container-type', 'inline-size')) {
  // 使用容器查询
  console.log('支持容器查询');
} else {
  // 回退到 JavaScript 检测
  console.log('使用 JavaScript 检测');
}

3. 组件化设计

<!-- 创建可复用的响应式组件 -->
<template>
  <div :class="responsiveClasses">
    <slot name="mobile" v-if="isMobile"></slot>
    <slot name="tablet" v-else-if="isTablet"></slot>
    <slot name="desktop" v-else></slot>
    <slot v-if="!isMobile && !isTablet && !isDesktop"></slot>
  </div>
</template>

<script>
export default {
  name: 'ResponsiveSlot',
  inject: ['responsiveLayout'],
  computed: {
    isMobile() {
      return this.responsiveLayout?.isMobile || false;
    },
    isTablet() {
      return this.responsiveLayout?.isTablet || false;
    },
    isDesktop() {
      return this.responsiveLayout?.isDesktop || false;
    },
    responsiveClasses() {
      return {
        'responsive-slot': true,
        [`screen-${this.responsiveLayout?.currentScreenSize}`]: true
      };
    }
  }
};
</script>

4. 主题切换

// 根据屏幕尺寸切换主题
function switchThemeByScreen() {
  const screenInfo = window.responsiveHelper.getCurrentScreenInfo();
  
  if (screenInfo.isMobile) {
    document.body.classList.add('theme-mobile');
    document.body.classList.remove('theme-desktop');
  } else {
    document.body.classList.add('theme-desktop');
    document.body.classList.remove('theme-mobile');
  }
}

// 监听屏幕变化
window.addEventListener('screenSizeChange', switchThemeByScreen);

迁移指南

从媒体查询迁移

原来的 CSS

@media (max-width: 768px) {
  .mobile-menu {
    display: block;
  }
  .desktop-menu {
    display: none;
  }
}

新的 JavaScript 方式

// 在组件中
mounted() {
  this.updateMenuVisibility();
  window.addEventListener('screenSizeChange', this.updateMenuVisibility);
},
methods: {
  updateMenuVisibility() {
    const mobileMenu = this.$el.querySelector('.mobile-menu');
    const desktopMenu = this.$el.querySelector('.desktop-menu');
    
    if (this.$refs.responsiveLayout.isMobile) {
      mobileMenu.style.display = 'block';
      desktopMenu.style.display = 'none';
    } else {
      mobileMenu.style.display = 'none';
      desktopMenu.style.display = 'block';
    }
  }
}

新的 CSS 类方式

/* 使用响应式类名 */
.mobile-menu {
  display: none;
}

.desktop-menu {
  display: block;
}

/* 移动端显示 */
.screen-xs .mobile-menu,
.screen-sm .mobile-menu,
.screen-md .mobile-menu {
  display: block;
}

.screen-xs .desktop-menu,
.screen-sm .desktop-menu,
.screen-md .desktop-menu {
  display: none;
}

调试和测试

1. 启用调试模式

<ResponsiveLayout :showDebug="true">
  <!-- 内容 -->
</ResponsiveLayout>

2. 控制台调试

// 查看当前屏幕信息
console.log(window.responsiveHelper.getCurrentScreenInfo());

// 手动触发屏幕变化
window.dispatchEvent(new Event('resize'));

3. 测试不同屏幕尺寸

// 模拟不同屏幕尺寸
function simulateScreenSize(width) {
  Object.defineProperty(window, 'innerWidth', {
    writable: true,
    configurable: true,
    value: width
  });
  
  window.dispatchEvent(new Event('resize'));
}

// 测试移动端
simulateScreenSize(375);

// 测试平板端
simulateScreenSize(768);

// 测试桌面端
simulateScreenSize(1200);

总结

通过使用 JavaScript 检测屏幕尺寸替代媒体查询,我们获得了:

  1. 更好的控制性: 可以编程控制响应式行为
  2. 更高的灵活性: 支持复杂的响应式逻辑
  3. 更好的性能: 避免 CSS 重排和重绘
  4. 更好的维护性: 集中管理响应式逻辑
  5. 更好的兼容性: 支持所有浏览器

这种方案特别适合需要复杂响应式逻辑的现代 Web 应用。