主题
小程序项目目录分析(Day1 1/2)
本页对应 day1,共 5 段视频,约 1 小时 40 分。
学习目标
- 能够独立完成微信小程序项目目录的清理与重建,准确说明
app.js、app.json、app.wxss及页面四类文件的作用与配置要求。 - 能够使用
rpx完成页面元素适配,正确说明 iPhone 6 下1px = 2rpx的换算关系,并将固定像素样式改为等比例适配样式。 - 能够正确使用
bindtap与catchtap绑定事件,说明冒泡差异,并通过事件对象、自定义属性和setData完成数据修改与动态属性渲染。 - 能够将阿里图标库资源引入微信小程序项目,配置全局图标样式,并在页面中通过类名使用图标、调整图标大小与颜色。
- 能够使用
wx:for、wx:key、wx:if、wx:show等模板指令完成列表渲染和元素显示隐藏控制。 - 能够独立完成选项卡案例,实现内容区切换、选中项高亮,并通过自定义属性传递索引值控制显示状态。
知识点
先看项目目录:哪些是应用级,哪些是页面级
为什么需要:刚导入小程序项目时,最容易“看见文件就改”,结果不知道哪个文件管应用、哪个文件管页面。先把目录分清,后面写页面才不会乱。
怎么想:先按“应用级”和“页面级”两类看。app.js、app.json、app.wxss 是应用级文件;pages 放页面,一个页面通常四个文件。sitemap.json 按本节笔记理解是搜索/索引相关配置,project.config.json 是项目配置文件,初学阶段先知道用途,不急着改。
怎么做:练习时可以把模板页面删掉,但配置文件要留下,再自己建应用文件和首页。转写稿里就是先保留配置文件,删掉多余模板,再重新建 app.js、app.json、app.wxss 和 pages/home。注意转写里的“pays”应纠正为 pages,“APP点GX”应理解为 app.js,“杰son”是 json,“微信SS”是 wxss。
易错点:导入已有项目不是新建项目;pages 不是 pays;配置文件不要随手删;json 的键必须用双引号。
app.js:应用入口,用 App 函数创建小程序应用
为什么需要:小程序要先有“应用”,才能挂页面。app.js 就是应用入口。
怎么想:转写稿强调,app.js 里通过内置的 App 函数创建微信小程序应用,App 接收一个配置对象,里面可以放生命周期函数,初学先知道结构即可。
怎么做:
js
App({
// 应用级逻辑,后续再学生命周期
})易错点:App 首字母大写;不要把它和页面里的 Page 混用;app.js 是应用级,不是某个页面自己的逻辑。
app.json:应用配置,管导航栏和页面路由
为什么需要:应用要有导航栏样式,也要知道先加载哪些页面。app.json 就是应用配置文件。
怎么想:转写稿里先配 window:导航栏背景色、文字颜色、标题。背景色只能用十六进制;文字颜色常见为 black 或 white。页面路由放在 pages 数组里。
怎么做:
json
{
"pages": [
"pages/home/home"
],
"window": {
"navigationBarBackgroundColor": "#cccccc",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信小程序"
}
}易错点:json 左侧键必须加双引号;导航栏背景色不要写英文颜色名;pages 路径要写全,如 pages/home/home;没有页面时项目跑不起来。
app.wxss:全局样式,所有页面都能用
为什么需要:有些样式多个页面都要用,比如页面背景、公共字体,不该每个页面重复写。
怎么想:app.wxss 是应用级样式文件,写在里面的样式对全部页面生效。小程序样式文件用 .wxss,不要再用 .css 作为小程序样式文件。
怎么做:
css
page {
background-color: #f5f5f5;
}易错点:app.wxss 不是页面私有样式;页面自己的样式应写到页面 .wxss;不要因为语法像 CSS 就把文件后缀写成 .css。
pages:页面目录,一个页面通常四个文件
为什么需要:应用可以有很多页面,但页面必须放在 pages 下,并且结构、样式、逻辑、配置分开管理。
怎么想:转写稿说一个页面四个文件:.wxml 管结构,.wxss 管样式,.js 写页面逻辑,.json 是页面配置。用开发者工具新建页面时,它会自动生成四个文件,并帮助配好路由。
怎么做:例如 pages/home 下:
js
// home.js
Page({
data: {}
})html
<!-- home.wxml -->
<view>首页</view>css
/* home.wxss */
.home {
padding: 20rpx;
}json
{
"usingComponents": {}
}易错点:页面逻辑用 Page,不是 App;wxml 不是 html;页面 json 也要符合 JSON 格式;新建页面后检查 app.json 的 pages 是否已有路径。
新建页面与调试:为什么一开始跑不起来
为什么需要:只留配置文件时,项目常常启动不了,要知道报错在说什么。
怎么想:转写稿的调试过程是:先删模板、保留配置文件;此时应用配置里没有页面,工具会提示页面缺失;然后新建 pages/home,工具自动生成四个文件并配置路由,项目就能跑。
怎么做:先建 app.js、app.json、app.wxss,再建 pages/home/home 四件套,确认 app.json 的 pages 包含 pages/home/home,保存后重新编译。
易错点:不要只建文件夹不建四个文件;不要漏配路由;Page 不要写成小写;报“页面找不到”时先查 app.json 路径,而不是乱改样式。
课堂画面





