☰
微信小程序JS入门实战:用按钮实现价格过滤功能
2026/10/14 4:26:22 网站建设 项目流程

1. 从一个按钮说起:为什么我拿价格过滤来练手

做微信小程序开发,绕不开JavaScript。很多人学完WXML和WXSS之后,一到JS逻辑就卡壳,看文档都懂,自己写就懵。我当初也是这样,后来发现问题的根源在于:缺少一个足够小、但又能跑通完整数据流的练手项目。价格过滤这个需求刚好卡在这个点上——它涉及数据绑定、事件处理、数组操作、条件渲染,几乎把小程序JS入门的核心知识点串了一遍,但业务逻辑又简单到不需要任何后端和数据库。

这篇文章就是围绕“用按钮实现对价格进行过滤”这个具体场景,把我在实际编码过程中踩过的坑、想明白的原理、以及可以直接抄作业的代码,完整地分享出来。适合已经了解小程序基本结构、能看懂WXML和WXSS、但JS逻辑还不太熟练的朋友。看完之后你至少能掌握三件事:怎么用按钮触发数据变化、怎么用数组方法做条件筛选、怎么让过滤结果实时反映到页面上。

我见过太多教程一上来就讲生命周期、讲异步编程,新手听完一头雾水。其实入门阶段最需要的是“输入-处理-输出”这个最小闭环的反复练习。价格过滤就是这样一个闭环:用户点按钮是输入,JS里做筛选是处理,页面重新渲染是输出。把这个闭环跑通十遍,比看一百页文档都管用。

2. 整体设计思路:数据驱动视图,而不是手动改页面

2.1 为什么选择“按钮触发过滤”而不是输入框实时过滤

价格过滤的实现方式有好几种,最常见的是输入框输入价格区间然后实时过滤,或者用滑块选择范围。我选择按钮触发,原因有三个。

第一,按钮的事件模型最简单。小程序的按钮绑定bindtap,点击触发一个函数,函数里改数据,页面自动更新。这条链路没有输入事件的防抖问题,没有输入法兼容问题,新手可以把全部注意力放在数据逻辑上,而不是被各种边界情况分散精力。

第二,按钮天然适合做“状态切换”。价格过滤通常有几个固定档位,比如“全部”、“100以下”、“100到500”、“500以上”。每个按钮对应一个过滤条件,点哪个就按哪个条件筛。这种离散的状态切换比连续区间更容易理解和调试。

第三,从教学角度看,按钮触发能强迫你理解“事件对象”和“数据绑定”的关系。输入框实时过滤容易让人把注意力放在输入值上,而按钮过滤让你必须显式地传递一个标识(比如按钮的data-price-type),这个标识就是过滤条件的来源。理解了这个传递过程,后面做更复杂的交互就顺了。

2.2 数据流的完整链路设计

整个过滤功能的数据流是这样的:页面初始化时,data里存两份商品数据,一份是原始完整列表(originalList),一份是当前展示列表(filteredList)。用户点击按钮,事件处理函数拿到按钮上的过滤类型标识,根据这个标识对originalList做筛选,把结果赋给filteredList。因为filteredList绑定了WXML的列表渲染,数据一变,页面自动更新。

这里有个关键设计决策:为什么要存两份数据?直接用一份不行吗?不行。如果你只有一份数据,过滤之后原始数据就丢了,点“全部”按钮就没法恢复。存两份是最简单的做法,原始数据永远不动,每次过滤都从原始数据里重新筛。这就像你有一筐苹果,每次按大小挑的时候都是从整筐里挑,而不是从上次挑剩下的里面再挑。

注意:原始数据在页面生命周期内应该保持不变。任何过滤、排序、搜索操作都应该基于原始数据的副本进行,这是数据管理的基本原则。

2.3 过滤条件的抽象方式

过滤条件我用一个字符串标识来表示,比如all、low、mid、high。为什么不直接用价格数字?因为按钮是固定的几个档位,用标识更直观,也更容易扩展。如果以后要加“300到800”这种新档位,只需要加一个按钮和对应的判断分支就行。

在事件处理函数里,我用switch语句根据标识走不同分支。每个分支里写对应的筛选逻辑。这种写法的好处是逻辑清晰,每个档位的筛选条件一目了然。坏处是如果档位很多,switch会很长。但对于入门练习来说,四五个档位完全够用,可读性比所谓的“优雅”更重要。

3. 核心细节拆解:从数据定义到事件绑定的完整实现

3.1 数据结构的设计与初始化

商品数据我用对象数组来存,每个对象包含名称和价格两个字段。价格用数字类型,不要用字符串。我见过有人把价格写成"199"这种字符串,然后比较的时候用字符串比较,结果"1000"比"200"小,因为字符串比较是逐字符比的。这种坑踩一次就记住了。

data: { originalList: [ { name: '商品A', price: 50 }, { name: '商品B', price: 120 }, { name: '商品C', price: 300 }, { name: '商品D', price: 80 }, { name: '商品E', price: 600 }, { name: '商品F', price: 450 }, { name: '商品G', price: 30 }, { name: '商品H', price: 900 } ], filteredList: [], currentFilter: 'all' }

初始化的时候,filteredList先给空数组,然后在onLoad生命周期里把它赋值为originalList的副本。为什么要用副本?因为如果你直接this.setData({ filteredList: this.data.originalList }),两个变量指向同一个数组引用,后面操作filteredList可能会影响到originalList。用slice()或者扩展运算符复制一份,确保两个数组完全独立。

onLoad() { this.setData({ filteredList: [...this.data.originalList] }); }

3.2 按钮的WXML结构与事件绑定

四个按钮分别对应全部、低价、中价、高价。每个按钮上绑定bindtap事件,同时用><view class="filter-bar"> <button bindtap="handleFilter">handleFilter(e) { const type = e.currentTarget.dataset.type; const originalList = this.data.originalList; let filteredList = []; switch (type) { case 'all': filteredList = [...originalList]; break; case 'low': filteredList = originalList.filter(item => item.price < 100); break; case 'mid': filteredList = originalList.filter(item => item.price >= 100 && item.price <= 500); break; case 'high': filteredList = originalList.filter(item => item.price > 500); break; default: filteredList = [...originalList]; } this.setData({ filteredList: filteredList, currentFilter: type }); }

这段代码里,filter方法是核心。它接收一个回调函数,回调返回true的项保留,返回false的项丢弃。回调里的item就是数组里的每个商品对象。用箭头函数是因为箭头函数不绑定自己的this,这里虽然没用到this,但养成习惯没坏处。

currentFilter这个字段我用来记录当前选中的是哪个档位,方便给按钮加高亮样式。比如选中的按钮加个背景色,让用户知道当前处于哪个过滤状态。这个字段不是必须的,但加上之后用户体验会好很多。

4. 实操过程全记录:从零到跑通的每一步

4.1 项目初始化与文件结构

新建一个小程序项目,清空默认的index页面内容。文件结构保持默认的index.wxml、index.wxss、index.js、index.json四个文件。不需要额外引入任何组件库或工具库,原生小程序的能力完全够用。

index.json里保持默认配置就行,不需要特别设置。index.wxss里我加了一些基础样式,让列表看起来不那么简陋。样式不是重点,但稍微花点时间调一下,做出来的东西自己看着舒服,学习动力也足一些。

.filter-bar { display: flex; justify-content: space-around; padding: 20rpx; background-color: #f5f5f5; } .filter-bar button { font-size: 26rpx; padding: 10rpx 20rpx; margin: 0 10rpx; } .product-list { padding: 20rpx; } .product-item { display: flex; justify-content: space-between; padding: 20rpx; border-bottom: 1rpx solid #eee; }

4.2 数据初始化的时机选择

数据初始化我放在onLoad里,而不是直接写在data里。为什么?因为data里写的是初始值,onLoad里做的是“根据初始值计算出展示值”。这个区分很重要。data里的originalList是原始数据,filteredList初始为空,然后在onLoad里把filteredList设为originalList的副本。这样逻辑上更清晰:原始数据是固定的,展示数据是计算出来的。

如果你直接把filteredList在data里写成和originalList一样的数组,虽然也能跑,但两份数据是独立写的,改了一个忘了改另一个就会出问题。用onLoad计算的方式,保证了两份数据的一致性。

提示:onLoad在小程序页面加载时只执行一次,适合做数据初始化和一次性计算。onShow每次页面显示都会执行,适合做需要刷新的操作。这个场景用onLoad就够了。

4.3 过滤逻辑的边界情况处理

写过滤逻辑的时候,边界情况最容易出错。比如“100以下”到底含不含100?“100-500”含不含100和500?这些边界如果不明确,用户点按钮的时候就会发现有些商品莫名其妙消失了或者重复出现了。

我的处理方式是:低价档用< 100,中价档用>= 100 && <= 500,高价档用> 500。这样100归中价档,500也归中价档,所有价格都被覆盖且不重叠。这个规则不是绝对的,你可以根据实际需求调整,但一定要保证所有档位加起来能覆盖全部数据,且没有交集。

还有一个边界是空结果。如果某个档位下没有商品,filteredList就是空数组,页面会显示空白。这时候最好加一个空状态提示,比如“暂无符合条件的商品”。这个提示用wx:if判断filteredList.length === 0来显示。

<view wx:if="{{filteredList.length === 0}}" class="empty-tip"> 暂无符合条件的商品 </view>

4.4 按钮高亮状态的实现

按钮高亮我用currentFilter和按钮的><button bindtap="handleFilter" >.filter-bar button.active { background-color: #07c160; color: #fff; }

这样点击哪个按钮,哪个按钮就变绿,用户一眼就能看出当前处于哪个过滤状态。这个交互细节虽然小,但能显著提升使用体验。

5. 常见问题与排查技巧实录

5.1 点击按钮没反应,页面数据不更新

这是新手最常见的问题。原因通常有三个:一是事件绑定写错了,比如写成了bindTap(大写T),小程序事件名是大小写敏感的,正确写法是bindtap。二是setData没调用,或者调用了但没传对参数。三是数据更新了但WXML里绑定的变量名写错了。

排查方法:在事件处理函数第一行加console.log('点击了'),看控制台有没有输出。如果有输出说明事件绑定没问题,问题在数据更新。然后在setData之前打印一下filteredList,看数据对不对。如果数据对但页面没变,检查WXML里的变量名是否和data里的字段名完全一致。

5.2 过滤结果不对,少了或多了某些商品

这种问题九成出在边界条件上。比如你写的是price > 100 && price < 500,那100和500的商品就丢了。或者你写的是price >= 100 || price <= 500,那所有商品都满足条件,等于没过滤。

排查方法:把过滤条件单独拿出来,用几个典型价格手动算一下。比如价格100,代入你的条件,看结果是true还是false,是否符合预期。这个手动验证的过程虽然笨,但最有效。

5.3 切换过滤条件后,原始数据被修改了

这个问题我在前面提过,根源是数组引用共享。如果你在过滤的时候直接操作了originalList,比如用了splice或者直接赋值,原始数据就被破坏了。解决办法是永远用filter、map、slice这些不改变原数组的方法,或者用扩展运算符复制一份再操作。

注意:filter和map返回新数组,不改变原数组。splice、push、pop、shift、unshift、sort、reverse都会改变原数组。入门阶段尽量只用前两个。

5.4 常见问题速查表

问题现象可能原因排查方法解决方案
点击按钮无反应事件名大小写错误检查WXML中是否为bindtap改为全小写bindtap
数据变了页面没变未调用setData在函数内打印日志用this.setData更新数据
过滤结果为空条件写反或边界错误手动代入典型值验证调整比较运算符
原始数据被破坏使用了改变原数组的方法检查是否用了splice等改用filter或先复制
按钮高亮不切换currentFilter未更新检查setData是否包含该字段在setData中一并更新
列表渲染报错wx:key重复或缺失检查wx:key的值是否唯一用唯一字段作为key

5.5 几个我踩过的坑

第一个坑:>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询