3、小程序前端基础:WXML模板语法、WXSS样式布局、JS逻辑交互、Flex弹性布局实战

好,咱们进入第三章。这一章可以说是小程序开发的「地基工程」。你想想看,不管后面要做多复杂的电商系统,最终呈现给用户的,就是页面长什么样、交互顺不顺畅。说白了,就是 WXML、WXSS、JS 这三兄弟怎么配合干活。

我个人习惯,学新框架时先别急着写业务逻辑。先把模板语法摸透,把布局玩明白。不然写到后面,你会发现改个样式比写个接口还痛苦。嗯,我当年刚转小程序时,就吃过这个亏。

3.1 WXML 模板语法:小程序的「骨架」

WXML 长得像 HTML,但骨子里完全不一样。它不是真正的 DOM 标签,而是微信自己定义的一套标记语言。你写 <view>,它最终会被渲染成原生组件。

3.1.1 数据绑定

这是最基础,也是最常用的。用双花括号 {{ }} 把 JS 里的数据「塞」到模板里。

<!-- 页面显示:用户昵称:张三 -->
<view>用户昵称:{{nickName}}</view>

<!-- 属性绑定也支持 -->
<image src="{{avatarUrl}}"></image>

<!-- 还可以做简单运算 -->
<view>总价:{{price * count}} 元</view>

我在项目中遇到过一个问题:数据绑定时,如果某个字段是 undefined,页面会直接显示空字符串,不会报错。这其实是个坑,你调试时很难发现。所以我建议,所有从接口返回的数据,最好在 data 里先给个默认值。

3.1.2 条件渲染

wx:if 来控制元素显示隐藏。注意,它和 hidden 有本质区别。

<view wx:if="{{isVip}}">尊贵的VIP用户,欢迎您!</view>
<view wx:else>开通会员享受更多优惠</view>

<!-- 多个条件用 wx:elif -->
<view wx:if="{{score >= 90}}">优秀</view>
<view wx:elif="{{score >= 60}}">及格</view>
<view wx:else>继续努力</view>
注意: wx:if 是「真条件渲染」,它会销毁和重建节点。如果频繁切换,性能开销大。而 hidden 只是控制 CSS 的 display,节点始终存在。我一般这样选:切换频率低用 wx:if,频率高用 hidden

3.1.3 列表渲染

电商系统里,商品列表、订单列表,全得靠 wx:for

<view wx:for="{{productList}}" wx:key="id">
  <text>商品:{{item.name}} - ¥{{item.price}}</text>
</view>

<!-- 也可以自定义变量名 -->
<view wx:for="{{cartItems}}" wx:for-item="goods" wx:for-index="idx" wx:key="goodsId">
  <text>第{{idx + 1}}件:{{goods.title}}</text>
</view>

wx:key 一定要加。不加的话,小程序会报警告,而且列表更新时性能会变差。我曾经因为偷懒没加 key,结果列表重新排序时,页面闪得跟幻灯片似的。

3.2 WXSS 样式布局:让页面「穿上衣服」

WXSS 和 CSS 基本一样,但多了个 rpx 单位。这个单位是微信专门为适配不同屏幕设计的。你写 750rpx,它就等于屏幕宽度。说白了,你按 iPhone 6 的 375px 设计稿来写,1px = 2rpx,换算起来非常方便。

3.2.1 选择器与样式导入

支持类选择器、ID 选择器、标签选择器。但注意,不支持通配符 *

/* 外部样式文件,用 @import 导入 */
@import "common.wxss";

.container {
  padding: 20rpx;
  background-color: #f8f8f8;
}

#userAvatar {
  width: 100rpx;
  height: 100rpx;
  border-radius: 50%;
}

3.2.2 常用样式技巧

做电商页面,有几个样式我几乎天天用:

  • 单行文本溢出省略overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  • 多行文本省略display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  • 1px 边框(解决高清屏边框变粗):用伪元素 + transform 缩放
小技巧: 我习惯把常用的样式封装成 class,比如 .ellipsis.border-bottom,放到一个 base.wxss 文件里。每个页面都导入它,省得重复写。

3.3 JS 逻辑交互:让页面「活起来」

JS 文件是页面的「大脑」。它负责处理数据、响应事件、调用 API。

3.3.1 页面生命周期

小程序页面有自己的一套生命周期。你只需要记住最常用的几个:

生命周期 触发时机 常用场景
onLoad 页面加载时触发一次 获取页面参数、请求初始数据
onShow 页面每次显示时触发 刷新购物车数量、恢复状态
onReady 页面初次渲染完成 获取节点信息、开始动画
onPullDownRefresh 用户下拉刷新 重新加载列表数据
onReachBottom 页面触底 加载更多数据(分页)

我一般把数据请求放在 onLoad 里。但如果是 tab 页面,切换回来需要刷新数据,那就得放 onShow 里。你想想看,用户从商品详情页返回列表页,如果列表不刷新,他刚买的商品还显示「可购买」,这不就出 bug 了吗?

3.3.2 事件绑定与传参

事件绑定用 bindcatch。区别在于 catch 会阻止事件冒泡。

<button bindtap="handleClick">点我</button>
<view catchtap="handleViewTap">点击我不会冒泡</view>

// JS 中定义事件处理函数
Page({
  handleClick(e) {
    console.log('按钮被点击了');
  }
});

传参不能直接写在括号里,得用 data-* 自定义属性:

<view wx:for="{{list}}" wx:key="id">
  <button bindtap="handleDelete" data-id="{{item.id}}">删除</button>
</view>

// 在事件中通过 e.currentTarget.dataset.id 获取
handleDelete(e) {
  const id = e.currentTarget.dataset.id;
  console.log('要删除的ID是:', id);
}
避坑指南: 我曾经在 wx:for 里直接用 data-index="{{index}}" 传索引,结果列表增删后索引全乱了。后来我改用数据本身的唯一 ID 来传参,再通过 ID 去查找对应数据。这样更稳定。

3.4 Flex 弹性布局实战:电商页面「三板斧」

Flex 布局,说白了就是让元素「能屈能伸」。小程序里几乎所有页面布局,都能用 Flex 搞定。我总结了电商页面最常用的三种模式。

3.4.1 水平居中 + 垂直居中

比如商品卡片里的「加入购物车」按钮,要水平垂直居中。

.center-box {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100rpx;
}

3.4.2 两端对齐 + 垂直居中

商品列表里,左边是图片和名称,右边是价格和按钮。这种布局用 justify-content: space-between 最合适。

.product-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx;
}

.product-info {
  display: flex;
  align-items: center;
}

.product-price {
  text-align: right;
}

3.4.3 等分排列(flex:1)

首页的分类导航,一行显示 4 个图标,每个占 1/4 宽度。

.nav-list {
  display: flex;
}

.nav-item {
  flex: 1;
  text-align: center;
}

你想想看,如果不用 Flex,用浮动或者 inline-block,你得算宽度、清浮动、处理间距。用 Flex 一行代码就搞定了。我刚开始写小程序时,还习惯用 float,后来发现 Flex 真香,现在基本不用 float 了。

实战建议: 做电商页面时,我习惯把 Flex 容器和项目的样式分开写。容器只控制排列方向和对齐方式,项目只控制自身大小和间距。这样后期改布局,只需要改容器,不用动每个项目。

好了,这一章的内容就这些。WXML 是骨架,WXSS 是衣服,JS 是灵魂,Flex 是让它们协调工作的「胶水」。把这四个基础打牢,后面写电商页面就会非常顺手。下一章,咱们开始搭建项目结构,真正动手写代码了。