uni-app 数据绑定和事件处理,用一个筛选表单讲清楚
页面真正变复杂往往不是因为元素多而是因为用户开始操作了。输入关键词。选择分类。调整价格。点搜索。页面要跟着这些操作变化。数据变了视图要更新用户点了按钮方法要执行某个字段变了可能还要触发请求。这一篇不用零散例子讲ref、v-model、computed、watch。我们写一个商品筛选表单把它们串起来。太长不看版这一篇用商品筛选表单讲交互链路。用户输入或选择以后事件函数更新响应式数据computed负责生成展示文案和派生值点击搜索时再把页面状态整理成请求参数。能用computed表达的派生数据不要重复存一份。这个表单要做什么页面上有四件事输入关键词。选择分类。输入最低价和最高价。点击搜索得到请求参数。交互链路是这样。用户输入或选择事件函数修改响应式数据computed 生成派生结果视图自动更新点击搜索时组装参数文件位置pages/goods/filter.vue也别忘了注册到pages.json。{path:pages/goods/filter,style:{navigationBarTitleText:筛选商品}}先把响应式数据建好在 Vue3 的script setup里最常用的是ref和reactive。这里表单字段属于一个整体用reactive很顺手。script setup import { reactive, computed, watch } from vue const categories [全部, 食品, 日用品, 数码, 服饰] const form reactive({ keyword: , categoryIndex: 0, minPrice: , maxPrice: }) /scriptreactive适合管理一组相关字段比如表单。如果只是一个简单值比如count、visible、status用ref更直接。有一个新手很容易混的点ref在脚本里要.value在模板里不用。reactive对象字段正常用点操作就行。constcountref(0)count.value模板里写text{{ count }}/text不用写count.value。输入框有两种写法商品关键词可以用v-model。input classinput v-modelform.keyword placeholder输入商品关键词 /v-model适合普通输入框能少写一段事件赋值逻辑。但你仍然要理解事件写法因为 uni-app 里很多组件的值都在事件对象里。比如价格输入我们故意用input写。input classinput typedigit :valueform.minPrice placeholder最低价 inputhandleMinPriceInput /consthandleMinPriceInput(e){form.minPricee.detail.value}在 uni-app 和小程序场景里表单事件值经常在e.detail.value。你可以把它理解成组件把用户操作结果放进事件详情里再交给你的方法。视图页面状态handleInputinput用户视图页面状态handleInputinput用户输入价格触发 input 事件读取 e.detail.value 并写入 form视图更新picker 也走事件分类选择用picker。picker :rangecategories :valueform.categoryIndex changehandleCategoryChange view classpicker-value {{ currentCategory }} /view /pickerconsthandleCategoryChange(e){form.categoryIndexNumber(e.detail.value)}picker返回的是索引当前分类文本可以通过计算属性得到。constcurrentCategorycomputed((){returncategories[form.categoryIndex]})computed 适合派生数据不要重复存如果一个值可以由其他字段算出来就不要再存一份。比如当前分类文本、是否填写价格、搜索按钮文案都可以算出来。consthasPriceRangecomputed((){returnBoolean(form.minPrice||form.maxPrice)})constsearchSummarycomputed((){constparts[]if(form.keyword.trim()){parts.push(关键词${form.keyword.trim()})}if(currentCategory.value!全部){parts.push(分类${currentCategory.value})}if(hasPriceRange.value){parts.push(价格${form.minPrice||0}-${form.maxPrice||不限})}returnparts.length0?parts.join():还没有设置筛选条件})这里不要额外维护一个summary字段。否则你要在关键词变化、分类变化、价格变化时到处同步一漏就错。数据绑定里很重要的一条经验是源数据只留一份能算出来的就算出来。watch 适合副作用但不要滥用computed适合算值。watch适合做副作用。什么叫副作用比如数据变化以后你要请求接口、写缓存、打印日志、上报埋点这些都不是单纯展示。比如关键词变化后你想提示用户正在准备搜索。watch(()form.keyword,(newKeyword){if(!newKeyword.trim())returnconsole.log(关键词变化了新的关键词是,newKeyword)})但初学阶段要克制使用watch。如果你只是想显示一个由其他数据计算出来的文本用computed。如果你想在关键词变化后自动请求接口要考虑防抖或取消旧请求。否则用户连续输入a、ab、abc你可能瞬间打三次请求旧请求还可能比新请求更晚回来。这个问题会在网络请求那篇专门讲。点击搜索时组装参数搜索按钮不要直接把整个form原样扔给接口。页面状态是给页面用的请求参数是给接口用的中间最好做一次整理。constbuildSearchParams(){constparams{}constkeywordform.keyword.trim()if(keyword){params.keywordkeyword}if(currentCategory.value!全部){params.categorycurrentCategory.value}if(form.minPrice){params.minPriceNumber(form.minPrice)}if(form.maxPrice){params.maxPriceNumber(form.maxPrice)}returnparams}consthandleSearch(){constparamsbuildSearchParams()uni.showToast({title:Object.keys(params).length0?开始搜索:请先设置条件,icon:none})console.log(搜索参数,params)}这样做有两个好处。页面字段可以保留页面需要的形态比如categoryIndex。接口参数可以整理成接口需要的形态比如category、minPrice、maxPrice。这也是项目里很常见的分层页面状态归页面请求参数归请求。完整页面代码template view classpage view classpanel view classform-item text classlabel关键词/text input classinput v-modelform.keyword placeholder输入商品关键词 / /view view classform-item text classlabel分类/text picker :rangecategories :valueform.categoryIndex changehandleCategoryChange view classpicker-value {{ currentCategory }} /view /picker /view view classprice-row view classprice-item text classlabel最低价/text input classinput typedigit :valueform.minPrice placeholder0 inputhandleMinPriceInput / /view view classprice-item text classlabel最高价/text input classinput typedigit :valueform.maxPrice placeholder不限 inputhandleMaxPriceInput / /view /view view classsummary {{ searchSummary }} /view button typeprimary clickhandleSearch 搜索商品 /button /view /view /template script setup import { reactive, computed, watch } from vue const categories [全部, 食品, 日用品, 数码, 服饰] const form reactive({ keyword: , categoryIndex: 0, minPrice: , maxPrice: }) const currentCategory computed(() { return categories[form.categoryIndex] }) const hasPriceRange computed(() { return Boolean(form.minPrice || form.maxPrice) }) const searchSummary computed(() { const parts [] if (form.keyword.trim()) { parts.push(关键词${form.keyword.trim()}) } if (currentCategory.value ! 全部) { parts.push(分类${currentCategory.value}) } if (hasPriceRange.value) { parts.push(价格${form.minPrice || 0} - ${form.maxPrice || 不限}) } return parts.length 0 ? parts.join() : 还没有设置筛选条件 }) const handleCategoryChange (e) { form.categoryIndex Number(e.detail.value) } const handleMinPriceInput (e) { form.minPrice e.detail.value } const handleMaxPriceInput (e) { form.maxPrice e.detail.value } const buildSearchParams () { const params {} const keyword form.keyword.trim() if (keyword) { params.keyword keyword } if (currentCategory.value ! 全部) { params.category currentCategory.value } if (form.minPrice) { params.minPrice Number(form.minPrice) } if (form.maxPrice) { params.maxPrice Number(form.maxPrice) } return params } const handleSearch () { const params buildSearchParams() uni.showToast({ title: Object.keys(params).length 0 ? 开始搜索 : 请先设置条件, icon: none }) console.log(搜索参数, params) } watch( () form.keyword, (newKeyword) { if (!newKeyword.trim()) return console.log(关键词变化了, newKeyword) } ) /script style langscss .page { min-height: 100vh; padding: 32rpx; background: #f6f7fb; } .panel { padding: 28rpx; border-radius: 16rpx; background: #ffffff; } .form-item { margin-bottom: 28rpx; } .label { display: block; margin-bottom: 12rpx; font-size: 26rpx; color: #374151; } .input, .picker-value { min-height: 80rpx; padding: 0 24rpx; border: 1rpx solid #d1d5db; border-radius: 10rpx; font-size: 28rpx; line-height: 80rpx; } .price-row { display: flex; gap: 20rpx; } .price-item { flex: 1; } .summary { margin: 8rpx 0 28rpx; padding: 20rpx; border-radius: 10rpx; background: #f3f4f6; font-size: 26rpx; color: #4b5563; } /style这条链路比背语法重要数据绑定和事件处理的核心不是某个单独 API。它是一条链路用户操作组件。组件触发事件。事件函数读取e.detail.value或直接通过v-model更新数据。响应式数据变化。模板自动更新。computed负责派生展示。点击搜索时把页面状态整理成请求参数。这条链路顺了页面就从静态模板变成了真正能交互的应用。后面做列表、路由、请求、状态管理本质上都还在处理同一件事数据放哪里谁来改改完以后谁会受到影响。这里容易翻车在脚本里忘记.valueref在脚本里读写要用.value模板里会自动解包。把 computed 能算出的值再存一份比如筛选摘要可以由表单字段算出来就不要额外维护summary状态。watch 里直接请求接口关键词一变就请求用户连续输入时会打很多请求。网络请求篇会讲防抖、取消旧请求和请求序号校验参见08-网络请求与页面状态设计.md。页面状态和请求参数混在一起页面可以存categoryIndex但接口可能要的是category。提交前做一次buildSearchParams项目后面会清楚很多。自己试试给筛选表单加一个“只看有库存”的开关并把它放进搜索参数。给价格区间加一个校验最低价不能大于最高价。参考资料Vue Composition API 文档https://vuejs.org/api/composition-api-setup.htmlVue computed 文档https://vuejs.org/guide/essentials/computed.htmlDCloud input 组件文档https://uniapp.dcloud.net.cn/component/input.html

相关新闻

如何用Resurrectio生成ReStructuredText文档:自动化测试报告终极指南

如何用Resurrectio生成ReStructuredText文档:自动化测试报告终极指南

如何用Resurrectio生成ReStructuredText文档:自动化测试报告终极指南 【免费下载链接】resurrectio CasperJS test recorder Chrome extension 项目地址: https://gitcode.com/gh_mirrors/re/resurrectio 想要快速生成专业的自动化测试文档吗?Res…

2026/10/20 11:45:03 阅读更多 →
枚举sys模块

枚举sys模块

枚举sys模块 在内核中枚举进程的模块时,我们主要通过PEB中的_PEB_LDR_DATA中的三个链表来获取进程加载的模块,使用这种方法需要临时将当前地址空间切换到对应进程的地址空间,因为PEB是在进程虚拟地址空间中的,而不是内核地址空间。…

2026/10/18 11:33:40 阅读更多 →
课堂文化视域下学生数学情感培育的实现途径

课堂文化视域下学生数学情感培育的实现途径

《课堂文化视域下学生数学情感培育的实现途径》(庄海翔,《基础教育研究》2020.02,)本文立足课堂文化视角,针对当前数学教学重知识、轻情感,课堂 “盯盘补” 模式催生学生畏难消极情绪的问题展开研究。核心观…

2026/10/20 8:10:56 阅读更多 →

最新新闻

深入解析上下文模式:从参数透传到架构级上下文管理

深入解析上下文模式:从参数透传到架构级上下文管理

1. 从"上下文模式"说起:一个被低估的架构决策第一次看到"context-mode"这个词,很多人会下意识地把它归类成某个框架里的配置项,或者某个API的一个参数枚举值。但如果你真正在工程一线待过几年,就会明白&#…

2026/10/20 15:31:21 阅读更多 →
部落竞争与成员合作算法(CTCM)实现无人机城市三维避障航迹规划

部落竞争与成员合作算法(CTCM)实现无人机城市三维避障航迹规划

楼宇之间穿行这件事,我在之前的项目里吃过大亏。当时用的是二维平面路径规划,无人机飞着飞着就撞上了高层建筑的侧立面——原因很简单,二维路径只考虑水平面的躲避,根本没算高楼在不同高度层的“投影范围”,更没考虑爬…

2026/10/20 15:31:21 阅读更多 →
CANOCO 5.0 PCA分析完全指南:从数据导入到排序图解读

CANOCO 5.0 PCA分析完全指南:从数据导入到排序图解读

简介:CANOCO 5.0 是一款面向生态学与植物群落研究者的专业排序分析工具,主要用于 PCA、对应分析等多元统计,帮助将样方或植物种排列在特定空间中,使排序轴反映生态梯度,进而解释植被分布与环境因子之间的连续关系。对于…

2026/10/20 15:31:21 阅读更多 →
附合导线与四等水准平差表格的Excel自动生成实战

附合导线与四等水准平差表格的Excel自动生成实战

做测量最烦的从来不是外业晒大太阳,而是内业排表。上周刚交完一个项目的测量资料,一条附合导线带四个转点,一段约三公里的四等水准路线,白天的数据其实测得很顺,到了晚上却卡在了表格上:角度闭合差、坐标增…

2026/10/20 15:31:21 阅读更多 →
基于单片机的红外测距仪:从光电转换到查表标定全解析

基于单片机的红外测距仪:从光电转换到查表标定全解析

红外测距仪这名字听着唬人,其实拆开看就是两件事:用红外光“摸”一下目标有多远,再把距离显示出来。最近帮A同学捋一套基于单片机的红外测距仪设计,从原理图到代码再到标定整体过了一遍,发现这东西对入门的价值被严重低…

2026/10/20 15:31:21 阅读更多 →
WebSocket断线重连实战:心跳机制、指数退避与消息补偿方案

WebSocket断线重连实战:心跳机制、指数退避与消息补偿方案

做个长连接项目,断线重连这关早晚要过。我见过不少团队,连接正常时觉得一切都很简单,直到某天用户反馈“消息发不出去”“聊天记录中间断了一截”,才意识到WebSocket的稳定性问题才是整个项目的分水岭。这篇文章不打算从握手细节讲…

2026/10/20 15:30:20 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →