☰
从0到1开发微信小程序:WorkBuddy任务管理工具保姆级教程
2026/10/20 16:15:04 网站建设 项目流程

接手这个项目是个临时决定。朋友所在的团队想找一个轻量工具来管理日常任务,找了一圈市面上的协作软件都不太合口味,最后撂给我一句:"要不你写个微信小程序吧,就叫WorkBuddy,我们几个人用。"就这样,我从零开始把一个小程序做到能上线、能真机使用,中间踩了不少坑,也理清了一套完整的从0到1开发路线。这篇内容没有任何保留,适合没写过小程序的开发者、想验证产品想法的个人开发者,以及从后端转前端的全栈工程师参考。我会从需求定位讲到注册配置,再到页面开发、数据持久化,最后是上线审核和避坑实录,每一环节都有可复制的代码和步骤。

很多教程只会告诉你"怎么调API",却不会告诉你"为什么这么设计、审核为什么被拒、真机为什么和白屏不一样"。这篇我会把项目背后的决策逻辑也一并拆开讲,保证你看完不只是多了一堆代码片段,而是真正具备独立开发一个小程序的能力。

1. WorkBuddy到底做什么:先把需求说清楚

1.1 从名字看产品:一种轻量工具类小程序的定位

WorkBuddy拆开看就是 Work(工作)+ Buddy(伙伴),定位是"工作场景里的轻量伙伴"。最初的需求非常简单:一个团队内部的任务管理工具,能够记录"今天谁负责什么、进展到哪一步了、有没有完成"。不需要复杂的审批流、OKR、甘特图,那些对几个人的小团队来说根本用不上。

我一开始就想清楚了,这类工具类小程序的生存逻辑不是"功能越多越好",而是"打开就记录,记录完就走"。用户用完即走,留存靠的是数据能持久化、下次打开还能看到上次的任务状态。所以产品设计的核心原则只有一条:所有操作尽量控制在三个页面以内。

功能选型上,我做了一个MVP(最小可行产品)范围控制:登录和账号体系不要,因为个人主体的小程序如果没做好隐私说明很容易在审核时被卡;多人实时协作不要,因为涉及后端和数据库,会拖慢上线时间。第一版只解决单人视角下的任务记录问题,团队协作通过定期分享或微信群同步代替。

这个克制帮我省了至少两周的开发时间,也让审核材料变得非常干净。如果你也想做工具类小程序,我真心建议先列一个"绝对不做"清单,比列功能清单更重要。

1.2 功能清单:MVP阶段只做三件事

WorkBuddy第一版严格只做三件事:

  1. 任务管理:添加任务、删除任务、编辑任务内容,每条任务记录包含标题、截止日期、优先级。
  2. 状态流转:任务有三种状态——待办、进行中、已完成,通过按钮一键切换。
  3. 数据持久化:所有任务保存到微信本地缓存,关闭小程序再打开,数据还在。

这三件事分别对应了三个页面:任务列表页(首页)、添加/编辑任务页、数据总览页(简单的完成率统计)。

刚开始我也犹豫要不要做"账号系统 + 云端同步",后来算了笔账:没有账号系统,用户打开即用,转化率高;用本地存储,不需要处理网络异常、登录态过期、服务器费用,开发量直接砍掉一大半。对一个工具类小程序来说,MVP阶段的本地存储完全够用,等验证完需求、确认有人真的每天在用,再上云开发也不迟。

这里也给大家一个判断标准:如果你的应用只有单一用户使用场景,或者多用户数据同步不是核心卖点,本地存储就是性价比最高的选择。别被"上云"绑架。

1.3 为什么选择微信小程序而不是App或H5

这个项目启动前,我对比了一下三个技术载体,选择小程序不是因为情怀,而是算出来的:

维度微信小程序原生AppH5网页
获客成本微信内扫码/搜索即用,不需要安装应用商店下载安装,流失率高需输入网址或关注入口,触达弱
开发语言前端JS + 类HTML/CSS,上手快Android/iOS两套原生,成本高前端技术,但能力受限
系统能力调用微信开放能力、本地缓存最强,但权限复杂受浏览器沙箱限制
发布审核微信审核,流程清晰应用商店审核,周期长且规则多无需审核,但无分发渠道
团队规模1~2人可维护至少需要移动端2人1人可维护

