Day 18: 响应式设计 – 适配所有设备
🎯 学习目标
- 理解响应式设计的核心概念和重要性
- 掌握视口(Viewport)和媒体查询的使用方法
- 学会使用相对单位和弹性布局
- 掌握移动优先的设计策略
- 能够创建适配手机、平板、桌面的网页
💡 核心概念
什么是响应式设计?
响应式设计(Responsive Design) 是一种网页设计方法,使网页能够根据用户设备的屏幕尺寸、方向和平台自动调整布局和内容显示,提供最佳的用户体验。
为什么需要响应式设计?
设备多样性:
- 📱 手机(320px – 768px)
- 📱 平板(768px – 1024px)
- 💻 桌面(1024px+)
- 🖥️ 大屏显示器(1440px+)
用户体验需求:
- 不需要缩放和横向滚动
- 内容可读性良好
- 触控目标足够大
- 加载速度快
响应式设计的三大支柱
-
弹性网格布局(Fluid Grid)
- 使用相对单位(%、em、rem)
- 布局自适应容器大小
-
弹性图像和媒体(Flexible Images)
- 图像自适应容器
- 使用
max-width: 100%
-
媒体查询(Media Queries)
- 根据设备特性应用不同样式
- 断点(Breakpoints)设计
📝 视口(Viewport)设置
视口是什么?
视口是用户在设备上可见的网页区域。移动设备的视口通常比桌面小,因此需要正确配置。
视口 meta 标签
必须添加在 HTML 的 <head> 中:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
参数解释:
| 参数 | 说明 | 推荐值 |
|---|---|---|
width |
设置视口宽度 | device-width(设备宽度) |
initial-scale |
初始缩放比例 | 1.0(不缩放) |
minimum-scale |
最小缩放比例 | 1.0 |
maximum-scale |
最大缩放比例 | 1.0(可选) |
user-scalable |
是否允许用户缩放 | no(可选) |
完整示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<!-- ⚠️ 必须添加视口配置 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式设计示例</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- 页面内容 -->
</body>
</html>
视口未设置的影响
没有视口配置时:
- 移动设备会使用桌面视口宽度(通常 980px)
- 内容被缩小显示,用户需要手动缩放
- 文字太小,链接难以点击
设置视口后:
- 页面使用设备实际宽度
- 布局自动适配
- 更好的可读性和交互性
📐 媒体查询(Media Queries)
基本语法
@media screen and (min-width: 768px) {
/* 当视口宽度 >= 768px 时应用这些样式 */
body {
font-size: 18px;
}
}
常用媒体类型
| 类型 | 说明 |
|---|---|
all |
所有设备(默认) |
screen |
屏幕设备(电脑、平板、手机) |
print |
打印预览 |
speech |
屏幕阅读器 |
媒体特性
| 特性 | 说明 | 示例值 |
|---|---|---|
width |
视口宽度 | 768px |
min-width |
最小宽度 | 768px |
max-width |
最大宽度 | 1024px |
height |
视口高度 | 600px |
orientation |
设备方向 | portrait(竖屏)或 landscape(横屏) |
resolution |
设备分辨率 | 2dppx(Retina) |
逻辑操作符
/* and:同时满足多个条件 */
@media screen and (min-width: 768px) and (max-width: 1024px) {
/* 平板设备样式 */
}
/* ,(逗号):或(满足任一条件) */
@media screen, (min-width: 768px) {
/* 屏幕设备或宽度 >= 768px */
}
/* not:否定 */
@media not print {
/* 非打印设备 */
}
/* only:防止旧浏览器不识别媒体查询 */
@media only screen and (min-width: 768px) {
/* 现代浏览器 */
}
常用断点(Breakpoints)
移动优先(Mobile First):
/* 基础样式:手机(默认) */
.container {
width: 100%;
padding: 1rem;
}
/* 平板(>= 768px) */
@media screen and (min-width: 768px) {
.container {
width: 750px;
margin: 0 auto;
}
}
/* 桌面(>= 1024px) */
@media screen and (min-width: 1024px) {
.container {
width: 960px;
}
}
/* 大屏(>= 1440px) */
@media screen and (min-width: 1440px) {
.container {
width: 1200px;
}
}
桌面优先(Desktop First):
/* 基础样式:桌面 */
.container {
width: 1200px;
margin: 0 auto;
}
/* 平板和手机 */
@media screen and (max-width: 1023px) {
.container {
width: 100%;
padding: 1rem;
}
}
🎨 相对单位
为什么使用相对单位?
响应式设计需要灵活的尺寸,相对单位可以根据父元素或视口大小自动调整。
常用相对单位
| 单位 | 相对于 | 适用场景 |
|---|---|---|
% |
父元素 | 宽度、高度、边距 |
em |
元素字体大小 | 字体、间距、组件大小 |
rem |
根元素字体大小 | 全局字体、布局 |
vw |
视口宽度的 1% | 全屏元素、响应式字体 |
vh |
视口高度的 1% | 全屏元素 |
vmin |
视口宽度和高度中较小的 1% | 全屏元素 |
vmax |
视口宽度和高度中较大的 1% | 全屏元素 |
示例:相对单位
/* 百分比 */
.column {
width: 50%; /* 父元素宽度的一半 */
padding: 5%; /* 父元素宽度的 5% */
}
/* em(相对于当前元素字体大小) */
.box {
font-size: 16px;
padding: 1em; /* 16px */
margin: 0.5em; /* 8px */
}
/* rem(相对于根元素字体大小) */
html {
font-size: 16px; /* 根元素 */
}
h1 {
font-size: 2rem; /* 32px */
margin-bottom: 1rem; /* 16px */
}
/* 视口单位 */
.hero {
width: 100vw; /* 视口宽度 */
height: 100vh; /* 视口高度 */
font-size: 4vw; /* 视口宽度的 4% */
}
rem vs em 的选择
rem 的优势:
- 统一的基准,便于计算
- 修改根字体大小可以全局缩放
- 适合布局和组件
em 的优势:
- 组件内部更灵活
- 适合与字体相关的属性(如行高、段落间距)
推荐用法:
/* 使用 rem 设置全局布局 */
.container {
padding: 1.5rem; /* 24px */
margin: 2rem; /* 32px */
}
/* 使用 em 设置组件内部 */
.card {
padding: 1.5em; /* 相对于卡片字体大小 */
}
🎮 响应式图片
最大宽度技巧
img {
max-width: 100%; /* 图像不超过容器宽度 */
height: auto; /* 保持宽高比 */
}
响应式图片(srcset)
<img src="image-800.jpg"
srcset="image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
33vw"
alt="响应式图片">
参数解释:
srcset:提供不同尺寸的图片400w、800w:图片的实际宽度sizes:告诉浏览器图片在不同视口宽度下的显示尺寸100vw:视口宽度的 100%
Picture 元素(艺术指导)
<picture>
<!-- 手机:纵向裁剪图片 -->
<source media="(max-width: 600px)"
srcset="image-portrait.jpg">
<!-- 桌面:完整图片 -->
<source media="(min-width: 601px)"
srcset="image-landscape.jpg">
<!-- 降级方案 -->
<img src="image-default.jpg" alt="响应式图片">
</picture>
🔄 移动优先策略
什么是移动优先?
**移动优先(Mobile First)**是指先设计移动设备的布局,然后逐步增强到平板和桌面。
优势
- 📱 性能优先:移动设备性能较弱,先考虑性能
- 🎯 内容优先:屏幕小,强迫你关注核心内容
- 🚀 渐进增强:基础功能在所有设备可用,高级设备获得更好体验
- 💻 开发效率:从简单到复杂,更容易维护
实现方式
1. 默认样式(手机)
/* 手机布局:单列 */
.container {
padding: 1rem;
}
.header {
text-align: center;
}
.nav {
display: flex;
flex-direction: column; /* 垂直排列 */
}
.card {
width: 100%;
margin-bottom: 1rem;
}
2. 平板增强(>= 768px)
@media screen and (min-width: 768px) {
.container {
max-width: 750px;
margin: 0 auto;
}
.nav {
flex-direction: row; /* 水平排列 */
justify-content: space-between;
}
.card {
width: calc(50% - 1rem);
display: inline-block;
}
}
3. 桌面增强(>= 1024px)
@media screen and (min-width: 1024px) {
.container {
max-width: 960px;
}
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
}
🎯 实战示例
示例 1:响应式导航栏
HTML:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式导航栏</title>
<link rel="stylesheet" href="responsive-nav.css">
</head>
<body>
<nav class="navbar">
<div class="nav-container">
<div class="nav-logo">我的网站</div>
<button class="nav-toggle" id="navToggle">
<span></span>
<span></span>
<span></span>
</button>
<ul class="nav-menu" id="navMenu">
<li><a href="#home">首页</a></li>
<li><a href="#about">关于</a></li>
<li><a href="#services">服务</a></li>
<li><a href="#contact">联系</a></li>
</ul>
</div>
</nav>
<script src="responsive-nav.js"></script>
</body>
</html>
CSS:
/* 重置样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
line-height: 1.6;
}
/* 导航栏基础样式 */
.navbar {
background-color: #2c3e50;
color: white;
padding: 1rem 0;
position: sticky;
top: 0;
z-index: 1000;
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-logo {
font-size: 1.5rem;
font-weight: bold;
}
/* 手机菜单(默认) */
.nav-menu {
list-style: none;
display: flex;
flex-direction: column; /* 垂直排列 */
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: #34495e;
flex-direction: column;
max-height: 0; /* 默认隐藏 */
overflow: hidden;
transition: max-height 0.3s ease;
}
.nav-menu.active {
max-height: 500px; /* 显示菜单 */
}
.nav-menu li {
border-bottom: 1px solid #455a64;
}
.nav-menu a {
display: block;
padding: 1rem;
color: white;
text-decoration: none;
transition: background-color 0.3s;
}
.nav-menu a:hover {
background-color: #1a252f;
}
/* 汉堡菜单按钮 */
.nav-toggle {
display: block; /* 手机显示 */
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
}
.nav-toggle span {
display: block;
width: 25px;
height: 3px;
background-color: white;
margin: 5px 0;
transition: all 0.3s;
}
/* 汉堡菜单动画 */
.nav-toggle.active span:nth-child(1) {
transform: rotate(45deg) translate(8px, 8px);
}
.nav-toggle.active span:nth-child(2) {
opacity: 0;
}
.nav-toggle.active span:nth-child(3) {
transform: rotate(-45deg) translate(7px, -7px);
}
/* 平板和桌面(>= 768px) */
@media screen and (min-width: 768px) {
.nav-toggle {
display: none; /* 隐藏汉堡菜单 */
}
.nav-menu {
position: static;
flex-direction: row; /* 水平排列 */
background-color: transparent;
max-height: none;
overflow: visible;
}
.nav-menu li {
border-bottom: none;
margin-left: 2rem;
}
.nav-menu a {
padding: 0.5rem 0;
}
}
JavaScript:
// 获取元素
const navToggle = document.getElementById('navToggle');
const navMenu = document.getElementById('navMenu');
// 切换菜单
navToggle.addEventListener('click', () => {
navToggle.classList.toggle('active');
navMenu.classList.toggle('active');
});
// 点击菜单项后关闭菜单(手机)
document.querySelectorAll('.nav-menu a').forEach(link => {
link.addEventListener('click', () => {
navToggle.classList.remove('active');
navMenu.classList.remove('active');
});
});
示例 2:响应式卡片网格
HTML:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式卡片网格</title>
<link rel="stylesheet" href="responsive-grid.css">
</head>
<body>
<div class="container">
<h1>响应式卡片网格</h1>
<div class="card-grid">
<div class="card">
<img src="https://via.placeholder.com/400x200" alt="图片 1">
<h3>卡片 1</h3>
<p>这是卡片的内容描述...</p>
<a href="#" class="btn">了解更多</a>
</div>
<div class="card">
<img src="https://via.placeholder.com/400x200" alt="图片 2">
<h3>卡片 2</h3>
<p>这是卡片的内容描述...</p>
<a href="#" class="btn">了解更多</a>
</div>
<div class="card">
<img src="https://via.placeholder.com/400x200" alt="图片 3">
<h3>卡片 3</h3>
<p>这是卡片的内容描述...</p>
<a href="#" class="btn">了解更多</a>
</div>
<div class="card">
<img src="https://via.placeholder.com/400x200" alt="图片 4">
<h3>卡片 4</h3>
<p>这是卡片的内容描述...</p>
<a href="#" class="btn">了解更多</a>
</div>
</div>
</div>
</body>
</html>
CSS:
/* 容器 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem 1rem;
}
/* 标题 */
h1 {
text-align: center;
margin-bottom: 2rem;
font-size: 2rem;
}
/* 卡片网格 */
.card-grid {
display: grid;
gap: 1.5rem;
/* 手机:1 列 */
grid-template-columns: 1fr;
}
/* 卡片 */
.card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}
/* 卡片图片 */
.card img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
/* 卡片内容 */
.card h3 {
padding: 1rem 1rem 0.5rem;
font-size: 1.25rem;
}
.card p {
padding: 0 1rem 1rem;
color: #666;
}
.card .btn {
display: inline-block;
margin: 0 1rem 1rem;
padding: 0.5rem 1rem;
background-color: #3498db;
color: white;
text-decoration: none;
border-radius: 4px;
transition: background-color 0.3s;
}
.card .btn:hover {
background-color: #2980b9;
}
/* 小平板(>= 480px):2 列 */
@media screen and (min-width: 480px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 平板(>= 768px):保持 2 列,调整间距 */
@media screen and (min-width: 768px) {
.container {
padding: 3rem 2rem;
}
h1 {
font-size: 2.5rem;
}
}
/* 桌面(>= 1024px):3 列 */
@media screen and (min-width: 1024px) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
}
/* 大屏(>= 1440px):4 列 */
@media screen and (min-width: 1440px) {
.card-grid {
grid-template-columns: repeat(4, 1fr);
}
}
示例 3:响应式英雄区域
HTML:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式英雄区域</title>
<link rel="stylesheet" href="responsive-hero.css">
</head>
<body>
<section class="hero">
<div class="hero-content">
<h1 class="hero-title">欢迎来到我的网站</h1>
<p class="hero-subtitle">打造完美的响应式设计</p>
<div class="hero-buttons">
<a href="#signup" class="btn btn-primary">立即开始</a>
<a href="#learn" class="btn btn-secondary">了解更多</a>
</div>
</div>
</section>
</body>
</html>
CSS:
/* 英雄区域 */
.hero {
/* 使用背景图片 */
background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)),
url('https://images.unsplash.com/photo-1497366216548-37526070297c?w=1920');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
/* 高度:视口高度 */
height: 100vh;
/* 居中内容 */
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
text-align: center;
}
/* 英雄内容 */
.hero-content {
color: white;
max-width: 600px;
}
/* 标题 */
.hero-title {
font-size: 2rem;
margin-bottom: 1rem;
line-height: 1.2;
}
/* 副标题 */
.hero-subtitle {
font-size: 1rem;
margin-bottom: 2rem;
opacity: 0.9;
}
/* 按钮容器 */
.hero-buttons {
display: flex;
flex-direction: column; /* 手机:垂直排列 */
gap: 1rem;
}
/* 按钮 */
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
transition: all 0.3s;
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
transform: scale(1.05);
}
.btn-secondary {
background-color: transparent;
color: white;
border: 2px solid white;
}
.btn-secondary:hover {
background-color: white;
color: #333;
}
/* 小平板(>= 480px) */
@media screen and (min-width: 480px) {
.hero-title {
font-size: 2.5rem;
}
.hero-subtitle {
font-size: 1.25rem;
}
}
/* 平板(>= 768px) */
@media screen and (min-width: 768px) {
.hero-title {
font-size: 3rem;
}
.hero-subtitle {
font-size: 1.5rem;
}
.hero-buttons {
flex-direction: row; /* 水平排列 */
justify-content: center;
}
.btn {
padding: 1rem 2rem;
font-size: 1.1rem;
}
}
/* 桌面(>= 1024px) */
@media screen and (min-width: 1024px) {
.hero-title {
font-size: 4rem;
}
.hero-subtitle {
font-size: 1.75rem;
}
}
/* 横屏优化 */
@media screen and (max-width: 1024px) and (orientation: landscape) {
.hero {
height: auto; /* 不使用视口高度 */
min-height: 100vh;
padding: 2rem;
}
}
⚠️ 重要注意事项
1. 视口配置必须正确
<!-- ✅ 正确 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ❌ 错误:忘记添加视口配置 -->
<!-- 会导致移动设备显示异常 -->
2. 移动优先 vs 桌面优先
移动优先(推荐):
/* 默认:手机样式 */
.container {
width: 100%;
}
@media (min-width: 768px) {
/* 增强到平板和桌面 */
.container {
max-width: 750px;
}
}
桌面第一(不推荐):
/* 默认:桌面样式 */
.container {
width: 1200px;
}
@media (max-width: 767px) {
/* 降级到手机 */
.container {
width: 100%;
}
}
3. 避免固定宽度
/* ❌ 错误:固定宽度 */
.container {
width: 1200px;
}
/* ✅ 正确:使用最大宽度 */
.container {
max-width: 1200px;
width: 100%;
}
4. 使用相对单位
/* ❌ 错误:固定字体大小 */
p {
font-size: 16px;
}
/* ✅ 正确:使用 rem */
html {
font-size: 16px;
}
p {
font-size: 1rem;
}
5. 测试多种设备
- 📱 手机(iPhone SE、iPhone 12 Pro)
- 📱 平板(iPad)
- 💻 桌面(1920×1080)
- 🖥️ 大屏(2560×1440)
6. 性能优化
移动设备性能考虑:
- 压缩图片
- 减少 HTTP 请求
- 使用 CSS 而非 JavaScript
- 懒加载图片和视频
✍️ 练习任务
基础练习
-
响应式文本
- 创建一个页面,标题在不同设备上使用不同字体大小
- 手机:2rem,平板:3rem,桌面:4rem
- 使用媒体查询实现
-
响应式图片
- 创建一个图片网格
- 手机:1 列,平板:2 列,桌面:3 列
- 图片自适应容器宽度
进阶练习
-
响应式表单
- 创建一个联系表单
- 手机:单列布局
- 桌面:双列布局(名字和邮箱并排)
- 添加焦点状态和错误提示
-
响应式导航栏
- 创建一个导航栏
- 手机:汉堡菜单
- 平板和桌面:水平菜单
- 添加 JavaScript 交互
实战练习
-
个人作品集页面
- 创建一个完整的响应式作品集页面
- 包含:导航栏、英雄区域、作品展示、联系表单
- 在手机、平板、桌面都有良好的显示效果
-
电商产品卡片
- 创建产品卡片网格
- 包含:图片、标题、价格、购买按钮
- 适配 1-4 列布局
- 添加悬停效果
💡 常见问题 FAQ
Q1: 移动优先和桌面优先有什么区别?
A:
移动优先:
- ✅ 性能更好
- ✅ 内容更聚焦
- ✅ 开发更简单
- ✅ 现代最佳实践
桌面优先:
- ❌ 性能较差
- ❌ 可能过度设计
- ❌ 代码更复杂
- ⚠️ 适合已有桌面网站改造
Q2: 如何选择断点?
A: 使用常见设备宽度作为断点:
| 设备 | 宽度 | 断点 |
|---|---|---|
| 小手机 | 320px – 480px | min-width: 480px |
| 大手机 | 480px – 768px | min-width: 768px |
| 平板 | 768px – 1024px | min-width: 1024px |
| 桌面 | 1024px+ | min-width: 1440px |
| 大屏 | 1440px+ | – |
避免使用特定设备尺寸(如 iPhone 的 375px),使用通用断点更灵活。
Q3: px 和 rem 哪个更好?
A:
px 的优势:
- 精确控制
- 兼容性好
rem 的优势:
- 响应式更好
- 便于全局缩放
- 无障碍访问(用户可调整根字体大小)
推荐:
/* 基础字体使用 px(精确控制) */
html {
font-size: 16px;
}
/* 布局使用 rem(响应式) */
.container {
padding: 1.5rem; /* 24px */
}
Q4: 如何测试响应式设计?
A:
浏览器开发者工具:
- 按
F12打开开发者工具 - 点击设备图标(Ctrl+Shift+M / Cmd+Shift+M)
- 选择不同设备或自定义尺寸
真实设备测试:
- 使用多台设备访问
- 使用 Chrome DevTools 的远程调试
- 使用 BrowserStack 或 LambdaTest
Q5: 响应式和自适应有什么区别?
A:
响应式(Responsive):
- 使用流式布局
- 使用媒体查询
- 平滑过渡,适配任意尺寸
自适应(Adaptive):
- 固定断点
- 每个断点使用不同布局
- 跳跃式变化
推荐使用响应式设计,更灵活。
Q6: 如何优化移动设备性能?
A:
图片优化:
<!-- 使用 srcset 提供不同尺寸 -->
<img src="image-small.jpg"
srcset="image-small.jpg 400w,
image-medium.jpg 800w,
image-large.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw">
CSS 优化:
/* 避免复杂选择器 */
.card .title .text {
/* ❌ 深层嵌套 */
}
.card-title {
/* ✅ 扁平化 */
}
JavaScript 优化:
- 延迟加载非关键 JavaScript
- 使用事件委托
- 防抖和节流
📚 拓展阅读
相关概念
-
弹性盒子(Flexbox)
- 一维布局系统
- 适合导航栏、按钮组
-
网格布局(Grid)
- 二维布局系统
- 适合复杂布局
-
容器查询(Container Queries)
- 基于容器尺寸而非视口尺寸
- 更细粒度的响应式控制
推荐阅读
🎯 总结
核心要点
- 视口配置:响应式设计的基础
- 媒体查询:根据设备应用不同样式
- 相对单位:灵活的尺寸系统
- 移动优先:从简单到复杂的设计策略
- 性能优化:移动设备更需要关注性能
响应式设计检查清单
- [ ] 添加视口 meta 标签
- [ ] 使用媒体查询
- [ ] 图片自适应(max-width: 100%)
- [ ] 使用相对单位(rem、em、%)
- [ ] 测试多种设备
- [ ] 优化性能
- [ ] 确保触控目标足够大(至少 44x44px)
- [ ] 确保文字可读(最小 16px)
下一步学习
- Day 19: 项目 1 – 待办事项应用(整合所有 DOM 操作知识)
- Day 20: 项目 2 – 计时器应用(深入学习定时器和间隔)
- Day 21: 项目 3 – 猜数字游戏(游戏逻辑和用户交互)
Happy Coding! 🚀
创建完美适配所有设备的响应式网页!