微信小程序页面交互 ------ 知识点详解与案例实现
一、Page() 函数
1.1 语法说明
Page()
是微信小程序用于注册页面的函数,接收一个对象作为参数,该对象包含页面的初始数据、生命周期函数、事件处理函数等。
javascript
Page({
data: { /* 页面初始数据 */ },
onLoad() { /* 页面加载时执行 */ },
onShow() { /* 页面显示时执行 */ },
// 自定义方法
handleClick() { }
})
1.2 案例:页面初始化数据
javascript
// pages/index/index.js
Page({
data: {
title: '欢迎使用小程序',
count: 0
},
onLoad() {
console.log('页面加载完成');
}
})
xml
<!-- pages/index/index.wxml -->
<view>{{title}}</view>
<view>计数:{{count}}</view>
二、数据绑定
2.1 语法说明
使用双大括号 {``{}}
在 WXML 中绑定 JS 中 data
的数据。
2.2 案例:动态显示用户名
javascript
Page({
data: {
userName: '张三'
}
})
xml
<view>你好,{{userName}}!</view>
三、事件绑定
3.1 语法说明
在 WXML 中使用 bind
或 catch
绑定事件,如 bindtap
表示点击事件。
3.2 案例:点击按钮增加计数
javascript
Page({
data: {
count: 0
},
increment() {
this.setData({
count: this.data.count + 1
});
}
})
xml
<view>当前计数:{{count}}</view>
<button bindtap="increment">+1</button>
四、事件对象
4.1 语法说明
事件处理函数会自动接收一个 event
对象,包含事件信息,如 target.dataset
。
4.2 案例:获取按钮自定义数据
javascript
Page({
handleClick(e) {
console.log('按钮ID:', e.currentTarget.dataset.id);
}
})
xml
<button data-id="btn1" bindtap="handleClick">按钮1</button>
五、this 关键字
5.1 说明
在 Page 对象的方法中,this
指向当前页面实例,可用于访问 data
或调用 setData()
。
注意:在回调函数中(如 setTimeout),
this
可能丢失,需用箭头函数或缓存this
。
5.2 案例:正确使用 this
javascript
Page({
data: { msg: 'Hello' },
showMsg() {
console.log(this.data.msg); // 正确
},
delayedMsg() {
const that = this; // 缓存 this
setTimeout(function() {
that.setData({ msg: 'Delayed Hello' });
}, 1000);
}
})
六、setData() 方法
6.1 语法说明
用于修改 data
中的数据并触发页面重新渲染。不能直接修改 this.data
。
javascript
this.setData({
key: newValue
})
6.2 案例:更新用户输入
javascript
Page({
data: {
inputVal: ''
},
onInput(e) {
this.setData({
inputVal: e.detail.value
});
}
})
xml
<input bindinput="onInput" value="{{inputVal}}" />
<view>你输入了:{{inputVal}}</view>
七、条件渲染(wx:if / wx:elif / wx:else)
7.1 语法说明
根据条件决定是否渲染某块内容。
7.2 案例:根据分数显示等级
javascript
Page({
data: {
score: 85
}
})
xml
<view wx:if="{{score >= 90}}">优秀</view>
<view wx:elif="{{score >= 70}}">良好</view>
<view wx:else>需努力</view>
八、<block>
标签
8.1 说明
<block>
是一个逻辑容器,不会被渲染为真实节点,常用于包裹多个元素进行条件或列表渲染。
8.2 案例:条件渲染多个元素
xml
<block wx:if="{{showDetails}}">
<view>姓名:张三</view>
<view>年龄:25</view>
</block>
九、hidden 属性
9.1 说明
hidden="{``{true}}"
会隐藏元素,但元素仍存在于 DOM 中 (与 wx:if
不同)。
9.2 案例:切换显示/隐藏
javascript
Page({
data: {
isHidden: false
},
toggle() {
this.setData({
isHidden: !this.data.isHidden
});
}
})
xml
<view hidden="{{isHidden}}">这段文字可被隐藏</view>
<button bindtap="toggle">切换显示</button>
十、data-* 自定义属性
10.1 说明
在元素上定义 data-xxx
属性,可在事件对象中通过 e.currentTarget.dataset.xxx
获取。
10.2 案例:计算器按钮传递操作符
xml
<button data-op="+" bindtap="handleOp">+</button>
<button data-op="-" bindtap="handleOp">-</button>
javascript
Page({
handleOp(e) {
const op = e.currentTarget.dataset.op;
console.log('操作符:', op);
}
})
十一、模块(module.exports / require)
11.1 说明
小程序支持 CommonJS 模块系统,可将工具函数封装为模块。
11.2 案例:数学工具模块
javascript
// utils/math.js
function add(a, b) {
return a + b;
}
module.exports = { add };
javascript
// pages/index/index.js
const math = require('../../utils/math.js');
Page({
onLoad() {
console.log(math.add(2, 3)); // 5
}
})
十二、列表渲染(wx:for)
12.1 语法说明
使用 wx:for
遍历数组或对象,wx:key
用于提升性能。
12.2 案例:美食列表
javascript
Page({
data: {
foods: [
{ id: 1, name: '红烧肉', price: 38 },
{ id: 2, name: '宫保鸡丁', price: 28 }
]
}
})
xml
<view wx:for="{{foods}}" wx:key="id">
{{index + 1}}. {{item.name}} - ¥{{item.price}}
</view>
十三、网络请求(wx.request)
13.1 案例:获取远程美食数据
javascript
Page({
onLoad() {
wx.request({
url: 'https://api.example.com/foods',
success: (res) => {
this.setData({ foods: res.data });
},
fail: () => {
wx.showToast({ title: '加载失败', icon: 'none' });
}
});
}
})
十四、提示框(wx.showToast / wx.showModal)
14.1 案例:提交问卷后提示
javascript
wx.showToast({
title: '提交成功',
icon: 'success',
duration: 1500
});
十五、WXS(WeiXin Script)
15.1 说明
WXS 是小程序的脚本语言,用于在 WXML 中处理逻辑(类似过滤器)。
15.2 案例:格式化价格
javascript
// utils/format.wxs
var formatPrice = function(price) {
return '¥' + price.toFixed(2);
}
module.exports = { formatPrice };
xml
<wxs src="./utils/format.wxs" module="fmt" />
<view wx:for="{{foods}}" wx:key="id">
{{item.name}} - {{fmt.formatPrice(item.price)}}
</view>
十六、上拉触底(onReachBottom)
16.1 案例:加载更多美食
javascript
Page({
data: {
page: 1,
foods: []
},
onLoad() {
this.loadFoods();
},
onReachBottom() {
this.setData({ page: this.data.page + 1 });
this.loadFoods();
},
loadFoods() {
// 模拟请求
const newFoods = [...Array(5)].map((_, i) => ({
id: this.data.page * 5 + i,
name: `美食${this.data.page * 5 + i}`,
price: 20 + i
}));
this.setData({ foods: [...this.data.foods, ...newFoods] });
}
})
十七、下拉刷新(onPullDownRefresh)
17.1 配置与使用
json
// pages/food/food.json
{
"enablePullDownRefresh": true
}
javascript
Page({
onPullDownRefresh() {
this.setData({ foods: [], page: 1 });
this.loadFoods();
wx.stopPullDownRefresh(); // 停止刷新动画
}
})
十八、双向数据绑定(model:)
18.1 说明
小程序支持 model:value
实现类似 Vue 的双向绑定(需基础库 2.9.3+)。
18.2 案例:调查问卷输入
javascript
Page({
data: {
answer: ''
}
})
xml
<input model:value="{{answer}}" placeholder="请输入答案" />
<view>你输入的是:{{answer}}</view>
注意:传统方式仍需
bindinput + setData
,model:value
是语法糖。
综合性案例
案例1:【比较数字大小】完整实现
javascript
// pages/compare/compare.js
Page({
data: {
num1: '',
num2: '',
result: ''
},
onInput1(e) {
this.setData({ num1: e.detail.value });
},
onInput2(e) {
this.setData({ num2: e.detail.value });
},
compare() {
const n1 = parseFloat(this.data.num1);
const n2 = parseFloat(this.data.num2);
if (isNaN(n1) || isNaN(n2)) {
wx.showToast({ title: '请输入有效数字', icon: 'none' });
return;
}
let res = '';
if (n1 > n2) res = '第一个数大';
else if (n1 < n2) res = '第二个数大';
else res = '两数相等';
this.setData({ result: res });
}
})
xml
<!-- compare.wxml -->
<input placeholder="输入第一个数字" bindinput="onInput1" value="{{num1}}" />
<input placeholder="输入第二个数字" bindinput="onInput2" value="{{num2}}" />
<button bindtap="compare">比较</button>
<view wx:if="{{result}}">结果:{{result}}</view>
案例2:【简易计算器】
javascript
Page({
data: {
display: '0',
firstNum: null,
operator: null,
waitingForOperand: false
},
inputDigit(e) {
const digit = e.currentTarget.dataset.digit;
if (this.data.waitingForOperand) {
this.setData({ display: digit, waitingForOperand: false });
} else {
this.setData({ display: this.data.display === '0' ? digit : this.data.display + digit });
}
},
inputOperator(e) {
const nextOperator = e.currentTarget.dataset.op;
const inputValue = parseFloat(this.data.display);
if (this.data.firstNum === null) {
this.setData({ firstNum: inputValue });
} else if (this.data.operator) {
const result = this.calculate(this.data.firstNum, inputValue, this.data.operator);
this.setData({ display: String(result), firstNum: result });
}
this.setData({ waitingForOperand: true, operator: nextOperator });
},
calculate(first, second, op) {
switch (op) {
case '+': return first + second;
case '-': return first - second;
case '*': return first * second;
case '/': return first / second;
default: return second;
}
},
clear() {
this.setData({ display: '0', firstNum: null, operator: null, waitingForOperand: false });
},
equals() {
const inputValue = parseFloat(this.data.display);
if (this.data.firstNum !== null && this.data.operator) {
const result = this.calculate(this.data.firstNum, inputValue, this.data.operator);
this.setData({ display: String(result), firstNum: null, operator: null, waitingForOperand: true });
}
}
})
xml
<view class="display">{{display}}</view>
<view class="row">
<button bindtap="clear">C</button>
<button data-op="*" bindtap="inputOperator">×</button>
<button data-op="/" bindtap="inputOperator">÷</button>
</view>
<view class="row">
<button data-digit="7" bindtap="inputDigit">7</button>
<button data-digit="8" bindtap="inputDigit">8</button>
<button data-digit="9" bindtap="inputDigit">9</button>
<button data-op="-" bindtap="inputOperator">−</button>
</view>
<view class="row">
<button data-digit="4" bindtap="inputDigit">4</button>
<button data-digit="5" bindtap="inputDigit">5</button>
<button data-digit="6" bindtap="inputDigit">6</button>
<button data-op="+" bindtap="inputOperator">+</button>
</view>
<view class="row">
<button data-digit="1" bindtap="inputDigit">1</button>
<button data-digit="2" bindtap="inputDigit">2</button>
<button data-digit="3" bindtap="inputDigit">3</button>
<button bindtap="equals">=</button>
</view>
案例3:【美食列表 + 下拉刷新 + 上拉加载】
(结合前面"列表渲染"、"网络请求"、"上拉触底"、"下拉刷新"知识点)
可参考第十二至十六节代码组合实现。
案例4:【调查问卷 + 双向绑定】
javascript
Page({
data: {
questions: [
{ id: 1, text: '你喜欢吃辣吗?', answer: '' },
{ id: 2, text: '你最喜欢的菜系?', answer: '' }
]
},
submit() {
const unanswered = this.data.questions.some(q => !q.answer.trim());
if (unanswered) {
wx.showToast({ title: '请填写所有问题', icon: 'none' });
return;
}
wx.showToast({ title: '提交成功!', icon: 'success' });
console.log('问卷结果:', this.data.questions);
},
onAnswer(e) {
const index = e.currentTarget.dataset.index;
const value = e.detail.value;
const questions = this.data.questions;
questions[index].answer = value;
this.setData({ questions });
}
})
xml
<view wx:for="{{questions}}" wx:key="id" class="question">
<text>{{item.text}}</text>
<input model:value="{{item.answer}}" bindinput="onAnswer" data-index="{{index}}" />
</view>
<button bindtap="submit">提交问卷</button>
本章小结
本章围绕页面交互展开,涵盖:
- 页面注册(Page)
- 数据驱动(data + setData)
- 用户交互(事件绑定、事件对象)
- 动态渲染(条件、列表、block、hidden)
- 实用功能(网络请求、提示框、上拉/下拉)
- 高级特性(WXS、双向绑定、模块化)
掌握这些知识点,可构建功能完整、交互流畅的小程序页面。