资讯动态

小程序开发零基础实战:待办清单项目从搭建到上架全流程

发布时间:2026/10/9 6:32:56 来源:尧图企业网站定制
我喜欢琢磨一件事为什么很多人学小程序开发卡在“新建项目”这一步就不再往前走了。后来和几个朋友聊下来答案几乎一致——教程要么只讲理论把文档念一遍要么直接扔给你一个完整的商城项目连目录都看不明白。这两种我都经历过所以我决定换一种方式用一篇文章带你真正“写完”一个小程序。这篇文章做的是一个零基础也能跟完的实战项目待办清单小程序。它麻雀虽小五脏俱全所有小程序开发的核心知识点都会牵出来包括页面结构怎么写、样式怎么适配、数据怎么流动、本地缓存怎么存、登录手机号怎么获取、发布上架前怎么准备。我尽量按照自己当年踩坑摸索的顺序来讲你跟着敲一遍代码比看十遍文档管用。文章适合三类人完全没写过小程序的纯新手、想快速了解小程序开发全流程的产品或测试、以及正准备做毕业设计或外包项目需要一个简单底子的学生。已经熟练的老手可以挑后面几章看尤其是发布和真机调试相关的内容。1. 项目概述与整体思路拆解1.1 为什么我推荐用“待办清单”作为入门项目我见过不少新手一上来就想着做“全功能电商”“社区问答”结果写了两天就没下文。原因很简单需求太大任何一个环节卡住都会劝退你。待办清单不一样它数据模型简单界面结构清晰交互也很直白基本可以覆盖小程序开发里你会用到的高频能力页面布局和数据绑定这是所有页面的地基输入框、复选框、按钮这些常用组件的交互方式数组操作也就是增删改查业务系统归根结底做的是这些事本地缓存wx.setStorageSync 这套 API 用熟练了你就算没有后台也能让数据“活”起来条件渲染和列表渲染这两个在小程序里是使用频率非常高的逻辑展示能力如果你能独立把这个项目写完再去看任何一个小程序页面就不会再有“这是不是天书”的恐惧感。因为你会发现所有页面本质上都是“数据 模板 事件”三件事的组合。我用这个项目带过不少新人基本上半天内就能看到成品这种正反馈对新手很重要。1.2 原生开发还是uniapp选型背后的道理很多人在入门之前就听到过 uniapp 的名字甚至有人直接劝他“别学原生直接上 uniapp”。这个建议放在商业项目里有一定道理因为多端复用可以节省成本但对于一个刚起步的新手我坚持建议先用原生框架。原因一小程序开发者工具对原生项目的调试链路最完善。你写的代码有问题报错信息直接、清晰地指向某个文件某一行。而 uniapp 多了一层编译出了问题你得先判断是源码问题还是编译产物问题新手很容易绕晕。原因二原生的目录结构就是小程序的运行结构你能直观感受 app.json、page.json、wxml、wxss 这些文件各自承担什么职责。等你理解了原生再学 uniapp 其实一天就能切换思维但反过来直接学 uniapp 的人往往搞不清楚小程序底层的运行规则。原因三官方文档和社区方案绝大多数都是原生写法你遇到问题时搜索到的代码片段基本能直接为你所用。uniapp 虽然也很火但要等你有了基础再根据业务需要去选择这才合理。我在文章里全部用原生代码实现因为这是最直接可复现、也最适合入门的一条路。等这个项目做完你再去看 uniapp 打包相关的内容后面第 4 章我也会提到一些思路会顺畅很多。1.3 小程序的“四件套”WXML、WXSS、JS、JSON接触小程序首先会被一堆后缀名搞懵。其实拆开来看它们和网页开发的对应关系非常直接WXML相当于 HTML负责页面结构。它比 HTML 多了一些专属标签比如view、text、block还支持wx:for、wx:if这种模板语法。WXSS相当于 CSS负责样式。里面多了一个单位rpx它是响应式像素单位你不需要做媒体查询写个750rpx就等于全屏宽度换算规则后面我会细说。JS就是负责业务逻辑和网页里的 JavaScript 几乎一样只是运行环境有一些差异比如它是通过Page()函数注册页面而不是直接操作 DOM。JSON是配置文件不用你写逻辑只用来声明页面路径、窗口样式、导航栏标题这些“静态信息”。这四类文件每一个页面对应一套WXML/WXSS/JS/JSON全局也有对应的 app.json/app.js/app.wxss。理解了这个模型你就理解了小程序的“组件化页面”是怎么组织起来的。后面我们新建项目的时候你会亲眼看到这些文件是怎么被创建的。2. 环境准备与工程初始化2.1 注册账号与申请 AppID写代码之前先去 微信公众平台 注册一个小程序账号。注意这里说的是“小程序”不是“公众号”注册时主体类型选择“个人”信息按提示填写就行。个人主体能用的功能虽然少一些比如部分开放类目受限但用来学习和上线个人作品完全够用。注册完成后进入小程序后台的“开发管理”页面找到“开发设置”里面会有一个AppID。这个 AppID 相当于你小程序的身份证号后面开发者工具新建项目时要用到它。我在带新人时发现一个容易被忽略的问题很多人注册完账号没有点击“完善信息”导致 AppID 没生成。如果你在后台找不到 AppID先确认自己是否完成了小程序名称设置和头像上传这些基础信息不填很多开发能力是置灰的。还有一点要提醒如果你只是临时体验一下开发者工具也支持“测试号”不需要注册账号。但测试号有功能限制而且缓存的项目和个人体验差很远所以我建议你老老实实注册一个反正免费。2.2 安装开发者工具与创建项目从微信官方下载“微信开发者工具”安装过程没什么特别之处一路下一步即可。打开之后用你注册的微信号扫码登录然后选择“新建项目”。新建项目时有几个选项要注意目录选一个不带中文和空格的路径否则部分工具链可能会出幺蛾子AppID选择刚才复制下来的自己的 AppID如果没有也可以选“测试号”但还是建议用自己的后端服务这里可以先选“不使用云服务”我们文章里用纯本地存储和基础 API 实现模板直接选“JavaScript - 基础模板”不要选 Skyline 或插件模板创建完成后你会看到开发工具左边是模拟器右边是代码编辑区下面还有调试器面板。我建议你花 10 分钟把界面上每个按钮都点一遍尤其是“编译”按钮和“预览”按钮后面一天都离不开它们。有个细节很容易劝退新手——第一次打开工具会弹出一堆“权限提醒”和“更新提示”处理完就好不要被吓到。实际上你只需要在模拟器里看到默认页面渲染出来了环境就算准备好了。2.3 app.json 全局配置页面注册与导航栏在项目编辑器里找到app.json这是个核心配置文件。打开后你会看到默认代码大概长这样{ pages: [ pages/index/index, pages/logs/logs ], window: { navigationBarBackgroundColor: #ffffff, navigationBarTitleText: 我的待办, navigationBarTextStyle: black }, style: v2, sitemapLocation: sitemap.json }pages数组里是所有页面路径第一个路径就是启动页。这里有个硬性规则新增一个页面必须在 pages 里注册否则页面文件存在也没法跳转。这个坑我见过不少新手踩因为他们从没动过 app.json。window里配置的是全局窗口样式。navigationBarTitleText就是页面最顶部那条导航栏上显示的标题。navigationBarBackgroundColor和navigationBarTextStyle控制背景色和文字色只支持 black/white。这里顺便把很多人搜过的“微信小程序顶部导航栏高度”问题讲透。导航栏高度不是固定的不同机型不同系统版本下都不一样。如果你设计的页面需要自定义头部就涉及精确计算导航栏高度的问题。小程序提供了一个 APIwx.getWindowInfo()可以拿到系统信息和状态栏高度。你自己算的话一般是顶部导航栏高度 状态栏高度 导航栏本身高度通常大约 44pxiPhone X 及以上机型会更高约 47px 左右新手期最稳妥的办法是使用navigationStyle: custom把导航栏变成自定义然后你通过下面的代码动态计算高度const info wx.getWindowInfo(); this.setData({ statusBarHeight: info.statusBarHeight, navBarHeight: info.statusBarHeight 44 });不展开太多这里你只要理解导航栏高度是一个需要动态适配的值等后面做自定义头部的时候再细抠就行。2.4 项目目录结构与代码规范创建完项目后本质上的目录结构看起来是project.config.json // 项目配置文件开发者工具使用 app.js // 全局逻辑 app.json // 全局配置 app.wxss // 全局样式 sitemap.json // 搜索索引配置 pages/ index/ index.wxml index.wxss index.js index.jsonindex 就是启动页文件夹。一个页面的四件套放在同一个目录下这是惯例。你不用纠结文件名必须叫 index改成 todo 也没问题只要 app.json 里注册的路径和文件名一致就行。关于代码规范我从一开始就建议大家保持几个习惯以后项目大了能省很多事文件命名统一用小写字母多英文单词用驼峰或中线连接别混用WXML 标签属性尽量用双引号JS 里字符串用单引号保持格式统一页面数据用data字段统一声明更新时通过setData修改不要直接赋值公共样式放 app.wxss页面私有样式放各自 wxss不要所有东西都堆到全局这些不是强制规则但作为从业者我太能体会“高可维护性”有多重要。你入门时就建立规范意识后面做复杂项目的时候就不会面对一堆自己都看不懂的代码。3. 页面开发实战待办清单的核心功能3.1 WXML 页面骨架与数据绑定现在开始写真正的页面。目标是做一个能“添加待办事项、标记完成、筛选、删除”的清单界面。先打开pages/index/index.wxml把默认模板清空替换成下面的骨架view classpage view classheader text classtitle我的待办/text text classcount{{unfinishedCount}} 项未完成/text /view view classinput-area input classinput placeholder输入新的待办事项 value{{inputValue}} bindinputhandleInput confirm-typedone bindconfirmhandleAdd / button classadd-btn bindtaphandleAdd添加/button /view view classfilter-bar view classfilter-item {{filter all ? active : }} bindtapchangeFilter >.page { min-height: 100vh; background: #f5f5f5; padding: 30rpx; box-sizing: border-box; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30rpx; } .title { font-size: 44rpx; font-weight: 600; } .count { font-size: 26rpx; color: #888; } .input-area { display: flex; margin-bottom: 20rpx; } .input { flex: 1; height: 88rpx; background: #fff; border-radius: 12rpx; padding: 0 24rpx; font-size: 28rpx; } .add-btn { margin-left: 20rpx; width: 160rpx; line-height: 88rpx; text-align: center; background: #4caf50; color: #fff; border-radius: 12rpx; font-size: 28rpx; padding: 0; } .filter-bar { display: flex; margin-bottom: 20rpx; } .filter-item { margin-right: 20rpx; padding: 10rpx 28rpx; background: #fff; border-radius: 40rpx; font-size: 26rpx; color: #666; } .filter-item.active { background: #4caf50; color: #fff; } .todo-item { display: flex; align-items: center; background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx; } .todo-item.done .item-text { text-decoration: line-through; color: #aaa; } .item-checkbox { transform: scale(0.8); } .item-text { flex: 1; font-size: 30rpx; color: #333; } .item-delete { color: #e74c3c; font-size: 26rpx; padding: 10rpx; } .empty { text-align: center; color: #999; margin-top: 100rpx; font-size: 28rpx; }这里最关键的是理解rpx。小程序规定了屏幕宽度永远是750rpx你可以把它想象成把宽度平均分成了 750 份。比如 iPhone 6 的逻辑宽度是 375px那 75rpx 就等于 37.5px在更宽的安卓机上750rpx 仍然是整个宽度。所以你不需要写媒体查询一套样式所有机型基本都能适配。另外注意padding: 30rpx使用box-sizing: border-box是为了让 padding 值包含在元素总宽度里避免布局“撑破”。这种细节在新手写布局时非常容易踩一列元素明明宽度算了 50%一加 padding 就换行了。3.3 JS 逻辑数据的定义与处理样式是皮逻辑才是魂。到index.js把默认内容替换成核心逻辑Page({ data: { inputValue: , filter: all, todoList: [], }, onLoad() { const stored wx.getStorageSync(todoList); if (stored stored.length) { this.setData({ todoList: stored }); } }, handleInput(e) { this.setData({ inputValue: e.detail.value }); }, handleAdd() { const value this.data.inputValue.trim(); if (!value) return; const todoList this.data.todoList.slice(); todoList.unshift({ id: Date.now(), title: value, done: false, }); this.setData({ todoList, inputValue: }); wx.setStorageSync(todoList, todoList); }, toggleItem(e) { const id e.currentTarget.dataset.id; const todoList this.data.todoList.map(item { if (item.id id) { return { ...item, done: !item.done }; } return item; }); this.setData({ todoList }); wx.setStorageSync(todoList, todoList); }, deleteItem(e) { const id e.currentTarget.dataset.id; const todoList this.data.todoList.filter(item item.id ! id); this.setData({ todoList }); wx.setStorageSync(todoList, todoList); }, changeFilter(e) { const filter e.currentTarget.dataset.filter; this.setData({ filter }); }, get showList() { const { todoList, filter } this.data; if (filter active) return todoList.filter(item !item.done); if (filter done) return todoList.filter(item item.done); return todoList; }, get unfinishedCount() { return this.data.todoList.filter(item !item.done).length; }, });这里有一个重要的机制需要说清楚小程序的数据流是单向的一切页面变化都要通过setData来驱动。你不能像写原生 JS 一样直接改data.todoList因为直接改虽然内存里的值变了但页面 UI 不会同步刷新。setData会把新的数据从逻辑层传递到渲染层并触发界面更新。关于e.detail.value输入框每次输入会触发bindinput事件事件对象里detail.value就是输入框当前的值。这是小程序输入组件的固定事件格式需要记一下。再提一下showList和unfinishedCount。微信小程序旧版本不支持这种 getter 写法需要用函数在setData时手动计算但新版基础库已经支持能在 WXML 里直接引用。如果你发现编译报错说明基础库版本偏低升级一下开发者工具或者把基础库切到最新版即可。还有一个细节数组和对象在setData后会被整体替换所以我在每次修改前用.slice()复制了todoList这能避免一些新旧数据交替时的奇怪问题。这个习惯在写复杂业务时也很重要尽量避免直接对引用对象进行破坏性修改。3.4 事件机制与组件交互的细节这一节专门把事件处理的细节捋顺因为新手在这个环节最容易懵。小程序的bindtap事件可以绑定在任何组件上。点击时事件对象e里通过currentTarget.dataset可以取到>.safe-bottom { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); }为什么这个方法有效因为苹果手机的 Home 条区域会被系统识别为安全区env(safe-area-inset-bottom)返回的具体像素值是系统算好的你直接用就行。安卓上多数情况下它是 0也不影响布局。自定义导航栏的另一个坑是你不能再用页面本身的返回能力需要自己写wx.navigateBack而且页面回到 tab 页面时导航逻辑也要单独处理。如果你只是入门我建议先用默认导航栏把核心功能跑通自定义导航栏可以作为进阶练习。5.5 调试技巧学会看控制台和网络面板最后分享一个非常实用的习惯。很多人遇到 bug 的第一反应是“重编译一下”其实更应该先看调试器的 Console 和 Network 面板。Console 面板会直接红字显示 JS 异常点击就能跳到对应代码位置Network 面板会显示所有请求的状态和响应体接口报错也好、取不到数据也好都在这里定位AppData 面板可以看到当前页data的实时快照当你怀疑数据状态不对时直接看这里的值比瞎猜高效得多我见过一个新手调试了半小时“列表是空的”最后发现是 Network 面板里请求没发出去原因是baseUrl配置写错了。如果他能先看 Network这个 bug 一分钟就能解决。所以别急着刷新先看数据到底走到了哪一步。写在最后的一点经验每次有人问我“学完这个待办清单之后下一步做什么”我都会建议他把这个项目默默做完然后立刻做一个自己想用的工具。可以是记账、习惯打卡、倒计时无论多简单都行。因为只有当你开始“自由创作”而不是跟着教程敲代码时才会真正遇到那些教程没有讲的边角问题而这些边角才是你能力增长最快的地方。我在实际带项目时发现一个规律能把待办清单好好写完的人往往三天内就能听懂别人口中的“生命周期、组件通信、分包预加载”这些概念而一直停留在“看教程”的人三个月后可能还在看第一课。做出来比看明白重要这是我学编程至今最深的一条体会。希望你这篇文章跟着敲完也能收获同样的感觉。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