# 第2次课 讲稿

> 课程：小程序开发
> 主题：小程序项目结构与全局配置
> 教材：《微信小程序开发实战（第2版）》第1章
> 时长：3学时（理论 1.5 + 实践 1.5）
> 重点：项目目录结构、app.json配置、页面生命周期
> 难点：生命周期函数执行时机

---

## 一、开场（0:00）

上次课大家都把项目跑起来了。现在打开你们创建的项目，看一眼左边的文件树——是不是有点懵？根目录几个文件，pages 下面每个页面又是四个文件。

（请一位同学说说他看到了哪些文件）

今天就来把这堆文件理清楚。我先给个结论：**小程序的文件是"按职责拆"的，不是"按页面拆"的**。一个页面为什么要四个文件？因为结构、样式、逻辑、配置这四件事，微信要求你分开写，不许混在一起。

今天还有一个重点是生命周期。这个概念第一次听会觉得抽象，但它决定了"代码写在哪里才会在正确的时间跑起来"，是后面每一次课都要用到的基本功。

---

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

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

- 掌握小程序项目的目录结构与文件组成
- 理解 app.json 全局配置文件的作用与配置项
- 掌握页面生命周期的各个阶段与回调函数
- 能够创建包含多个页面的项目

本次课 3 学时的安排是：**理论讲解 1.5 学时，上机实践 1.5 学时**——目录结构与配置讲解后立即动手配置多页面项目。

---

## 三、知识点讲解

### 幻灯片 3：小程序项目目录结构

**【8:00 讲解】**

**app.js**

小程序入口逻辑

**app.json**

全局配置文件

**app.wxss**

全局样式文件

**project.config.json**

项目配置

