主题
项目基本的骨架与搭建首页的静态(Day3 1/2)
本页对应 day3,共 4 段视频,约 1 小时 22 分。
学习目标
- 能调用分类层级接口获取数据,并在分类页左侧渲染一级分类、右侧渲染对应二级分类,完成切换分类时数据联动展示。
- 能在小程序页面生命周期中封装并调用异步请求函数,避免在
onLoad中直接编写异步逻辑,并通过调试输出验证接口返回数据。 - 能查看并复用个人中心静态结构与样式,配置页面自定义导航栏,完成“请登录”入口及登录页路由跳转。
- 能使用 npm 安装依赖,并通过开发者工具“构建 npm”将依赖转换为小程序可用包,验证第三方库在小程序中正常引入使用。
- 能按 Vant 文档完成安装、全局样式配置、项目配置修改与组件注册,并在登录页中使用 Vant 按钮等组件完成静态改造。
- 能搭建用户编辑页静态结构,完成头像按钮、昵称输入区、底部操作按钮布局,并实现授权登录后进入编辑页且登录页销毁的跳转逻辑。
知识点
本节围绕项目基本骨架,把分类业务、个人中心静态、npm 与 Vant、登录跳转和编辑页静态串起来。转写稿里反复强调:先看接口和数据,再写渲染;静态要自己搭,样式和布局要心里有数。
分类模块:先看接口,再发请求渲染
为什么需要:分类页左侧是一级分类,右侧是该分类下的商品/子分类。静态已搭好,差的是“发请求—拿数据—渲染”。
怎么想:先看接口文档。转写稿中“干请求”即 get 请求;当前未登录,不带 token;接口无参数。返回整体是数组,约 10 个一级对象,每个对象下有 children,且各一级下子级数量不同。所以左侧遍历一级数组,右侧遍历“当前选中索引”对应项的 children。
怎么做:先封装 API,命名可用 reqCategory,不带参数、默认 GET;再在页面 onLoad 里调用封装后的方法。注意不要在生命周期里直接写 async,而是封装成函数:
js
// api/category.js
export const reqCategory = () => request({
url: '按接口文档填写地址' // 默认 GET,不带参数
})js
// 分类页
import { reqCategory } from '../../api/category'
Page({
data: {
cateArr: [],
currentIndex: 0
},
onLoad() {
this.getData()
},
async getData() {
const res = await reqCategory()
console.log(res)
if (res.code === 200) {
this.setData({ cateArr: res.data })
}
}
})渲染时左侧一级要加 key,右侧用当前索引取子级:
html
<view v-for="(item, index) in cateArr" :key="item.id">
{{ item.name }}
</view>
<view v-for="child in cateArr[currentIndex].children" :key="child.id">
{{ child.name }}
</view>易错点:this.getData() 的 this 不能漏;不要直接写 getData();不要在 onLoad 上加 async;渲染前先打印确认 res.data 结构;文本插值不要写错,属性绑定按小程序语法处理。
个人中心静态与隐藏顶部导航
为什么需要:个人中心要展示未登录/已登录两种状态,还要把顶部系统导航去掉,做出自定义头部效果。
怎么想:静态结构可从 todo 里拷贝,但样式和图标要接入项目。顶部导航不是自己写的 DOM,不能直接删元素,要用页面配置。
怎么做:在个人中心页面的 json 中写:
json
{
"navigationStyle": "custom"
}它属于页面配置,只保留右上角胶囊按钮。图标字体要在 app.wxss 中引入:
css
@import "路径/iconfont.wxss";易错点:navigationStyle 是页面配置,不要误以为改全局;custom 不是完全无导航,而是只保留胶囊;静态拷贝后要检查图标样式是否全局引入。
npm 在小程序中使用与 Vant
为什么需要:想用 Vant 这类小程序 UI 组件库,减少重复搭按钮、分割线、日历等。
怎么想:小程序可以用 npm,但 node_modules 里的包不能直接引用,必须通过开发者工具“构建 npm”,生成小程序可用依赖。
怎么做:终端安装后,在工具里执行“构建 npm”。按 Vant 文档修改 app.json 的 style: "v2",并在 project.config.json 的 setting 中按文档补 packNpmRelationList,新版本路径注意用 ./miniprogram_npm 一类写法。页面 json 注册组件:
json
{
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
}html
<van-button type="primary">按钮</van-button>易错点:不构建 npm 就直接 import 会报错;构建后仍不生效,先重新编译/清缓存;组件必须在页面 json 注册;文档里的路径要按当前工具版本调整。
登录跳转与编辑页静态
为什么需要:个人中心点“请登录”进入登录页;授权后进入编辑页,编辑页只需授权一次,返回时不应再回登录页。
怎么做:请登录绑定事件,登录页不是 tabBar,可用 navigateTo;登录页进入编辑页用 redirectTo,销毁登录页:
js
goLogin() {
wx.navigateTo({ url: '/pages/login/login' })
}
onTapEdit() {
wx.redirectTo({ url: '/pages/edit/edit' })
}编辑页静态:顶部头像用 button 包 image,下方昵称用 flex,一左一右;底部两个按钮用 flex。根节点宽高 100%,按钮默认 padding 要去掉:
css
.edit { width: 100%; height: 100%; }
.avatar { width: 200rpx; height: 200rpx; padding: 0; }
.avatar .image { width: 200rpx; height: 200rpx; }
.name { display: flex; align-items: center; }
.foot { display: flex; }易错点:根节点类选择器别漏点;button 默认有 padding,不清理头像会有缝隙;图片默认尺寸大,要显式设宽高;navigateTo 不能跳 tabBar,redirectTo 会销毁当前页,按业务选择。
分类模块:先看接口,再写静态渲染
为什么需要:首页分类不是写死的,左侧一级分类、右侧对应二级分类都来自接口。先确认接口返回结构,才能决定静态结构怎么遍历。
怎么想:分类接口是 GET,无参数;返回数组,数组每一项是一级分类,一级分类下有 children 二级分类。左侧遍历一级,右侧遍历当前选中项的 children。
怎么做:先封装 API:
js
// api/index.js
import request from '../utils/request'
export const reqCategory = () => request({
url: '/category',
method: 'get'
})页面中定义数据并请求:
js
data() {
return {
sARR: [],
currentIndex: 0
}
},
onLoad() {
this.getData()
},
methods: {
async getData() {
const res = await reqCategory()
if (res.code === 200) {
this.sARR = res.data
}
}
}静态遍历左侧:
html
<view
wx:for="{{sARR}}"
wx:key="id"
class="{{currentIndex === index ? 'active' : ''}}"
>
{{item.name}}
</view>右侧取当前一级分类的 children:
html
<view wx:for="{{sARR[currentIndex].children}}" wx:key="id">
{{item.name}}
</view>易错点:onLoad 里不要直接写大量 await 逻辑,最好封装成 this.getData();调用对象方法要写 this;wx:for 要加 wx:key;插值用 {{item.name}},不要写成字符串或乱加冒号。
生命周期里发请求:为什么要封装
为什么需要:页面一挂载就要拿分类数据,但生命周期里直接写异步逻辑,容易让代码同步异步混杂,后期难维护。
怎么想:onLoad 只负责“触发”,真正请求放到 methods 里。这样结构清晰,也方便复用和调试。
怎么做:先 console.log(res) 看整体返回,再赋值。确认 res.code === 200 后再 this.sARR = res.data。如果页面没数据,先检查接口、this、赋值路径,不要急着写渲染。
易错点:不要写成 getData() 而漏 this;不要没打印结果就直接遍历;不要忘了 await。
个人中心静态:先搭结构,再接业务
为什么需要:个人中心有未登录、已登录、编辑资料等状态,先把静态搭好,后面才好做条件渲染和授权逻辑。
怎么想:未登录展示默认头像和“请登录”;登录后展示用户头像、昵称。顶部导航要去掉,使用页面配置 navigationStyle: "custom",只保留右上角胶囊按钮。
怎么做:
json
{
"navigationStyle": "custom",
"navigationBarTitleText": "个人中心"
}结构上分头像区、订单区、功能区和底部说明。头像和昵称用条件渲染二选一;订单标题用 display:flex; justify-content:space-between 做左右分布。
易错点:navigationStyle 是页面配置,不是自己写个元素;页面没配标题时会继承全局配置,页面配置优先级更高。
小程序里使用 npm 与 Vant
为什么需要:登录页、编辑页要按钮、分割线、加载等组件,手写费时且不稳定,可以用 Vant Weapp。
怎么想:小程序能用 npm 包,但 node_modules 里的依赖不能直接给小程序用,必须通过开发者工具“构建 npm”转成小程序依赖。
怎么做:终端安装:
bash
npm i @vant/weapp -S --production在 app.json 加:
json
{
"style": "v2"
}按 Vant 文档在 project.config.json 配置 npm 路径,然后工具菜单执行“构建 npm”。页面 usingComponents 注册后使用:
json
{
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
}易错点:安装完不构建 npm 会报找不到组件;构建后仍报错,检查 project.config.json 路径和是否重新构建。
登录页与编辑页静态:跳转与布局
为什么需要:点击“请登录”进入登录页,授权后进入编辑页;授权只需一次,编辑页进入后应销毁登录页。
怎么想:普通跳转可用 wx.navigateTo;这里授权流程只走一次,可用 wx.redirectTo,避免返回登录页。
怎么做:
js
goLogin() {
wx.redirectTo({
url: '/pages/login/login'
})
}编辑页根节点撑满:
css
page {
width: 100%;
height: 100%;
}
.edit {
width: 100%;
height: 100%;
}头像按钮、昵称输入、底部两个按钮用 flex 布局;按钮内图片设置宽高,去掉默认 padding。
易错点:page 是标签选择器,不要写成 .page;百分比宽高参照父级;flex 布局要明确主轴和侧轴。
样式与调试提醒
转写中反复强调:静态要自己搭,不能只抄。调试时先看数据有没有、再看结构、最后调样式。样式重点掌握 flex 布局、水平垂直居中、justify-content、align-items 等。遇到工具卡住,先保存、重新编译、重新构建 npm,再判断是不是代码问题。
本节要解决的问题
本节围绕“网络请求:HTTPS 协议”展开。笔记给出了两个地址:接口文档地址 http://39.98.123.211:8300/doc.html,服务器地址 https://gmall-prod.atguigu.cn。核心目标是:在微信小程序里通过官方 API wx.request 拿到分类数据,再把数据渲染到分类页。
为什么需要:分类页的静态结构已经搭好,左侧是一级分类,右侧是对应分类下的商品/二级内容。静态页面没有数据,必须发请求拿真实数据。
怎么想:先看接口文档,确认请求方式、是否需要参数、返回数据结构,再写 API 封装,最后在页面生命周期里调用。
怎么做:用 wx.request 发 GET 请求,判断 res.code === 200,把返回数组存到页面数据里。
易错点:不要没看接口就写请求;不要请求回来不打印结构就直接渲染;不要把文档地址当成服务器请求地址。
先看接口文档,再决定请求怎么写
为什么需要:接口文档决定请求地址、请求方法、参数和返回结构。在本节先找到“获取分类层级数据”这个接口,确认它是 GET 请求,而且当前还没涉及登录,所以不需要带 token,也没有额外参数要求。
怎么想:先判断“要不要参数、要不要登录态、返回的是对象还是数组”。转写稿里明确说,返回整体是一个数组,数组里大约有 10 个对象,每个对象代表左侧的一级分类;每个一级分类下面还有 children,代表它自己的二级分类,而且不同一级分类下的二级数量不一样。
怎么做:打开接口文档,找到对应接口,先用工具或浏览器查看 JSON。重点看最外层是数组还是对象,再看每一项里的 id、name、children 等字段。
易错点:不要看到文档地址是 http://... 就直接拿它发请求;真正请求服务器要用笔记里的 https://gmall-prod.atguigu.cn。接口路径要按文档填,不能自己猜。
用 wx.request 封装分类请求
为什么需要:微信小程序发起网络请求需要通过它提供的 API 实现,本节用的就是 wx.request。把请求封装成函数,页面里只负责调用,逻辑更清楚,也方便以后复用。
怎么想:分类接口是 GET、不带参数,所以封装时只需要写 url 和 method。转写稿里把它封装成类似 reqCategory 的请求函数,页面里引入后调用。
怎么做:可以先写最基础的请求,确认能通,再封装:
js
// 基础示意:实际接口路径按接口文档填写
wx.request({
url: 'https://gmall-prod.atguigu.cn/接口路径',
method: 'GET',
success(res) {
console.log(res)
}
})封装时可以写成:
js
// 按转写稿整理,函数名和路径以项目为准
export const reqCategory = () => {
return wx.request({
url: 'https://gmall-prod.atguigu.cn/接口路径',
method: 'GET'
})
}易错点:wx.request 是异步的,不能请求完立刻拿结果;要先 await 或放在回调里。请求函数导出后,页面里要正确引入,路径别写错。
在分类页生命周期里发请求
为什么需要:分类页一挂载就应该拿到数据。转写稿里说,默认进入首页,点击 tabBar 切到分类页时,页面挂载后就要发请求。
怎么想:不要在 onLoad 里堆一大段异步逻辑。建议把“获取分类数据”封装成页面方法,比如 getData,然后在 onLoad 里调用。
怎么做:
js
import { reqCategory } from '../../api/index'
Page({
data: {
categoryList: []
},
onLoad() {
this.getData()
},
async getData() {
const res = await reqCategory()
console.log(res)
if (res.code === 200) {
this.setData({
categoryList: res.data
})
}
}
})转写稿里反复提醒:先 console.log 打印整体返回数据,确认结构,再做 res.code === 200 判断,再把数据存起来。
易错点:不要在生命周期里直接写一堆 await 导致逻辑混乱;不要忘记 this;不要没判断 code 就赋值;不要数据还没回来就先渲染。
先打印结构,再渲染左侧和右侧
为什么需要:分类页是“左侧一级分类 + 右侧当前一级分类下的 children”。如果不知道返回结构,直接写 v-for 很容易报错。
怎么想:先确认 res.data 是数组,数组每一项是一级分类;左侧遍历这个数组,显示 item.name;右侧遍历当前选中项的 children。
怎么做:左侧 v-for 要加 key,一般用 item.id;右侧根据当前索引取对应项的 children。文本要用插值,图片字段按接口返回写。
易错点:v-for 不加 key 会有警告;插值时不要写成字符串,也不要多加冒号;图片地址要用接口字段;先确认 AppData 里真的有数据,再写渲染,不要“咔咔先搞完再报错往回找”。
本节调试过程回顾
的调试顺序是:先看接口文档,确认“获取分类层级数据”是 GET、无参数;再写 API 请求函数;在分类页 onLoad 里调用封装好的 getData;await 后先 console.log(res),看到整体是数组、里面有 10 个一级分类、每项带 children;确认结构后再 this.setData 存分类数组;最后才改模板,左侧遍历一级分类,右侧遍历当前项 children。这个顺序能避免“数据没通就渲染”的无效调试。
课堂画面