WorkBuddy的用户都是微信重度用户,日常工作沟通本来就在微信里完成。做小程序意味着用户不需要跳出聊天环境去下载App,发一个二维码到群里就能完成分发,这个传播效率是App完全比不上的。

另外还有一个现实原因:作为一个个人开发者的试水项目,我没有精力和财力同时维护iOS和Android两个版本。小程序框架统一了封装层,同一套代码在两端表现基本一致,踩坑也都集中在微信框架内部,排查路径很清晰。如果你想快速验证一个想法,小程序绝对是一个值得优先考虑的载体。

2. 开发前的准备工作:注册、工具、空项目

2.1 注册小程序账号与AppID获取

开发WorkBuddy的第一步不是写代码,而是去微信公众平台注册一个小程序账号。打开"微信公众平台"官网,在注册入口选择"小程序",按流程填写邮箱、密码、激活邮箱,然后进入主体信息登记。主体类型建议直接选"个人",因为WorkBuddy是个人开发者的练手项目,个人主体的注册流程最简单,也不需要营业执照。

注册过程中会要求填写小程序名称、简介和头像。这里有个容易被忽略的点:小程序名称一旦确定,修改是有次数限制的,所以别随便起一个名字就提交。WorkBuddy这个名称是纯英文,审核时一般没问题,但如果你想起中英混排的名字,建议先查一下是否和已有的小程序重名,避免被驳回。

注册完成后,进入"小程序管理后台",在"设置-开发管理-开发设置"里能看到AppID。AppID相当于小程序的身份证,在开发者工具里新建项目时必须用到。很多人会问"测试号能不能用",我的建议是直接用正式AppID。虽然测试号省了注册步骤,但很多能力接口(比如订阅消息、部分开放标签)在测试号下是受限的,开发到一半再切换成本更高。

2.2 开发者工具的安装与项目初始化

下载"微信开发者工具",这是官方提供的一体化IDE,内置模拟器、调试器、代码编辑器。安装包建议选"稳定版",不要用"预发布版",因为预发布版经常有版本兼容问题,对新手来说很容易被报错信息劝退。

安装完成后打开工具,选择"小程序"类型,点击"新建项目"。项目名称填WorkBuddy,目录选一个空文件夹,AppID填入刚才获取的正式AppID,开发模式选"小程序",模板选"JavaScript基础模板"即可。这里不建议选TypeScript模板,因为对小项目来说JS的灵活度更高,入门门槛也更低。

创建成功后你会看到一个三栏界面:左侧是模拟器,中间是编辑器,右侧是调试器。模拟器默认显示iPhone的机型,可以随时切换成Android机型,调试器里的Console面板是你排错的第一现场。

工具有几个快捷键值得记住:

  • Cmd/Ctrl + S:保存文件并触发编译
  • Cmd/Ctrl + B:打开/关闭调试器
  • Cmd/Ctrl + P:快速跳转文件

前期把这三个快捷键用熟,开发效率会明显提升。

2.3 目录结构:每一个文件到底干嘛的

初始化之后你会看到一套标准目录,新手往往会看懵。WorkBuddy的前期结构是这么设计的:

WorkBuddy/ ├─ app.js // 应用入口,这里注册全局逻辑 ├─ app.json // 全局配置,页面路由、窗口样式、tabBar都在这 ├─ app.wxss // 全局样式表 ├─ sitemap.json // 小程序索引配置,有点像网站的robots.txt ├─ pages/ │ ├─ index/ // 首页(任务列表) │ │ ├─ index.js │ │ ├─ index.wxml │ │ ├─ index.wxss │ │ └─ index.json │ ├─ edit/ // 添加/编辑任务页 │ ├─ stats/ // 数据统计页 ├─ utils/ │ └─ task.js // 工具库,封装任务数据的读写 └─ project.config.json // 项目配置,networkTimeout等

每一个页面都由四个同名的文件组成:.js负责逻辑和数据处理,.wxml负责页面结构,.wxss负责样式,.json负责页面级配置。这种"四件套"的目录设计是微信小程序的固定套路,刚开始可能觉得繁琐,但用多了会发现每类文件职责单一,改结构不会牵连逻辑,挺适合工程化维护的。

app.json是所有页面的大脑,pages数组里第一个元素就是小程序启动后默认展示的首页。WorkBuddy的app.json最开始长这样:

