# 第4次课 讲稿

> 课程：小程序开发
> 主题：WXSS样式与Flex布局
> 教材：《微信小程序开发实战（第2版）》第2章
> 时长：3学时（理论 1 + 实践 2）
> 重点：WXSS语法、rpx单位、Flex布局、样式导入
> 难点：Flex布局的轴方向与对齐方式

---

## 一、开场（0:00）

上次课我们让页面"有内容"了，今天要让它"好看"。

先说一个大家马上会遇到的问题：同样一个按钮，在我的手机上宽度正好，到了同桌的手机上就顶出屏幕了。为什么？因为手机屏幕宽度不一样，而你用的是 px 这种固定单位。

小程序给了一个解法叫 rpx。规定很简单：**不管什么手机，屏幕宽度一律当成 750rpx**。你按 750 的比例去画，框架帮你换算。

今天另一半时间讲 Flex 布局。这个东西讲概念很枯燥，我建议大家把它当成一套"排队规则"来记：先决定队伍朝哪个方向排，再决定队伍在这个方向上怎么对齐，最后决定队伍在垂直方向上怎么对齐。三步，就这么多。

---

## 二、明确本课目标（约 5:00）

把目标写在白板上或直接翻到课件第 2 页，逐条念一遍：

- 掌握WXSS样式的基本语法与特点
- 理解rpx响应式单位与px的换算
- 掌握Flex布局的核心属性与用法
- 能够使用Flex实现常见页面布局
- 案例：案例2-2 本地生活

本次课 3 学时的安排是：**理论讲解 1 学时，上机实践 2 学时**——Flex 布局需大量练习，案例 2-2 本地生活实操 2 学时。

---

## 三、知识点讲解

### 幻灯片 3：WXSS 样式特点

**【8:00 讲解】**

**WXSS = WeiXin Style Sheets**

小程序样式语言

- rpx响应式单位: 1rpx = 屏幕宽度/750
- iPhone6: 1rpx = 0.5px (750rpx = 375px)
- 支持样式导入: @import "路径"
- 支持内联样式: style和class

### 幻灯片 4：Flex布局核心概念

**【13:00 讲解】**

**display: flex**

开启弹性布局

**flex-direction**

主轴方向(row/column)

**justify-content**

主轴对齐(start/center/end/space-between/space-around)

**align-items**

交叉轴对齐(stretch/center/flex-start/flex-end)

**flex-wrap**

是否换行(nowrap/wrap)


### 幻灯片 5：Flex子项属性

**【18:00 讲解】**

**flex**

子项占比(如 flex:1 等分)

**align-self**

单独设置交叉轴对齐

**order**

排列顺序(默认0, 越小越靠前)

**flex-grow**

放大比例

**flex-shrink**

缩小比例


### 幻灯片 6：案例2-2 本地生活

**【23:00 讲解】**

- 使用Flex实现顶部导航栏布局
- 使用Flex实现宫格菜单(4列均分)
- 使用Flex实现推荐列表(左图右文)
- rpx单位确保不同屏幕适配
- 综合运用Flex布局完成本地生活首页

---

## 四、难点突破：用类比讲清楚

下面这几个点是学生最容易卡住的地方，抽象讲不通就换成类比。

### rpx 单位

像地图比例尺。地图上 1 厘米代表实际 1 公里，不管你把地图印在 A4 纸上还是印在墙上，比例关系不变。rpx 就是把所有手机屏幕都"印"成 750 格。

### Flex 主轴与交叉轴

把容器想成一条跑道。flex-direction 决定跑道朝哪个方向铺，justify-content 管选手在跑道上前后怎么站，align-items 管选手在跑道宽度上左右怎么站。换了跑道方向，"前后"和"左右"就跟着换，这是最容易绕晕的地方。

### flex: 1

相当于"剩下的地方你们几个平分"。三个子元素都写 flex:1，就是各占三分之一；一个写 flex:2 其余写 flex:1，就是 2:1:1 分。

---

## 五、代码演示环节

以下每一段都建议**现场敲**，不要直接贴完整代码。边敲边问"下一行该写什么"，让学生跟上节奏。

### 演示 1：rpx响应式单位

**文件：** `rpx.wxss`　**预期效果：** rpx换算示意

