Skip to content

小程序项目目录分析(Day1 1/2) ​

本页对应 day1,共 5 段视频,约 1 小时 40 分。

学习目标 ​

  1. 能够独立完成微信小程序项目目录的清理与重建,准确说明 app.js、app.json、app.wxss 及页面四类文件的作用与配置要求。
  2. 能够使用 rpx 完成页面元素适配,正确说明 iPhone 6 下 1px = 2rpx 的换算关系,并将固定像素样式改为等比例适配样式。
  3. 能够正确使用 bindtap 与 catchtap 绑定事件,说明冒泡差异,并通过事件对象、自定义属性和 setData 完成数据修改与动态属性渲染。
  4. 能够将阿里图标库资源引入微信小程序项目,配置全局图标样式,并在页面中通过类名使用图标、调整图标大小与颜色。
  5. 能够使用 wx:for、wx:key、wx:if、wx:show 等模板指令完成列表渲染和元素显示隐藏控制。
  6. 能够独立完成选项卡案例,实现内容区切换、选中项高亮,并通过自定义属性传递索引值控制显示状态。

知识点 ​

先看项目目录:哪些是应用级,哪些是页面级 ​

为什么需要:刚导入小程序项目时,最容易“看见文件就改”,结果不知道哪个文件管应用、哪个文件管页面。先把目录分清,后面写页面才不会乱。

怎么想:先按“应用级”和“页面级”两类看。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 路径,而不是乱改样式。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

代码示例 ​

示例一:应用文件 + 首页(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 都能覆盖。

常见错误 ​

  1. 页面写了 width:100%; height:100% 却不满屏 现象:首页根节点只被文字撑开,背景色只覆盖一小块。 原因:百分比是相对父级算的,页面最外层 page 默认没有高度,子元素百分比自然失效。 修法:在 app.wxss 里给 page 设 height:100%(全局一次搞定),不要每个页面重复写。

  2. 用 px 写死尺寸,换机型就变形 现象:iPhone6 上刚好,切到 Plus 或 iPad 元素偏小、留白一片。 原因:px 不随屏幕宽度等比缩放,写死就等于放弃适配。 修法:统一改 rpx,以 iPhone6(375px 宽)为基准换算,整宽就是 750rpx;只有 1px 细边框这类可以保留 px。

  3. 事件回调里带小括号传参 bindtap="handler(1)" 现象:点击报错或拿到 undefined,参数根本进不去。 原因:小程序事件回调不支持直接传参,小括号写法无效。 修法:元素上写 data-index="1",回调里用 e.currentTarget.dataset.index 取;注意是 currentTarget 不是 target(后者可能是内部子节点)。

  4. 直接改 this.data.msg = xxx 或用 += 累加后视图不动 现象:控制台打印值变了,页面文字纹丝不动。 原因:data 只是数据源,视图更新只认 setData;且在原值上累加属于原地修改。 修法:this.setData({ msg: this.data.msg + '!' }),永远构造新值再塞回去。

  5. 动态属性忘了插值,font-size="fontSize" 或 style="font-size:30px" 写死 现象:字号不跟着数据变,甚至属性值变成字面量字符串。 原因:属性值默认是字符串,不是表达式;另外小程序没有 v-bind,别把 Vue 的 :style 搬过来。 修法:style="font-size:{{fontSize}}px",凡是"这块要动"的地方都套 {{}},内容区和属性区通用。

  6. wx:for 不写 wx:key,或写成 wx:key="{{item.id}}" 现象:控制台告警,列表增删/倒序后状态错位。 原因:wx:key 的值是"数组元素中唯一属性的属性名",是个字符串,不是插值表达式。 修法:直接 wx:key="id";没有唯一 id 时才退而用 wx:key="*this"。

  7. 阿里图标引入后显示成方块/问号 现象:类名写对了,图标位置是个小方框。 原因:一是把 iconfont.css 原样拷进来(小程序只认 .wxss);二是 @font-face 的 src 还指向本地字体文件,路径没跟着搬过来。 修法:改名 iconfont.wxss,在 app.wxss 里 @import 引入变成全局样式,src 换成在线地址;类名以 iconfont 开头再加具体图标名。

  8. 父元素 bindtap 导致点子元素也触发父级逻辑 现象:点选项卡里的按钮,外面整块的点击事件也执行了。 原因:bind 是冒泡的,事件会往上走。 修法:需要阻断的那一层换成 catchtap;只是普通点击、无嵌套冲突时用 bindtap 即可。

练习 ​

  1. 结构复原:清空项目,只留 app.js / app.json / app.wxss 三个应用文件,新建 home 页面,用 rpx 做一条满宽、高 100rpx、文字水平垂直居中的标题栏,然后在 iPhone6 / Plus / iPad 三个模拟器下切换验证不变形。

  2. 事件传参 + 选项卡:写三个"军事/新闻/直播"按钮,均分一行,点击切换下方三块内容之一,选中项高亮。要求:不允许在 bindtap 里写小括号传参,索引必须通过 data-* + dataset 取;高亮类名用三元表达式动态拼。

  3. 动态属性综合:一个按钮,点一次让某段文字字号 +2、旋转角度 +15deg、颜色在两种之间来回切换。全部通过 {{}} 写进 style,不许操作 DOM。

  4. 列表渲染:准备 3 条 todo 数据,wx:for 渲染,wx:key 用 id;点击任意一条只把这一条标题后追加"✔",其他条不受影响(提示:setData 的路径写法)。

  5. 图标改造:从阿里图标库挑 4 个图标下载,把 iconfont.css 改造成全局 iconfont.wxss(@import 引入 + 字体换在线地址),在第 2 题的三个内容区各用一个,并分别调一次大小和颜色。

原视频对照 ​

#视频时长
1微信小程序rpx34:28
2微信小程序事件注意事项与动态属性的写法19:10
3微信小程序项目使用阿里图标库17:11
4微信小程序模版指令的学习16:57
5微信小程序之选项卡12:37

小结 ​

  1. 项目跑不起来时,排错顺序固定:app.json 的 pages 有没有 → 页面四件套齐不齐 → 应用三件套在不在,别一上来就翻逻辑代码。
  2. 尺寸只有一条铁律:rpx 打天下,设计稿按 iPhone6(750rpx 满宽)量,其余机型交给底层换算,不要自己算比例。
  3. 视图是数据的投影,setData 是唯一出口;任何"改了没反应"的问题,先怀疑是不是绕过了 setData 或原地改了对象。
  4. 事件回调天生不能传参,data-* + dataset 是标准解法,这个套路会贯穿后面所有列表、选项卡、条件渲染场景。
  5. {{}} 既能写内容也能写属性,这是替代 Vue v-bind 的唯一方式,写动态样式、动态类名时第一时间想到它。
  6. 指令就那几个(wx:for / wx:if / hidden 等),不要臆造 wx:show、v-xx;wx:if 是销毁重建,hidden 是 display:none,按"要不要保留状态"来选。
  7. 全局与页面的边界要清楚:跨页面复用的样式(布局基座、图标字体)一律进 app.wxss 或 @import 进来,页面级 wxss 只管自己。
  8. 组件自带默认样式(如 image 默认 320×240)会悄悄干扰布局,效果和预期不符时,先查默认样式再查自己的代码。