**pages/**

页面目录(每个页面4个文件: js/json/wxml/wxss)


### 幻灯片 4：app.json 全局配置

**【13:00 讲解】**

**pages**

页面路径列表(第一个为首页)

**window**

全局默认窗口表现

**tabBar**

底部导航栏配置

**networkTimeout**

网络超时时间

**debug**

是否开启调试模式


### 幻灯片 5：页面生命周期

**【18:00 讲解】**

**onLoad**

页面加载时触发(只触发一次)

**onShow**

页面显示时触发(每次都触发)

**onReady**

页面初次渲染完成(只触发一次)

**onHide**

页面隐藏时触发

**onUnload**

页面卸载时触发


### 幻灯片 6：页面配置 page.json

**【23:00 讲解】**

**navigationBarTitleText**

导航栏标题

**navigationBarBackgroundColor**

导航栏背景色

**enablePullDownRefresh**

是否开启下拉刷新

**backgroundColor**

窗口背景色

**usingComponents**

自定义组件声明


---

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

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

### 四类文件的分工

把一个页面想成一个人：wxml 是骨架，决定有哪些部位；wxss 是衣服，决定长什么样；js 是大脑，决定怎么反应；json 是身份证，声明这个页面的一些属性。缺了骨架就站不起来，缺了大脑就只是个模特。

### app.json 的 pages 数组

pages 数组就像一本书的目录。第一项是封面（首页），你没写进目录的页面，读者根本翻不到——哪怕文件已经建好了，不登记进 pages 就访问不了，这是新手最常踩的坑。

### 生命周期

拿开店打比方：onLoad 是"这家店第一次开张"，装修布置只做一次；onShow 是"每天开门营业"，只要有人进来就触发；onReady 是"装修完毕可以接客了"；onHide 是"打烊但没退租"；onUnload 是"退租拆店"。所以拉取一次性数据放 onLoad，每次进来都要刷新的数据放 onShow。

---

## 五、代码演示环节

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

### 演示 1：项目目录结构详解

**文件：** `目录结构`　**预期效果：** 目录结构图示

```
// 小程序完整项目目录结构
myproject/
├── app.js              // 小程序入口文件
├── app.json            // 全局配置（页面/窗口/导航）
├── app.wxss           // 全局样式（所有页面共享）
├── project.config.json   // 工具项目配置
├── sitemap.json          // 小程序搜索索引配置
├── pages/              // 页面目录
│   ├── index/          // 首页
│   │   ├── index.wxml   // 页面结构模板
│   │   ├── index.wxss   // 页面私有样式
│   │   ├── index.js     // 页面逻辑脚本
│   │   └── index.json   // 页面局部配置
│   └── logs/           // 日志页
│       ├── logs.wxml
│       ├── logs.wxss
│       ├── logs.js
│       └── logs.json
├── utils/              // 工具函数目录
│   └── util.js          // 通用工具方法
└── images/             // 静态图片资源

// ⚠️ 关键规则
app.json 的 pages 数组第一个路径 = 首页
```

**讲解口径：**

小程序项目有三个**全局文件**：**app.js**是入口逻辑，**app.json**是全局配置，**app.wxss**是全局样式。pages目录下每个页面由4个文件组成：**.wxml**结构、**.wxss**样式、**.js**逻辑、**.json**配置。注意**app.json中pages数组第一个路径就是首页**。

### 演示 2：app.json 全局配置详解

**文件：** `app.json`　**预期效果：** 配置效果预览

```json
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],

"window": {
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTitleText": "我的小程序",
"navigationBarTextStyle": "black",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light",
"enablePullDownRefresh": false
},

"tabBar": {
"color": "#999",
"selectedColor": "#07c160",
"backgroundColor": "#fff",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "images/home.png",
"selectedIconPath": "images/home-active.png"
},
{
"pagePath": "pages/logs/logs",
"text": "日志",
"iconPath": "images/log.png",
"selectedIconPath": "images/log-active.png"
}
]
},

"style": "v2"
}
```

**讲解口径：**

app.json有三个核心配置节点：**pages**定义页面路径列表，第一个元素就是首页；**window**配置导航栏颜色、标题文字、窗口背景等全局窗口样式；**tabBar**配置底部导航栏，list数组最少2项最多5项，每项需要pagePath、text和iconPath。tabBar的icon图片建议尺寸**81×81px**，不支持网络图片。

### 演示 3：页面生命周期函数

**文件：** `index.js`　**预期效果：** 生命周期流程图

```js
// 页面生命周期函数执行顺序

Page({

// ① 页面加载时触发（只触发一次）
onLoad(options) {
console.log('onLoad 页面加载')
console.log('接收参数:', options)
// 适合：初始化数据、接收页面参数
},

// ② 页面显示时触发（每次都触发）
onShow() {
console.log('onShow 页面显示')
// 适合：刷新数据、恢复状态
},

// ③ 页面初次渲染完成（只触发一次）
onReady() {
console.log('onReady 页面就绪')
// 适合：获取节点信息
},

// ④ 页面隐藏时触发
onHide() {
console.log('onHide 页面隐藏')
// 触发场景：navigateTo跳转、切后台
},

// ⑤ 页面卸载时触发
onUnload() {
console.log('onUnload 页面卸载')
// 触发场景：redirectTo跳转、navigateBack
}

})

// 执行顺序：onLoad → onShow → onReady
// 退出顺序：onHide（隐藏）或 onUnload（卸载）
```

**讲解口径：**

页面有5个核心生命周期函数，执行顺序是**onLoad → onShow → onReady**。**onLoad**页面加载时触发一次，可接收路由参数；**onShow**每次页面显示都触发，适合刷新数据；**onReady**初次渲染完成触发一次。页面退出时，**onHide**在页面隐藏时触发（如navigateTo跳转），**onUnload**在页面卸载时触发（如redirectTo跳转）。

### 演示 4：页面配置 page.json

**文件：** `index.json`　**预期效果：** 配置覆盖效果

```json
// app.json 中的 window 全局配置
{
"window": {
"navigationBarTitleText": "全局标题",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": false
}
}

// pages/index/index.json 页面级配置
{
"navigationBarTitleText": "首页",
"navigationBarBackgroundColor": "#07c160",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f5f5",
"usingComponents": {}
}

// ⚠️ 覆盖规则
页面配置 会覆盖 app.json window 中的同名配置
页面json无需写window层级，直接写属性即可

// 常用页面配置项
navigationBarTitleText      // 导航栏标题
navigationBarBackgroundColor // 导航栏背景色
enablePullDownRefresh        // 是否开启下拉刷新
usingComponents              // 页面自定义组件
```

**讲解口径：**

页面配置**index.json**会覆盖app.json中window的同名配置。注意页面json中**不需要写window这一层**，直接写属性即可。比如app.json全局标题是"全局标题"，但index.json设置了"首页"，那首页导航栏显示的就是**"首页"**。同样导航栏背景色从白色变为绿色，下拉刷新从false变为true。

### 演示 5：app.json 全局配置详解

**文件：** `app.json`　**预期效果：** 配置效果

```json
// app.json 完整配置示例
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"navigationBarBackgroundColor": "#07C160",
"navigationBarTitleText": "我的小程序",
"navigationBarTextStyle": "white",
"backgroundColor": "#f5f5f5"
},
"tabBar": {
"color": "#999",
"selectedColor": "#07C160",
"list": [...]
}
}
```

**讲解口径：**

**app.json**是小程序最重要的配置文件。**pages**定义页面路径列表，第一个为首页；**window**配置导航栏颜色、标题文字等全局样式；**tabBar**配置底部导航栏。修改app.json后保存，开发者工具会自动编译生效。

### 演示 6：页面级配置与优先级

**文件：** `页面配置`　**预期效果：** 配置优先级

```
// 页面级配置 index.json
{
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true
}

// 配置优先级规则
页面.json > app.json 的 window 配置
页面级配置会覆盖全局配置

// 常用页面配置项
navigationBarTitleText  // 导航栏标题
enablePullDownRefresh   // 下拉刷新
disableScroll          // 禁止页面滚动
usingComponents         // 自定义组件
```

**讲解口径：**

页面级配置的优先级**高于**全局配置。在页面的.json文件中设置的navigationTitleText等属性，会覆盖app.json中window的对应配置。常用配置项包括导航栏标题、下拉刷新开关、自定义组件声明等。

### 演示 7：app.json 全局配置详解

**文件：** `app.json`　**预期效果：** 配置效果

```json
// app.json 完整配置示例
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"navigationBarBackgroundColor": "#07C160",
"navigationBarTitleText": "我的小程序",
"navigationBarTextStyle": "white"
},
"tabBar": {
"color": "#999",
"selectedColor": "#07C160"
}
}
```

**讲解口径：**

**app.json**是小程序最重要的配置文件。**pages**定义页面路径列表，第一个为首页；**window**配置导航栏颜色、标题等全局样式；**tabBar**配置底部导航栏。修改app.json后保存，开发者工具会自动编译生效。

### 演示 8：页面级配置与优先级

**文件：** `页面配置`　**预期效果：** 配置优先级

```
// 页面级配置 index.json
{
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true
}

// 配置优先级规则
页面.json > app.json 的 window 配置
页面级配置会覆盖全局配置

// 常用页面配置项
navigationBarTitleText  // 导航栏标题
enablePullDownRefresh   // 下拉刷新
disableScroll          // 禁止页面滚动
usingComponents         // 自定义组件
```

**讲解口径：**

页面级配置的优先级**高于**全局配置。在页面的.json文件中设置的navigationTitleText等属性，会覆盖app.json中window的对应配置。常用配置项包括导航栏标题、下拉刷新开关、自定义组件声明等。

---

## 六、常见错误与排查

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

| # | 现象 | 原因与解决 |
|:-:|------|------------|
| 1 | 新建了页面文件却访问不到 | 忘了在 app.json 的 pages 里登记。正确做法是直接在 app.json 里加一行路径，保存后工具会自动帮你建好四个文件。 |
| 2 | 把网络请求写在 onReady 里 | onReady 是渲染完成才触发，此时用户已经看到空白页了。首屏数据应该在 onLoad 里就发起请求。 |
| 3 | 分不清 onLoad 与 onShow | 从详情页返回列表页时，列表页只触发 onShow 不触发 onLoad。如果你把"刷新列表"写在 onLoad 里，返回后数据就是旧的。课上让学生自己打 console.log 走一遍跳转，比讲十遍都管用。 |
| 4 | app.wxss 与页面 wxss 冲突 | 全局样式会被页面样式覆盖，但选择器优先级规则仍然生效。样式没生效时先看是不是被更高优先级的规则盖掉了。 |

---

## 七、本课小结

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

- 项目目录结构详解
- app.json 全局配置详解
- 页面生命周期函数
- 页面配置 page.json
- 页面级配置与优先级

今天的核心就两句话：**文件按职责拆，代码按生命周期放**。

作业里那个"在每个生命周期函数里打日志"的任务一定要做，做完你对执行顺序的理解会比听我讲清楚得多。下次课进入第2章，开始写真正的页面。

---

## 八、布置作业

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

### 【基础】创建3页面项目

创建包含首页、关于页、设置页的小程序项目，配置 app.json 的 pages 和 window

### 【进阶】生命周期调试

在每个生命周期函数中添加 console.log，观察页面跳转时各函数的执行顺序

### 【挑战】自定义导航栏

通过 navigationStyle:"custom" 关闭原生导航栏，用 WXSS 自行实现带渐变背景的导航栏，并处理不同机型的状态栏高度适配

---

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