代码示例
示例一:应用文件 + 首页(rpx 适配 / flex 布局 / image / button)
app.json
json
{
"pages": [
"pages/home/home"
],
"window": {
"navigationBarBackgroundColor": "#cccccc",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信小程序"
}
}app.js
js
// App() 用于创建小程序应用,必须有这个文件
App({
onLaunch() {
console.log('小程序启动')
}
})app.wxss
css
/* 全局样式,所有页面都能用 */
page {
width: 100%;
height: 100%;
}pages/home/home.wxml
html
<view class="home">
<view class="title">微信小程序基础语法的学习</view>
<image class="avatar" src="/static/1.jpg" mode="aspectFill"></image>
<button type="primary" size="mini" bindtap="handleTap">点击我</button>
<input class="ipt" type="text" placeholder="请输入内容" />
</view>pages/home/home.wxss
css
.home {
width: 100%;
height: 100%;
background-color: #eee;
display: flex;
flex-direction: column;
align-items: center;
}
.title {
width: 750rpx;
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-size: 32rpx;
background-color: #4a90e2;
color: #ffffff;
}
.avatar {
width: 200rpx;
height: 200rpx;
border-radius: 50%;
margin: 20rpx 0;
}
.ipt {
border: 1rpx solid #999;
margin-top: 20rpx;
}pages/home/home.js
js
Page({
data: {},
handleTap() {
console.log('按钮被点击')
}
})要点与易错点
app.json是 JSON,左侧 key 必须加双引号,不能写注释;navigationBarTextStyle只能填black或white,navigationBarBackgroundColor只能是十六进制色值。- 一个页面必须四件套:
.wxml(结构)、.wxss(样式)、.js(逻辑)、.json(配置),缺一个都跑不起来;pages里没配路由,编译直接报错。 .home写height: 100%不生效,是因为它的父级page没高度,所以要在app.wxss里给page设height: 100%——百分比高度永远看父级。- 适配不用 px 用
rpx:750rpx就等于屏幕宽度,iPhone6(375×667)下1px = 2rpx,写死375px换到 plus 上就不适配了。 image有默认尺寸 320×240,不写宽高时原图会被压缩;想圆角就必须同时写死width/height,且别用mode="widthFix"(高度自适应后圆角就不是正圆了)。input在模拟器里点不出键盘是正常的,真机才会弹。
示例二:事件绑定 + setData + 插值写动态属性
pages/event/event.wxml
html
<view class="box" bindtap="outerTap">
<view class="msg">{{msg}}</view>
<button type="primary" size="mini" bindtap="handleTap">点击我(会冒泡)</button>
<button type="warn" size="mini" catchtap="handleTap">点击我(不冒泡)</button>
<view class="text" style="font-size: {{fontSize}}px; transform: rotate({{deg}}deg);">
字体大小与旋转
</view>
<button type="primary" size="mini" bindtap="addFont">字体加大</button>
</view>pages/event/event.wxss
css
.box {
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.msg {
font-size: 36rpx;
margin-bottom: 20rpx;
}
.text {
margin: 20rpx 0;
}pages/event/event.js
js
Page({
data: {
msg: '我爱你',
fontSize: 30,
deg: 15
},
handleTap(e) {
console.log(e)
this.setData({
msg: this.data.msg + '!'
})
},
outerTap() {
console.log('父元素被触发')
},
addFont() {
this.setData({
fontSize: this.data.fontSize + 1,
deg: this.data.deg + 15
})
}
})要点与易错点
bindtap事件会冒泡(点内层按钮,外层outerTap也执行);catchtap阻止冒泡,只执行自己。- 事件回调不能传参:
bindtap="handleTap(1)"是无效写法,加了小括号也不生效;要带数据只能用data-*自定义属性,从e.currentTarget.dataset里取。 - 改数据必须走
this.setData({...}),写this.data.msg += '!'页面不会更新。 - 小程序里没有 window / document,所以没有
querySelector、没有 DOM 操作,别再写document.getElementById。 - 插值
{{}}不光能放在内容区,属性里也能用:style="font-size: {{fontSize}}px"就是本节的重点;但v-bind、v-if、v-for这套 Vue 指令在小程序里不能用,要换成wx:系列。 - 事件回调会注入事件对象
e,e.currentTarget是绑定事件的那个元素,e.target是真正点到的元素,取自定义属性一般用前者。
示例三:选项卡(wx:for / wx:if / hidden / dataset)
pages/tab/tab.wxml
html
<view class="tab">
<view
wx:for="{{tabs}}"
wx:key="id"
class="tab-item {{flag === item.id ? 'active' : ''}}"
data-side="{{item.id}}"
bindtap="handler"
>{{item.name}}</view>
</view>
<view wx:if="{{flag === 1}}" class="con">军事:大毛打二毛</view>
<view wx:if="{{flag === 2}}" class="con">游戏:王者荣耀</view>
<view wx:if="{{flag === 3}}" class="con">直播:带货直播</view>
<view class="tip" hidden="{{flag !== 1}}">当前选中的是军事(hidden 控制,节点还在)</view>pages/tab/tab.wxss
css
.tab {
display: flex;
}
.tab-item {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background-color: #f5f5f5;
}
.tab-item.active {
background-color: #ffeb3b;
color: #000000;
}
.con {
padding: 30rpx;
}
.tip {
padding: 20rpx 30rpx;
color: #999999;
}pages/tab/tab.js
js
Page({
data: {
flag: 1,
tabs: [
{ id: 1, name: '军事' },
{ id: 2, name: '游戏' },
{ id: 3, name: '直播' }
]
},
handler(e) {
const flag = Number(e.currentTarget.dataset.side)
this.setData({ flag })
}
})要点与易错点
wx:for的默认变量是item(当前项)和index(下标),想改名用wx:for-item="xx"、wx:for-index="ii"。wx:key="id"里写的是数组元素对象的属性名,不能写item.id,也不能写{{item.id}},否则列表顺序变化时会出问题。data-side="{{item.id}}"传过去的值,从dataset.side取出来是字符串,所以这里用Number()转一下再用===比较;否则'1' === 1永远为 false,高亮和内容都不切换。- 判断相等用
===(两个等号),写成一个等号是赋值,条件永远成立。 - 高亮类名靠三元表达式拼在
class里:{{flag === item.id ? 'active' : ''}}。 wx:if是真正的条件渲染(不满足时节点被销毁重建),hidden只是隐藏、节点还在;小程序里没有v-show。- 本节的指令就这几个:
wx:for、wx:if、wx:else、wx:elif、hidden,v-on、v-model之类一律没有。 - 想用阿里图标:下载到本地后把
iconfont.css改名成iconfont.wxss放进项目,在app.wxss里用@import "iconfont.wxss";引入变全局,字体地址换在线链接;类名必须是iconfont+ 图标名,颜色和font-size都能覆盖。
常见错误
页面写了
width:100%; height:100%却不满屏 现象:首页根节点只被文字撑开,背景色只覆盖一小块。 原因:百分比是相对父级算的,页面最外层page默认没有高度,子元素百分比自然失效。 修法:在app.wxss里给page设height:100%(全局一次搞定),不要每个页面重复写。用 px 写死尺寸,换机型就变形 现象:iPhone6 上刚好,切到 Plus 或 iPad 元素偏小、留白一片。 原因:px 不随屏幕宽度等比缩放,写死就等于放弃适配。 修法:统一改 rpx,以 iPhone6(375px 宽)为基准换算,整宽就是 750rpx;只有 1px 细边框这类可以保留 px。
事件回调里带小括号传参
bindtap="handler(1)"现象:点击报错或拿到undefined,参数根本进不去。 原因:小程序事件回调不支持直接传参,小括号写法无效。 修法:元素上写data-index="1",回调里用e.currentTarget.dataset.index取;注意是currentTarget不是target(后者可能是内部子节点)。直接改
this.data.msg = xxx或用+=累加后视图不动 现象:控制台打印值变了,页面文字纹丝不动。 原因:data只是数据源,视图更新只认setData;且在原值上累加属于原地修改。 修法:this.setData({ msg: this.data.msg + '!' }),永远构造新值再塞回去。动态属性忘了插值,
font-size="fontSize"或style="font-size:30px"写死 现象:字号不跟着数据变,甚至属性值变成字面量字符串。 原因:属性值默认是字符串,不是表达式;另外小程序没有v-bind,别把 Vue 的:style搬过来。 修法:style="font-size:{{fontSize}}px",凡是"这块要动"的地方都套{{}},内容区和属性区通用。wx:for不写wx:key,或写成wx:key="{{item.id}}"现象:控制台告警,列表增删/倒序后状态错位。 原因:wx:key的值是"数组元素中唯一属性的属性名",是个字符串,不是插值表达式。 修法:直接wx:key="id";没有唯一 id 时才退而用wx:key="*this"。阿里图标引入后显示成方块/问号 现象:类名写对了,图标位置是个小方框。 原因:一是把
iconfont.css原样拷进来(小程序只认.wxss);二是@font-face的src还指向本地字体文件,路径没跟着搬过来。 修法:改名iconfont.wxss,在app.wxss里@import引入变成全局样式,src换成在线地址;类名以iconfont开头再加具体图标名。父元素
bindtap导致点子元素也触发父级逻辑 现象:点选项卡里的按钮,外面整块的点击事件也执行了。 原因:bind是冒泡的,事件会往上走。 修法:需要阻断的那一层换成catchtap;只是普通点击、无嵌套冲突时用bindtap即可。
练习
结构复原:清空项目,只留
app.js/app.json/app.wxss三个应用文件,新建home页面,用 rpx 做一条满宽、高 100rpx、文字水平垂直居中的标题栏,然后在 iPhone6 / Plus / iPad 三个模拟器下切换验证不变形。事件传参 + 选项卡:写三个"军事/新闻/直播"按钮,均分一行,点击切换下方三块内容之一,选中项高亮。要求:不允许在
bindtap里写小括号传参,索引必须通过data-*+dataset取;高亮类名用三元表达式动态拼。动态属性综合:一个按钮,点一次让某段文字字号 +2、旋转角度 +15deg、颜色在两种之间来回切换。全部通过
{{}}写进style,不许操作 DOM。列表渲染:准备 3 条 todo 数据,
wx:for渲染,wx:key用 id;点击任意一条只把这一条标题后追加"✔",其他条不受影响(提示:setData的路径写法)。图标改造:从阿里图标库挑 4 个图标下载,把
iconfont.css改造成全局iconfont.wxss(@import引入 + 字体换在线地址),在第 2 题的三个内容区各用一个,并分别调一次大小和颜色。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 微信小程序rpx | 34:28 |
| 2 | 微信小程序事件注意事项与动态属性的写法 | 19:10 |
| 3 | 微信小程序项目使用阿里图标库 | 17:11 |
| 4 | 微信小程序模版指令的学习 | 16:57 |
| 5 | 微信小程序之选项卡 | 12:37 |
小结
- 项目跑不起来时,排错顺序固定:
app.json的pages有没有 → 页面四件套齐不齐 → 应用三件套在不在,别一上来就翻逻辑代码。 - 尺寸只有一条铁律:rpx 打天下,设计稿按 iPhone6(750rpx 满宽)量,其余机型交给底层换算,不要自己算比例。
- 视图是数据的投影,
setData是唯一出口;任何"改了没反应"的问题,先怀疑是不是绕过了setData或原地改了对象。 - 事件回调天生不能传参,
data-*+dataset是标准解法,这个套路会贯穿后面所有列表、选项卡、条件渲染场景。 {{}}既能写内容也能写属性,这是替代 Vuev-bind的唯一方式,写动态样式、动态类名时第一时间想到它。- 指令就那几个(
wx:for/wx:if/hidden等),不要臆造wx:show、v-xx;wx:if是销毁重建,hidden是display:none,按"要不要保留状态"来选。 - 全局与页面的边界要清楚:跨页面复用的样式(布局基座、图标字体)一律进
app.wxss或@import进来,页面级wxss只管自己。 - 组件自带默认样式(如
image默认 320×240)会悄悄干扰布局,效果和预期不符时,先查默认样式再查自己的代码。