{ "pages": [ "pages/index/index", "pages/edit/edit", "pages/stats/stats" ], "window": { "navigationBarTitleText": "WorkBuddy", "navigationBarBackgroundColor": "#2B3A4A", "navigationBarTextStyle": "white", "backgroundColor": "#F5F6FA" }, "tabBar": { "color": "#8B8B8B", "selectedColor": "#2B3A4A", "list": [ { "pagePath": "pages/index/index", "text": "任务" }, { "pagePath": "pages/stats/stats", "text": "统计" } ] } }

tabBar是底部导航栏,WorkBuddy放了"任务"和"统计"两个入口,这样用户在列表和统计之间切换非常自然。需要注意的是,tabBar的pagePath必须是页面路径,而且tabBar页面不能通过wx.navigateTo跳转,要用wx.switchTab,这个细节后面我会细讲。

3. 从第一个页面到完整交互:核心页面开发实录

3.1 首页布局:用WXML和WXSS搭出工作台界面

WorkBuddy的首页是任务列表,也是用户打开小程序第一个看到的页面。我的目标是让用户一进来就知道"今天该干什么、进行中的任务有哪些、完成了多少"。

首页的WXML结构是这样设计的:

<view class="page"> <view class="header"> <text class="date">{{today}}</text> <view class="stats-cards"> <view class="card"> <text class="num">{{totalCount}}</text> <text class="label">全部任务</text> </view> <view class="card"> <text class="num">{{doneCount}}</text> <text class="label">已完成</text> </view> <view class="card"> <text class="num">{{pendingCount}}</text> <text class="label">待办</text> </view> </view> </view> <view class="task-list"> <view class="task-item {{item.status}}" wx:for="{{tasks}}" wx:key="id" > <view class="task-left"> <text class="task-title">{{item.title}}</text> <text class="task-meta">{{item.date}} · {{item.priority}}</text> </view> <view class="task-right"> <view class="status-btn">.stats-cards { display: flex; justify-content: space-between; margin-top: 16rpx; } .card { flex: 1; background: #fff; border-radius: 16rpx; padding: 24rpx 0; text-align: center; margin-right: 20rpx; box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06); } .card:last-child { margin-right: 0; }

3.2 待办列表:数据绑定与渲染

页面结构搭好之后,核心工作是把JS里的数据渲染到WXML上。小程序的数据绑定用的语法和Vue很像,但有一个非常重要的区别:不能直接修改data对象,必须通过this.setData()更新数据。

我一开始犯过错,直接写了this.data.tasks.push(newTask),结果页面纹丝不动,因为渲染层根本没有收到更新通知。正确的做法是:

Page({ data: { tasks: [], today: '', totalCount: 0, doneCount: 0, pendingCount: 0 }, onShow() { this.loadTasks(); }, loadTasks() { const tasks = getTaskList(); const today = new Date().toLocaleDateString('zh-CN', { month: 'long', day: 'numeric', weekday: 'long' }); const doneCount = tasks.filter(t => t.status === 'done').length; const pendingCount = tasks.filter(t => t.status !== 'done').length; this.setData({ tasks, today, totalCount: tasks.length, doneCount, pendingCount }); }, toggleStatus(e) { const id = e.currentTarget.dataset.id; const tasks = this.data.tasks.map(t => { if (t.id === id) { t.status = t.status === 'done' ? 'todo' : 'done'; t.statusText = t.status === 'done' ? '已完成' : '进行中'; } return t; }); this.setData({ tasks }); saveTaskList(tasks); }, deleteTask(e) { const id = e.currentTarget.dataset.id; const tasks = this.data.tasks.filter(t => t.id !== id); this.setData({ tasks }); saveTaskList(tasks); }, goToEditPage() { wx.navigateTo({ url: '/pages/edit/edit' }); } });

上面代码里有个细节值得一提:wx:for渲染列表时,每个任务对象都用了wx:key="id"来标识循环项,这样数据变化时小程序能精准diff出哪些节点需要更新,渲染性能和交互流畅度会明显提升。我见过不少初学代码直接写wx:key="*this",如果列表项是一个对象数组,这个写法会导致渲染异常,所以务必给每个任务生成唯一id(可以用Date.now()加随机后缀)。

状态切换按钮是通过><view class="page"> <view class="form"> <view class="field"> <text class="label">任务标题</text> <input class="input" placeholder="例如:整理周报" value="{{title}}" bindinput="onTitleInput" maxlength="30" /> </view> <view class="field"> <text class="label">截止日期</text> <picker mode="date" value="{{date}}" bindchange="onDateChange"> <view class="picker-value">{{date || '请选择日期'}}</view> </picker> </view> <view class="field"> <text class="label">优先级</text> <radio-group bindchange="onPriorityChange"> <radio value="高">高</radio> <radio value="中">中</radio> <radio value="低">低</radio> </radio-group> </view> <button class="save-btn" bindtap="saveTask">保存任务</button> </view> </view>

对应的JS逻辑:

Page({ data: { title: '', date: '', priority: '中' }, onTitleInput(e) { this.setData({ title: e.detail.value }); }, onDateChange(e) { this.setData({ date: e.detail.value }); }, onPriorityChange(e) { this.setData({ priority: e.detail.value }); }, saveTask() { const { title, date, priority } = this.data; if (!title.trim()) { wx.showToast({ title: '请填写任务标题', icon: 'none' }); return; } if (!date) { wx.showToast({ title: '请选择截止日期', icon: 'none' }); return; } const task = { id: Date.now() + '-' + Math.floor(Math.random() * 1000), title: title.trim(), date, priority, status: 'todo', statusText: '待办', createdAt: Date.now() }; const tasks = getTaskList(); tasks.unshift(task); saveTaskList(tasks); wx.showToast({ title: '已保存', icon: 'success' }); setTimeout(() => { wx.navigateBack(); }, 300); } });

表单校验我放在了保存函数的开头,一旦校验失败就用wx.showToast弹出提示。这里故意没有使用复杂的表单校验库,因为小程序表单字段少,手写校验简单直接,也少一个依赖。

picker组件是微信内置的日期选择器,用户点击后会弹出滚动选择面板,选中的日期会显示在页面上。对于WorkBuddy这种轻量工具来说,原生picker已经够用,不需要第三方日历组件。

保存后做一个300毫秒的延迟再navigateBack返回首页,是为了让"已保存"的toast提示先显示出来,否则页面立刻返回,toast会在新页面上弹出,容易被用户忽略。这个细节看起来小,实际体验差别很大。

3.4 页面跳转:navigateTo与switchTab的选择

页面跳转是每个小程序开发者的必修课。WorkBuddy里有两类跳转:普通页面跳转和tab页跳转,处理方式完全不同。

首页的悬浮按钮是跳转到一个普通页面(添加任务页),所以用wx.navigateTo:

wx.navigateTo({ url: '/pages/edit/edit' });

navigateTo会保留当前页面,把新页面压入页面栈,所以用户可以按左上角返回键回到首页。但注意一个坑:页面栈最多只能压10层,如果你在多层页面之间反复navigateTo而不navigateBack,第五六次之后新页面就无法打开了,调用会直接失败。WorkBuddy的页面层级很浅(首页 -> 编辑页),所以没有触发这个限制,但如果你做的是一个流程深的应用,一定要在关键节点调用wx.redirectTo替换当前页面,或者用wx.reLaunch重置整个页面栈。

如果要从一个tabBar页面跳到另一个tabBar页面,情况完全不同。比如首页跳转"统计"页,用navigateTo会报错,必须换成wx.switchTab:

wx.switchTab({ url: '/pages/stats/stats' });

switchTab会关闭所有非tab页面,跳转到指定的tabBar页面,且该跳转没有返回按钮,因为tab本身就是平级切换。

WorkBuddy还涉及一个"从编辑页返回后刷新数据"的需求。常规做法是在首页的onShow生命周期里重新读取数据,因为navigateBack返回时,下一页的onShow一定会重新执行。这也是我为什么会把loadTasks()放在onShow而不是onLoad里的原因:onLoad只在页面首次加载时执行一次,而onShow每次显示都会执行。从保存页返回首页,数据自然就刷新了。

4. 数据从哪来:本地存储与云开发的取舍

4.1 三种数据方案的对比

开发到数据层时,可能会犹豫:数据到底放哪。我梳理了三种方案,各有利弊:

方案优点缺点适用场景
本地存储零成本、开发快、无需网络数据只在本机,换设备不共享单人工具、原型验证
微信云开发免运维数据库、联运微信登录小额付费后会自动扣费,需注意限额需要多设备同步、轻协作
自建后端完全可控、可扩展需服务器、域名备案、接口开发复杂业务、多用户企业应用

WorkBuddy是一个纯个人工具,使用场景基本是"在手机上打开,记录几个任务,当天多次查看",单机本地存储完全够用。而且没有后端就没有网络失败问题,离线也能用。

如果你是做一个需要跨设备同步的任务工具,那本地存储就不合适了。这时我建议用微信云开发,它的云数据库可以自动处理用户身份,不需要自己写登录接口。但注意,云开发有免费额度和收费点,比如数据库读写次数、云函数调用次数、存储空间都有限额,上线前要做一下用量评估。

4.2 使用wx.setStorageSync实现本地数据持久化

本地存储的小程序API分为同步和异步两套,WorkBuddy全部用的是同步版本:

  • wx.getStorageSync(key):读取
  • wx.setStorageSync(key, value):写入
  • wx.removeStorageSync(key):删除

这里为什么不建议用异步版本?因为异步API需要写回调函数,在读取完数据之前,后续逻辑没法执行,代码容易变成回调地狱。同步API在数据量小的时候,性能影响可以忽略不计,代码却非常直观。

存储的数据格式必须是JSON可序列化的,所以我把任务数组直接存在一个key下面:

const TASK_KEY = 'workbuddy_tasks'; function getTaskList() { return wx.getStorageSync(TASK_KEY) || []; } function saveTaskList(tasks) { wx.setStorageSync(TASK_KEY, tasks); }

注意wx.getStorageSync在key不存在时返回空字符串'',而不是null或[],所以读取时一定要做|| []兜底,否则后续对数组做.map会直接报错。

本地存储的上限是10MB左右,对纯文本的任务数据来说,几百条任务只占几个KB,完全不用担心。但如果之后你要存图片、音频,就必须换方案了。

4.3 封装数据读写模块:把逻辑从Page里抽出来

数据读写逻辑如果散落在每个Page里,项目很快就乱套。我在utils目录下建了一个task.js,专门负责任务的增删改查和存储,这样Page里的代码只需要调用工具函数,不需要关心数据是怎么存储的,也方便以后替换成云开发。

utils/task.js的完整实现:

const TASK_KEY = 'workbuddy_tasks'; function getTaskList() { return wx.getStorageSync(TASK_KEY) || []; } function saveTaskList(tasks) { wx.setStorageSync(TASK_KEY, tasks); } function addTask(task) { const tasks = getTaskList(); tasks.unshift(task); saveTaskList(tasks); return task; } function updateTask(id, updates) { const tasks = getTaskList().map(t => { if (t.id === id) { return { ...t, ...updates }; } return t; }); saveTaskList(tasks); } function deleteTask(id) { const tasks = getTaskList().filter(t => t.id !== id); saveTaskList(tasks); } function getStats() { const tasks = getTaskList(); return { total: tasks.length, done: tasks.filter(t => t.status === 'done').length, pending: tasks.filter(t => t.status !== 'done').length }; } module.exports = { getTaskList, saveTaskList, addTask, updateTask, deleteTask, getStats };

模块化的收益在页面变多之后非常明显。比如统计页需要读取"已完成数量",直接调用getStats()就行,不用再写一遍过滤逻辑。首页的toggleStatus也只负责改UI状态,真正的持久化交给updateTask,每个函数只做一件事,排查问题时思路特别清晰。

如果你以后决定迁到云开发,只需要改utils/task.js内部实现,把wx.getStorageSync换成db.collection('tasks').get(),所有Page层代码一行都不用动。这就是模块化对抗需求变化的底气。

5. 上线发布全流程:从预览到审核

5.1 真机预览与调试

代码在模拟器里跑通,不等于真机上没问题。我在WorkBuddy开发到一半时点了一下开发者工具上的"预览"按钮,生成一个二维码,用手机微信扫码后,第一次在真机上打开,发现首页布局和模拟器里看到的完全不一样——模拟器里正常的间距到了真机上被拉大了。

后来发现原因是我的WXSS里写了固定的px尺寸,真机屏幕宽度和模拟器不一致,导致布局错乱。换成rpx之后就正常了。所以我的第一条建议是:从第一天开始就用真机预览,不要等到全部写完再真机调试。

预览时有个"调试模式"开关,打开后真机页面底部会出现一个调试按钮,点击可以查看类似开发者工具里的Console日志和Network请求,对排查真机上的JS报错特别有用。

还有一类问题只会在真机上暴露:本机网络环境。如果你的页面要请求本地开发服务器,比如http://localhost:3000,在真机上是不可能成功的,因为手机访问的是电脑的IP。WorkBuddy没有网络请求,所以绕开了这个问题,但你在开发其他项目时一定要注意微信的合法域名校验——真机上访问HTTP域名需要在公众平台配置request合法域名,否则请求直接fail。

5.2 上传版本、填写审核信息

当WorkBuddy功能稳定后,我在开发者工具右上角点击"上传",填写版本号1.0.0和项目备注"首个可体验版本,包含任务管理、统计功能",这个操作会把代码上传到微信后台,成为"开发版本"。

接下来需要登录微信公众平台,在"管理-版本管理"里找到这个开发版本,点击"提交审核"。提交前要填写审核信息,这部分很多人容易疏忽。WorkBuddy的填写经验如下:

  • 服务类目选择"工具-效率",这个类目和任务管理工具的定位完全匹配。
  • 功能页面描述,我写的是"提供任务新增、删除、状态管理、完成统计功能,所有数据存储在用户本地,无需服务器"。
  • 如果有测试账号或特殊登录方式,要在备注中说明。WorkBuddy没有账号体系,我就直接注明"无需登录,打开即用"。

这里我要特别提醒:类目选择一定要和实际功能匹配。我见过有开发者做一个阅读工具却选了"游戏"类目,结果审核被驳回三次。类目不匹配是审核拒绝最常见的原因之一,前置检查能省下大量时间。

5.3 审核注意事项与发布节奏

审核周期一般在几小时到几天不等。实测WorkBuddy提交审核后大概一天半通过,但后来一次版本更新赶上某节假日前,审核拖了快一周。所以如果你有重要的版本计划,尽量避开大型节假日前几天提交。

提交审核前,我整理了一份自检清单,你也可以直接用:

  • 页面是否有完整的用户体验路径,不能出现"正在开发中"之类占位页。
  • 是否有诱导分享、诱导关注的行为,比如分享后才能解锁功能的逻辑是绝对不能有的。
  • 是否涉及用户隐私信息收集,如果收集,必须有隐私政策说明。
  • 是否有侵权风险,包括名称、logo、文案。
  • 是否包含无法点击的按钮,审核人员如果发现按钮没反应会产生不好的体验印象。

WorkBuddy因为功能简单、不涉及支付、不涉及用户隐私,整个审核过程很顺利。但如果你涉及用户头像昵称,建议提前看微信最新的用户信息规范,不同时期的提取方式变动很大,按旧教程写的代码审核时可能直接被驳回落马。

6. 我从这个项目里踩过的坑

6.1 setData误用导致的页面不刷新

这是新手最容易踩的坑。我最初写任务管理逻辑时,直接在回调里做了this.data.tasks.push(newTask),以为数组变了页面就会跟着变。结果界面纹丝不动,但控制台打印数据却是有的,非常迷惑。

原因在于小程序的视图层和逻辑层是双线程架构,视图层展示的数据来自于一份被特殊处理过的副本。想更新视图,必须通过this.setData把数据发送到视图层,直接修改this.data不会触发渲染。

修正后的两种写法:

// 场景:完全替换数组 const newTasks = [...this.data.tasks, newTask]; this.setData({ tasks: newTasks }); // 场景:修改数组中某一项 this.setData({ 'tasks[0].status': 'done' });

第二种用字符串路径更新深层数据的写法,在处理列表项更新时非常高效,比"取出来修改再整体回填"省内存,也避免了整个列表的重渲染。

6.2 tabBar自定义样式的限制

WorkBuddy最开始想做一个中间凸起的"+"按钮,类似很多App底部导航的中心大按钮。研究之后发现,原生tabBar支持的文字和图标配置非常有限,不支持自定义按钮形态,只有通过"自定义tabBar"能力才能实现中间凸起效果,需要额外添加custom-tab-bar目录,配置"custom": true,然后手写组件的样式和逻辑。

对于MVP阶段来说,这个美观提升换来的开发成本太高了。我最后选择了最朴素的纯文字tabBar,放弃了中间大按钮。这个决策让我省了不少时间,也让功能更早交到用户手里。

现在回头看,工具类小程序的核心价值是完成任务,不是导航动画。颜值可以在第二版迭代时慢慢优化,先把核心功能上线才是正事。

6.3 刘海屏、底栏遮挡与原生组件的层级问题

首次真机预览时,我发现单独调整后发现一个常见问题:iPhone的底部小黑条(Home Indicator区域)会遮挡我页面底部的内容。解决方案是使用安全区域适配,在WXSS里追加:

.page { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); }

env(safe-area-inset-bottom)是系统提供的安全区高度变量,加上之后,页面底部的内容会在安全区域之上展示,不会被Home Indicator遮挡。这个适配对iPhone全面屏机型尤其重要,不做就很容易出现"按钮摁不到"的体验问题。

另一个坑是textarea等原生组件的层级问题。在WorkBuddy的编辑页里,如果我使用的是input组件,它本质上是一个原生组件,在某些Android机型上会覆盖在其他元素之上,导致弹出键盘时页面被顶起或者遮罩失效。如果是更复杂的富文本输入,可能要用cover-view来覆盖住它。WorkBuddy的场景比较简单,编辑页只有单行文本,所以没有出太大问题,但如果你做的是评论区、表单页,一定要留意原生组件的层级特性。

6.4 审核时因收集用户信息被驳回

这里讲一个微信对用户信息管控的案例。我在WorkBuddy第二版时尝试加入"显示用户昵称和头像"的功能,实现方式还是旧版的wx.getUserInfo弹窗。结果提交审核后被打回,理由是"小程序涉及收集用户信息但未说明用途,且当前功能与音乐类目无关"。当时产品定位还没有改,实际上是我自己的功能设计多此一举。

后来查阅了微信官方的最新公告,发现平台对用户昵称头像这类信息的获取和存储已经改成了新规,必须通过官方提供的按钮方式获得用户的主动授权,并且要明确说明使用目的。WorkBuddy其实根本不需要头像昵称,我最后直接砍掉了这个功能,审核一次通过。