```css
/* rpx 响应式像素单位 */

/* 小程序规定：屏幕宽度 = 750rpx */
/* 在 iPhone6 上：1rpx = 0.5px */
/* 在 iPhone6 上：1px  = 2rpx  */

/* 常见设备换算表 */
设备        屏幕宽度  1rpx→px  1px→rpx
iPhone5      320px     0.42px   2.34rpx
iPhone6/7/8  375px     0.5px    2rpx
iPhone6 Plus 414px     0.552px  1.81rpx

/* 实际使用示例 */
.box {
width: 750rpx;     /* 占满屏幕宽度 */
height: 200rpx;    /* 约100px(iPhone6) */
padding: 20rpx;     /* 约10px */
font-size: 28rpx;   /* 约14px */
}

/* ⚠️ 开发建议 */
/* 设计稿通常为 750px 宽度 */
/* 量出多少px就写多少rpx即可 */
/* 如设计稿上100px → 100rpx */
```

**讲解口径：**

小程序规定**屏幕宽度固定为750rpx**，在iPhone6上1rpx等于0.5px，即1px等于2rpx。使用rpx的好处是**自动适配不同屏幕宽度**。开发技巧：设计稿通常750px宽，**量出多少px就写多少rpx**，比如设计稿上100px的元素，直接写100rpx即可。

### 演示 2：Flex布局核心属性

**文件：** `flex.wxss`　**预期效果：** Flex属性效果

```css
/* Flex布局核心属性 */

/* 1. display: flex 开启弹性布局 */
.container {
display: flex;
}

/* 2. flex-direction 主轴方向 */
.row    { flex-direction: row; }          /* 水平（默认） */
.col    { flex-direction: column; }       /* 垂直 */
.row-rev { flex-direction: row-reverse; }  /* 水平反转 */

/* 3. justify-content 主轴对齐 */
.jc-start   { justify-content: flex-start; }    /* 左对齐 */
.jc-center  { justify-content: center; }        /* 居中 */
.jc-end     { justify-content: flex-end; }       /* 右对齐 */
.jc-between { justify-content: space-between; } /* 两端对齐 */
.jc-around  { justify-content: space-around; }  /* 等间距 */

/* 4. align-items 交叉轴对齐 */
.ai-start { align-items: flex-start; }  /* 顶部对齐 */
.ai-center { align-items: center; }    /* 垂直居中 */
.ai-end   { align-items: flex-end; }     /* 底部对齐 */
.ai-stretch { align-items: stretch; }  /* 拉伸填满 */

/* 5. flex-wrap 换行 */
.wrap    { flex-wrap: wrap; }         /* 允许换行 */
.nowrap  { flex-wrap: nowrap; }       /* 不换行（默认） */

/* 常用组合：水平垂直居中 */
.center-all {
display: flex;
justify-content: center;
align-items: center;
}
```

**讲解口径：**

Flex布局五个核心属性：**flex-direction**设置主轴方向，row水平column垂直；**justify-content**控制主轴对齐，常用center居中和space-between两端对齐；**align-items**控制交叉轴对齐，center实现垂直居中；**flex-wrap**设置是否换行。最常用的组合是**justify-content:center + align-items:center**实现水平垂直居中。

### 演示 3：Flex子项属性

**文件：** `flex-item.wxss`　**预期效果：** 子项属性效果

```css
/* Flex 子项属性 */

/* 1. flex:1 等分剩余空间 */
.item {
flex: 1;  /* 所有子项等分 */
}

/* 2. flex 比例分配 */
.left  { flex: 2; }  /* 占2份 */
.right { flex: 1; }  /* 占1份 */

/* 3. align-self 单独对齐 */
.item-special {
align-self: flex-end;  /* 单独底部对齐 */
}

/* 4. order 排列顺序 */
.item-first  { order: -1; }  /* 排最前 */
.item-last   { order: 1; }   /* 排最后 */

/* 实际案例：底部导航栏 */
.tab-bar {
display: flex;
flex-direction: row;
height: 100rpx;
background: #fff;
border-top: 1rpx solid #eee;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
```

**讲解口径：**

Flex子项有三个重要属性：**flex:1**让子项等分剩余空间，flex:2和flex:1可按比例分配；**align-self**让单个子项覆盖容器的align-items，单独设置对齐方式；**order**改变子项排列顺序，值越小越靠前。实际开发中**flex:1**最常用，如底部导航栏每个tab用flex:1实现等分。

### 演示 4：案例2-2 本地生活九宫格

**文件：** `grid.wxss`　**预期效果：** 九宫格效果

```css
/* 案例2-2：本地生活九宫格布局 */

/* 外层容器 */
.grid-container {
display: flex;
flex-wrap: wrap;         /* 关键：允许换行 */
padding: 20rpx;
background: #fff;
}

/* 每个格子：750rpx / 3 = 250rpx */
.grid-item {
width: 33.33%;       /* 三等分 */
display: flex;
flex-direction: column;  /* 图标在上文字在下 */
align-items: center;     /* 水平居中 */
justify-content: center;  /* 垂直居中 */
padding: 20rpx 0;
}

.grid-item image {
width: 80rpx;
height: 80rpx;
margin-bottom: 10rpx;
}

.grid-item text {
font-size: 24rpx;
color: #333;
}

/* WXML 结构 */
<view class="grid-container">
<view
wx:for="{{gridList}}"
wx:key="id"
class="grid-item">
<image src="{{item.icon}}" />
<text>{{item.name}}</text>
</view>
</view>

/* 九宫格核心：flex-wrap + width:33.33% */
```

**讲解口径：**

九宫格布局的核心是**flex-wrap:wrap**允许换行配合**width:33.33%**三等分。每个格子内部再用**flex-direction:column**让图标在上文字在下，**align-items:center**水平居中。配合wx:for循环渲染数据，就实现了常见的本地生活九宫格效果。关键公式：**每行N个 → width: (100/N)%**。

---

## 六、常见错误与排查

上机环节学生一定会遇到下面这些问题，提前讲一遍能省掉大量重复答疑。

| # | 现象 | 原因与解决 |
|:-:|------|------------|
| 1 | rpx 在大屏上字体过大 | 文字如果也用 rpx，在平板上会大得离谱。惯例是布局尺寸用 rpx，字号用 px 或者做上限控制。 |
| 2 | justify-content 和 align-items 记反 | 记住 justify 管主轴、align 管交叉轴。改了 flex-direction 之后一定要重新想一遍哪个是主轴。 |
| 3 | 给 flex 子项设了固定宽度还写 flex:1 | 两者会打架，实际表现取决于 flex-basis。要么固定宽度，要么弹性伸缩，别混用。 |
| 4 | 最后一行不满时被拉开 | justify-content: space-between 在最后一行只有两个元素时会一左一右分开。解决办法是补占位元素，或改用 gap + 固定列宽。 |

---

## 七、上机实践指引

本次课对应教材案例 1 个，源码已放在站点 `code/session04/` 下：

- **【案例2-2】本地生活** — 含 知识储备、案例实现 共 2 个工程

建议顺序：先带学生跑一遍「知识储备」里的最小示例，确认每个知识点都能单独跑通，再进「案例实现」做完整案例。直接上完整案例，出错了不知道错在哪。

---

## 八、本课小结

回到课件小结页，逐条回顾：

- rpx响应式单位
- Flex布局核心属性
- Flex子项属性
- 案例2-2 本地生活九宫格

今天的两个工具：rpx 解决适配，Flex 解决排版。这两样掌握了，绝大多数页面布局都能搞定。

案例2-2 本地生活里有导航栏、宫格、图文列表三种典型布局，都是 Flex 的经典用法，回去务必跑一遍。挑战任务那个"最后一行左对齐"的坑，值得花点时间琢磨。

---

## 九、布置作业

三档任务，基础必做，进阶建议做，挑战选做。

### 【基础】完成本地生活页面

按照案例2-2，完成包含轮播图和九宫格导航的本地生活页面

### 【进阶】自定义轮播指示器

修改 swiper 的 indicator-dots 样式，使用自定义指示器点

### 【挑战】响应式商品栅格

仅用 Flex 与 rpx 实现商品卡片栅格：一行 2 列且最后一行不满时左对齐不拉伸，在 iPhone 6 / iPhone 14 Pro Max 两种机型下均正常

---

> 配套资源：课件 `slides-v2/session04.pptx`　课堂视频 `videos-output/session04.mp4`　扩展阅读 `docs/第4次课_扩展阅读.md`