代码示例
1. 分类模块:wx.request 取数据 + 左侧一级、右侧二级渲染
api/index.js
js
// 服务器地址(笔记中给出)
const BASE_URL = 'https://gmall-prod.atguigu.cn'
// 对 wx.request 做一次 Promise 封装,方便用 async/await
export const request = (options) => {
return new Promise((resolve, reject) => {
wx.request({
url: BASE_URL + options.url,
// 不传 method 时默认就是 GET,这里保留是为了通用
method: options.method || 'GET',
data: options.data || {},
success: resolve,
fail: reject
})
})
}
// 获取分类层级数据:GET 请求、不需要带参数
// url 路径以接口文档(http://39.98.123.211:8300/doc.html)为准
export const reqCategory = () => request({ url: '/接口路径按文档填写' })pages/category/index.js
js
import { reqCategory } from '../../api/index'
Page({
data: {
categoryList: [], // 存储分类数据(10 个一级分类,每个带 children)
activeIndex: 0 // 当前选中的一级分类索引,右侧靠它取 children
},
// 生命周期:页面一挂载就发请求
onLoad() {
this.getData()
},
// 不要直接在 onLoad 里写 async,封装成函数再调用
async getData() {
const result = await reqCategory()
console.log(result) // 先打印看整体返回结构
if (result.code === 200) {
this.setData({
categoryList: result.data
})
}
},
// 点击左侧切换高亮 + 右侧内容
changeIndex(e) {
this.setData({
activeIndex: e.currentTarget.dataset.index
})
}
})pages/category/index.wxml
xml
<view class="category">
<!-- 左侧:一级分类 -->
<scroll-view class="left" scroll-y>
<view
class="item {{activeIndex === index ? 'active' : ''}}"
wx:for="{{categoryList}}"
wx:key="id"
data-index="{{index}}"
bindtap="changeIndex"
>
{{item.name}}
</view>
</scroll-view>
<!-- 右侧:当前一级分类下的二级分类 -->
<scroll-view class="right" scroll-y>
<view wx:if="{{categoryList.length}}">
<text class="title">{{categoryList[activeIndex].name}}</text>
<view class="child-list">
<block wx:for="{{categoryList[activeIndex].children}}" wx:key="id">
<image class="pic" src="{{item.imageUrl}}"></image>
<text>{{item.name}}</text>
</block>
</view>
</view>
</scroll-view>
</view>pages/category/index.wxss
css
.category {
display: flex;
height: 100%;
}
.left {
width: 200rpx;
height: 100%;
background: #f3f3f3;
}
.left .item {
height: 80rpx;
line-height: 80rpx;
text-align: center;
font-size: 28rpx;
}
.left .item.active {
color: #e93b3d;
background: #fff;
}
.right {
flex: 1; /* 右侧占满剩余宽度 */
height: 100%;
padding: 20rpx;
box-sizing: border-box;
}
.pic {
width: 120rpx;
height: 120rpx;
}关键行与易错点
request里method: options.method || 'GET':分类接口是 GET 且无参数,不写 method 也可以,保留是为了封装通用。- 分类接口返回的是数组,数组里每个对象是一级分类,对象内部还有
children才是二级分类,渲染时要分两次遍历。 wx:for一定要加wx:key(用id),不加会有控制台警告且影响复用性能。onLoad里不要直接写async onLoad,把请求封装成getData再调用,避免生命周期函数变成异步导致时序问题。this.setData必须带this,直接写setData会报错。- 右侧取的是
categoryList[activeIndex].children,所以activeIndex必须有默认值0,否则页面初次渲染右侧为空。
2. 个人中心:隐藏原生导航栏 + 请登录跳转
pages/mine/index.json
json
{
"navigationStyle": "custom"
}pages/mine/index.wxml
xml
<view class="container">
<!-- 顶部背景图(小熊) -->
<image class="bg" src="/static/images/bg.png"></image>
<view class="user-container">
<!-- 未登录:显示默认头像 + 请登录 -->
<view class="no-login" bindtap="goLogin">
<image class="avatar" src="/static/images/default.png"></image>
<text class="tip">请登录</text>
</view>
</view>
<view class="order">
<view class="order-title">
<text>我的订单</text>
<text>查看更多 ></text>
</view>
<view class="order-list">
<view class="order-item">
<van-icon name="balance-list-o" size="40rpx"></van-icon>
<text>商品订单</text>
</view>
<view class="order-item">
<van-icon name="gift-o" size="40rpx"></van-icon>
<text>礼品订单</text>
</view>
<view class="order-item">
<van-icon name="cash-back-record" size="40rpx"></van-icon>
<text>退款售后</text>
</view>
</view>
</view>
</view>pages/mine/index.wxss
css
.container {
width: 100%;
height: 100%;
position: relative;
}
.bg {
width: 100%;
height: 300rpx;
position: absolute;
top: 0;
left: 0;
}
.user-container {
position: relative; /* 层级压在背景图之上 */
z-index: 2;
padding: 200rpx 30rpx 0;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
}
.order {
background: #fff;
border-radius: 10rpx;
margin: 20rpx 30rpx;
}
.order-title {
display: flex;
justify-content: space-between;
padding: 20rpx;
}
.order-list {
display: flex;
justify-content: space-around;
}
.order-item {
display: flex;
flex-direction: column;
align-items: center;
}pages/mine/index.js
js
Page({
// 请登录文字点击 → 跳到登录页
goLogin() {
wx.navigateTo({
url: '/pages/login/login'
})
}
})关键行与易错点
- 顶部原生导航栏自己写不了,只能靠页面配置
"navigationStyle": "custom"隐藏,它只保留右上角胶囊按钮;写进app.json的window则全局生效,写在页面 json 里只对当前页生效。 - 页面配置优先级高于全局配置,例如登录页标题要改,就在
pages/login/login.json里写navigationTitle覆盖,不必动全局。 - 背景图用
position: absolute时,用户信息容器必须给position: relative+z-index,否则会被背景图盖住。 - 登录页不是 tabBar 页面,用
wx.navigateTo即可;如果目标页是 tabBar 页面则必须用wx.switchTab,用错会跳不过去。 - 根节点
.container要撑满一屏,高度依赖父级,百分比高度要逐层给到height: 100%。
3. 编辑个人中心静态 + Vant 组件库接入
依赖安装与构建(终端)
bash
npm init -y
npm i @vant/weapp -S --productionapp.json
json
{
"pages": [
"pages/index/index",
"pages/category/index",
"pages/mine/index",
"pages/login/login",
"pages/edit/edit"
],
"window": {
"navigationBarTitleText": "首页",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}project.config.json(setting 部分)
json
{
"miniprogramRoot": "miniprogram/",
"setting": {
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram/"
}
]
}
}pages/edit/edit.json
json
{
"navigationBarTitleText": "编辑个人中心",
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
}pages/edit/edit.wxml
xml
<view class="edit">
<!-- 头像:用 button 才能调起微信选择头像 -->
<button class="avatar-wrapper">
<image class="avatar" src="/static/images/avatar.png"></image>
</button>
<view class="name">
<text class="label">昵称</text>
<input class="input" placeholder="请输入昵称" />
</view>
<view class="footer">
<van-button type="warning">忽略设置</van-button>
<van-button type="primary">更新用户信息</van-button>
</view>
</view>pages/edit/edit.wxss
css
.edit {
width: 100%;
height: 100%;
background: #111;
}
/* 头像按钮:去掉 button 自带的内边距和边框 */
.avatar-wrapper {
width: 200rpx;
height: 200rpx;
padding: 0;
margin: 0 auto;
background: #111;
border: none;
border-radius: 50%;
overflow: hidden;
}
.avatar {
width: 200rpx;
height: 200rpx;
}
.name {
display: flex;
align-items: center;
height: 100rpx;
padding: 0 50rpx;
margin: 20rpx 0;
border: 1px solid #fff;
color: #fff;
}
.name .label {
padding-left: 20rpx;
margin-right: 20rpx;
}
.name .input {
flex: 1;
color: #fff;
}
.footer {
display: flex;
justify-content: space-around;
align-items: center;
padding: 40rpx 0;
}pages/login/login.js(授权后跳编辑页)
js
Page({
// Vant 按钮的点击事件名是 bind:click
goEdit() {
// 授权只需一次,用 redirectTo 把登录页销毁掉,返回时不再回到登录页
wx.redirectTo({
url: '/pages/edit/edit'
})
}
})关键行与易错点
- 小程序不能直接用
node_modules里的包,必须在开发者工具里执行「工具 → 构建 npm」,把它转成小程序可用的依赖,否则import报错。 app.json的"style": "v2"用来去掉小程序内置组件默认样式,避免影响 Vant 组件外观。- 新版开发者工具目录结构变了,
packNpmManually要配合miniprogramNpmDistDir指向miniprogram/,路径写错就会出现"构建 npm 找不到依赖"。 - Vant 组件用前必须在页面 json 的
usingComponents里注册,注册名(如van-button)和标签名要一致。 van-button的点击事件是bind:click,而原生button是bindtap,两者不要混用。- 头像用
<button>包<image>才能调起微信头像选择,但原生 button 自带padding和边框,必须padding: 0; border: none;清掉,否则图片两边会留缝。 .edit的height: 100%依赖父级页面高度,若父级没撑满会失效;写选择器时.edit是类名,别漏掉前面的点。- 登录页 → 编辑页用
wx.redirectTo(销毁当前页),这样从编辑页返回不会再回到登录页;用wx.navigateTo则会保留登录页在栈里。
常见错误
生命周期里直接写 async / await 现象:
onLoad里写async onLoad(){ await reqCategory() },页面偶发白屏、数据时序混乱,或后续维护时逻辑纠缠不清。 原因:小程序生命周期函数由框架调用,返回 Promise 不会被框架处理,异步逻辑与生命周期职责混在一起,且容易把"同步语义"写成异步。 修法:onLoad只做一行调用——this.getCategoryData(),把async与await全部收进这个自定义方法内部。调用组件方法忘记
this现象:onLoad(){ getData() }直接报 "getData is not defined"。 原因:方法挂在页面实例对象上,不是全局函数,必须通过实例访问。 修法:写成this.getData();同时确认方法定义在Page({ ... })的配置对象里。wx:for不写wx:key现象:控制台反复告警,切换选中项时高亮错位、列表复用异常。 原因:缺少唯一标识,diff 无法稳定复用节点。 修法:wx:key="id"(用数据里的唯一字段名,不加{{}}、不加item.)。模板里把变量当字符串写 现象:页面上直接显示
item.name、imgUrl这样的字面量,而不是真实数据。 原因:写成了普通属性值,没有用插值语法。 修法:属性与文本一律写成{{ item.name }}、src="{{ item.imgUrl }}";不要额外加冒号。页面根节点样式不生效 现象:
.page { height: 100% }写了没反应,页面撑不满、背景色只覆盖内容高度。 原因:根节点是内置标签page,不是带 class 的元素,用类选择器选不到。 修法:直接写标签选择器page { height: 100% }(不加点),且百分比高度依赖父级,需逐级确认。npm 装的包直接 require 就报错 现象:
import moment from 'moment'后运行报错,或 vant 组件注册了却不渲染。 原因:node_modules里的包不能直接被小程序使用,必须先转成小程序可识别的依赖目录。 修法:开发者工具 → 工具 → 构建 npm;每次新增依赖后都要重新构建,改完不生效先重新编译再刷新。引入 UI 组件库漏配置 现象:
van-button标签写了但页面空白,或组件默认样式被小程序内置样式污染。 原因:只npm install了,没做三件事——app.json加"style": "v2"、project.config.json里补packNpmManually与packNpmRelationList(路径按新版目录写./开头)、页面 json 的usingComponents注册。 修法:按官方"快速上手"逐步核对,缺一步都不行。图标字体不显示 现象:个人中心里阿里图标全是方块/空白。 原因:iconfont 的
@font-face与类名只写在了某个页面的 wxss 里,不是全局样式。 修法:把 iconfont 样式文件放进项目,在app.wxss中用@import "路径/iconfont.wxss";引入。该销毁的页面用了
navigateTo现象:授权登录页用navigateTo跳到编辑页,返回时又回到授权页,用户被迫二次授权。 原因:navigateTo保留页面栈,redirectTo关闭当前页。 修法:一次性流程(授权、登录中转)用wx.redirectTo;需要返回上一层的用wx.navigateTo;tabBar 页只能用switchTab。想隐藏导航栏却去改全局或写死结构 现象:想只让"我的"页顶部无导航,结果全站导航都没了,或自己拼了个假导航。 原因:导航栏是原生组件,只能通过配置控制,不能靠 CSS/结构改。 修法:在该页面的 json 里写
"navigationStyle": "custom",仅对当前页生效;页面配置优先级高于app.json全局配置。改完记得给内容补顶部安全区,避免被胶囊按钮遮挡。
练习
分类页数据渲染:在
api目录封装reqCategory(GET、无参),在分类页onLoad中通过一个async自定义方法取数并存入data,先console.log确认返回结构,再渲染左侧一级分类(含wx:key)与右侧当前选中项的子分类,并让左侧点击切换高亮与右侧内容。vant 落地:在登录页接入 vant,完成
style:v2、project.config.json的 npm 配置、usingComponents注册三步,用van-button替换原生按钮,并用van-divider做分隔线,验证自定义文字与主色按钮两种形态。隐藏顶部导航:把"我的"页改为自定义导航(
navigationStyle: custom),使背景图顶到状态栏下方,同时保证头像、昵称、订单区、功能列表在真机与模拟器上都不被胶囊遮挡。个人中心登录态两套 UI:未登录显示默认头像 + "请登录"(点击
redirectTo到登录页),已登录显示微信头像与昵称,用条件渲染实现二选一。编辑资料页静态:完成头像按钮(内含
image,去掉 button 默认 padding)、昵称输入行(左右 flex 布局 + 边框)、底部两个按钮(van-button的 default / primary 组合,横向 flex 排列),并给根节点写page标签样式保证满屏。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 分类模块业务完成 | 12:51 |
| 2 | 个人中心静态的查看与npm在小程序中使用 | 33:47 |
| 3 | 微信小程序UI组件库vant使用 | 16:04 |
| 4 | 用户编辑页的静态搭建 | 19:54 |
小结
- 数据没打印出来之前不要写渲染逻辑,先看结构再写模板,能省掉一半排错时间。
- 所有网络请求走
wx.request,接口必须是 https,封装到api层,页面里只调用不拼地址。 - 小程序可以用 npm 生态,但"装完"不等于"能用",构建 npm 是必经的一步,且每次新增依赖都要重来一次。
- UI 组件库(vant)能显著提效,代价是配置项多,配置顺序错一步组件就不渲染。
- 页面配置优先级高于全局配置,导航栏、标题这类原生能力只能靠配置,不能靠 CSS。
- 路由选择看语义:要返回用
navigateTo,一次性流程用redirectTo,tabBar 页用switchTab。 - 静态页面必须自己动手搭,flex 布局与水平垂直居中是硬功夫,也是面试必问,别指望抄。
- 样式不生效先怀疑两件事:选择器写错(比如给
page加了点)和编辑器缓存(重新编译再刷新)。