这件事给我的教训是:开发过程中别急着引入用户信息能力,先问自己"这个功能真的需要吗"。很多看起来锦上添花的功能,会带来合规成本和审核风险。工具类小程序在最简单、最克制的状态下,反而是最好维护的。

7. 写在最后:从一个Demo到一个能自用的小工具

WorkBuddy的开发周期比预期短,很大原因是功能范围控制得好。我没有去做账号体系,没有做后端,也没有堆砌漂亮但无用的动效,而是用一个列表页、一个编辑页、一个统计页撑起了完整的使用闭环。这个闭环让我每天都会打开它记几笔任务,用着顺手,朋友也通过体验版反馈了一些建议,比如"C级优先级的默认值和日期选择交互"。

我个人的体会是,小程序开发不难,难的是克制和一以贯之的判断标准。遇到一个需求,先问"这是核心功能吗",不是就砍掉;再问"不做会怎样",不会怎样就真的不做。WorkBuddy这款小程序最宝贵的地方不在于技术有多炫,而在于它用最少的代码解决了真实问题。

如果后续要继续升级,我的路线图是:先接入微信云开发,把本地存储换成云数据库,实现多设备同步;然后加一个"分享任务清单到微信群"的入口,让团队协作真正跑起来;再进一步可以利用订阅消息,在任务截止日期前给用户推送提醒。每一步都有明确的价值验证点,不会盲目堆功能。

最后再分享一个小技巧:真机调试阶段,不用每次都等审核通过。开发者工具上传后的"开发版本",在小程序后台"成员管理"里把测试人员的微信号加为体验成员后,对方就可以通过"体验版"二维码直接使用。这样在正式提交审核前,就能让一批真实用户帮你试错,体验反馈的性价比特别高。

WorkBuddy的代码现在放在我自己的项目仓库里,每次打开它,我都会想起那个从注册账号开始的下午。从0到1的本质不是走完流程,而是建立一套属于自己的可重复的决策方式。希望这篇保姆级教程能帮你少踩几个坑,早日做出自己的第一个小程序。

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

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

立即咨询