零基础自学小程序开发第10天:独立完成待办清单与自学方法

发布时间:2026/10/3 4:42:22
零基础自学小程序开发第10天:独立完成待办清单与自学方法
不知不觉就到了自学小程序开发的第10天。作为一个真正意义上的前端0基础选手这十天里我经历了“看文档像看天书→照抄官方示例→改一行报三个错→终于能独立做出一个小功能”的完整过程。如果你也是零基础入门前端、打算靠小程序开发开启编程这条路的人这篇 Day10 的记录应该能给你一个很实在的参照第10天不是“学会了很多API”的日子而是我真正开始“用代码解决问题”的日子。这篇文章没有复杂的理论只有我当天做的事、写过的代码、踩过的坑以及事后总结出来的一套自学方法。1. Day10 前传零基础到能写页面的这九步先交代一下前九天我都干了什么不然 Day10 的“能独立做功能”会显得很突兀毕竟零基础的人很容易卡在起步阶段就放弃了。我大概把前九天分成三段Day1~Day3注册开发者账号、下载微信开发者工具、跟着官方文档跑通第一个“Hello World”页面。这段时间最大的困惑不是代码而是“为什么项目里有那么多文件到底哪个文件管哪个”。后来才明白小程序一个页面通常由 .wxml、.wxss、.js、.json 四个文件组成wxml 管结构、wxss 管样式、js 管逻辑、json 管页面配置。这个认知花了我将近三天才真正内化。Day4~Day6补前端三大件的基本功。虽然小程序用的是 WXML 和 WXSS但核心思路还是 HTML、CSS、JavaScript 那一套。我在 B 站找了一套比较老的前端入门视频只看了 HTML 标签、CSS 选择器和 JS 基础语法不求体系完整目标是能看懂小程序代码里每一行大致在干嘛。Day7~Day9跟着官方示例“TODO List”和“计算器”项目边抄边改。我给自己定了一条规矩不整段复制而是看着示例代码自己敲一遍敲完再故意改几个值和条件看页面变化从而理解每段代码的作用。这三天我最大的收获是学会了看控制台报错不再一报错就慌。到第九天晚上我把一个长得像“待办清单”的静态页面写了出来但所有数据都是写死在代码里的按钮点了也没反应。于是我决定把 Day10 的目标定为不依赖任何教程自己从头设计并独立完成一个“能真正用起来”的小程序页面。哪怕功能再简单也必须是“我主动设计出来的”而不是“照着谁抄出来的”。对零基础的人来说这个转折比多背十个 API 重要得多。2. 今天做点什么待办清单的功能拆解与选型理由如果你现在去问任何一位前端老师“零基础第一个项目做什么”大概率会得到两个答案一个是待办清单一个是计算器。我选了前者因为它的交互链路特别适合练前端基本功输入内容、点击添加、列表渲染、状态切换、删除数据。一条链路下来WXML 布局、WXSS 样式、JS 数据操作、本地存储全都能碰到但又不至于像购物车、登录注册那样牵扯太多后端逻辑。在动手写代码之前我先在纸上把功能拆成四条拆完再动工用户在输入框内输入一段文字点击“添加”按钮后这条文字出现在下方列表中每条待办事项前面有一个可点击的区域点击后事项变为“已完成”状态样式上加删除线每条待办事项右侧有一个“删除”按钮点击后该项从列表移除所有数据在页面关闭后不能丢下次打开小程序还能看到之前添加的内容。我当时还认真考虑过要不要加上“编辑功能”或“按优先级排序”最后都放弃了。原因有两个第一功能越复杂调试时定位 bug 的成本越高零基础很容易被叠加的 bug 劝退第二第10天我更应该关注“把一条主链路跑顺”而不是“把功能堆得多完整”。后来看招聘网站上那些前端面试题很多基础问题其实都是围绕增删改查展开的把一个简单功能吃透比泛泛接触十个半成品有意义得多。我也顺手查了 uni-app 这类跨端框架的帖子发现它们能一套代码同时发布到微信、支付宝等多个小程序平台。但想了想现在还不是碰 uni-app 的时机——我连微信原生小程序的运行机制都没完全吃透贸然上框架只会增加一层抽象出了问题更不知道从哪查起。我给自己定了个顺序先原生、后框架、再跨端。这个决定让我在 Day10 这一天少了很多障碍。3. 画页面是真功夫WXML、WXSS 和盒子模型的第一次正面交锋功能定稿后我花了大概四十分钟写页面结构。这是第一次完全没看教程凭着自己对“页面由哪些部分组成”的理解来搭骨架。页面的视觉区域从上到下依次是标题栏、输入区输入框添加按钮、待办列表区每条事项包含勾选区和文字区和删除按钮。对应 WXML 结构如下view classcontainer view classheader text classtitle今日待办/text /view view classinput-row input classtodo-input placeholder输入新的待办事项 value{{inputValue}} bindinputonInput / button classadd-btn bindtapaddTodo添加/button /view view classtodo-list block wx:for{{todos}} wx:keyid view classtodo-item {{item.done ? done : }} view classtodo-check bindtaptoggleTodo>.container { padding: 40rpx 30rpx; box-sizing: border-box; } .header .title { font-size: 44rpx; font-weight: 700; } .input-row { display: flex; align-items: center; margin-top: 30rpx; } .todo-input { flex: 1; height: 80rpx; border: 2rpx solid #ddd; border-radius: 12rpx; padding: 0 20rpx; font-size: 28rpx; box-sizing: border-box; } .add-btn { margin-left: 20rpx; background: #07c160; color: #fff; font-size: 28rpx; line-height: 80rpx; height: 80rpx; padding: 0 30rpx; border-radius: 12rpx; }关键代码在第 1 行就埋好了box-sizing: border-box。它的意思是让浏览器把 padding 和 border 都算进 width 内部而不是额外往外撑。我前九天一直没弄明白为什么有些页面在屏幕宽一点的手机上看会错位直到今天给多个组件同时加边框时才真正理解“宽度、内边距、边框三者会互相打架”。这也解释了为什么很多小程序页面里给输入框加边框后看起来总比旁边的按钮宽一点。解决办法就一句话写样式时先给容器统一加上 border-box再谈其他属性。4. 数据驱动页面bindinput、bindtap 与 setData 的异步之谜页面画好之后接下来的任务就是让按钮和输入框真正起作用。这里我踩了一个特别典型的零基础误区差点把项目方向带偏。我最初的想法是“点击添加按钮以后手动创建一个新的 view 塞到列表里去”就像在浏览器里用 document.createElement 一样。但在小程序里根本没有这种操作方式所有动态内容都必须通过数据驱动改 JS 里的数据页面再根据数据重新渲染。这个观念转变是 Day10 最核心的收获没有之一。对应的 js 逻辑我写成了这样Page({ data: { inputValue: , todos: [] }, onShow() { const todos wx.getStorageSync(todos) if (todos) { this.setData({ todos }) } }, onInput(e) { this.setData({ inputValue: e.detail.value }) }, addTodo() { const text this.data.inputValue.trim() if (!text) { return } const todos this.data.todos todos.push({ id: Date.now(), text, done: false }) this.setData({ todos, inputValue: }) wx.setStorageSync(todos, todos) }, toggleTodo(e) { const id e.currentTarget.dataset.id const todos this.data.todos.map(item { if (item.id id) { return { ...item, done: !item.done } } return item }) this.setData({ todos }) wx.setStorageSync(todos, todos) }, deleteTodo(e) { const id e.currentTarget.dataset.id const todos this.data.todos.filter(item item.id ! id) this.setData({ todos }) wx.setStorageSync(todos, todos) } })先看事件绑定。bindinputonInput的意思是输入框内容变化时小程序会把一个事件对象 e 传给 onInput而输入框的最新内容放在e.detail.value里。我把这个值存到data.inputValue再用value{{inputValue}}把输入框的值和页面数据绑定起来。这一通下来输入框里的内容就不再是“用户随便输入的字”而是“页面数据的一个映射”。零基础阶段最容易忽略的就是这层映射关系总觉得 input 里显示什么和 JS 无关其实在小程序里恰恰是 JS 决定了 input 里显示什么。再说 setData。我在写完添加逻辑第一次测试时发现了一个诡异现象点击“添加”按钮后页面列表立刻出现了新数据但我紧接着在 addTodo 里加上console.log(this.data.todos.length)打印结果却是旧长度。这是因为 setData 是异步的它通知视图层更新的速度要快过 JavaScript 执行栈里下一行代码的运行速度如果你在 setData 之后立刻读 this.data很可能读到的是旧值。解决办法是不要依赖“setData 后马上从 this.data 里取新值”这种写法而是把需要使用的数据先计算好再一次性 setData如果一定要在更新后做逻辑可以给 setData 传第二个回调函数。还有一个小坑藏得很深在toggleTodo里我用...item展开对象生成新对象而没有直接改item.done。这样做是为了避免直接修改this.data.todos数组里的深层对象因为小程序和 Vue 类似都希望数据变更通过新对象的形式触发检测。我后来查资料时看到有开发者分享说“为什么我改了数组里某个字段页面却不刷新”十有八九是因为只改了原对象属性没有生成新数组。用 map 返回新数组或者用 filter 返回新数组是最安全的写法。// 错误的直接修改 // const list this.data.todos // list[0].done true // this.setData({ todos: list }) // 推荐的不可变写法 const todos this.data.todos.map((item, index) { if (index 0) { return { ...item, done: true } } return item }) this.setData({ todos })另外列表渲染里的wx:keyid值得单独说一句。它相当于给每个列表项一个稳定的身份标识让小程序在增删数据时可以精准对比而不是整个列表重新渲染。如果完全不给 key控制台会告警而且列表一长、数据一变性能会肉眼可见地下降。5. 刷新就丢数据的教训本地缓存与页面传参的两个坎功能本身做完之后我遇到了一个在初学者看来特别打击自信的问题每次重新编译小程序辛辛苦苦添加的待办事项全都没了。页面数据明明在 JS 里为什么一刷新就消失因为data只是存在于内存中的页面数据小程序关闭后内存就被释放了。想要数据“活”得久一点就必须找地方把它存起来。对 Day10 这个阶段来说最合适的就是本地缓存wx.setStorageSync和wx.getStorageSync。我在 Day10 之前就听过这两个 API 的名字但一直没理解它们的使用场景。这次终于想通了本地缓存相当于在手机本地开了一个小保险柜页面每次写入时打开柜子放进去下次打开小程序时再从柜子里拿出来初始化页面数据。由于小程序对前端初学者而言暂时不涉及后端本地缓存几乎是唯一的选择。一个需要注意的点是wx.setStorageSync会把数据序列化后存起来所以它支持直接存对象和数组取出来也是原样的类型这一点比浏览器里的 localStorage 还要直观不用担心[object Object]的问题。我在这个环节还做了个后来证明非常有用的扩展给每条待办事项加了一个“详情页”的跳转接口。说“接口”可能有点大实际就是想让用户在点击某条待办文字后可以进入一个单独的页面看到这条待办的完整描述。这就牵扯到小程序页面传参了。几个热词里反复出现的“前端传参”第一天见觉得神秘这次终于动手用了。最简单的传参方式就是拼接 URL跳转时把 id 放到地址里wx.navigateTo({ url: /pages/detail/detail?id item.id })然后在详情页的 onLoad(options) 里通过options.id把参数取出来再按 id 去本地缓存里翻出这条数据来展示。我特意没有把整条待办内容拼在 URL 里因为 URL 传参有长度限制而且一旦内容包含中文或特殊字符还需要额外 encodeURIComponent 处理。最安全的方式是只传一个唯一标识符比如 id接收方再通过标识符去数据源里查。这个经验后来在看别人分享的前端开发 skills 时也得到了验证——传参传 ID 而不传整个对象是很多规范团队的通用习惯。不过缓存不是万能的它有一个特别容易被忽略的坑读取时机。如果我在 onLoad 里读取缓存而缓存数据在 onShow 时才写入那首次打开页面就会读到空值如果用户从详情页返回列表页某些小程序页面并不会重新执行 onLoad所以如果数据在后台被改动你可能看到的还是旧数据。为此我后面把读取缓存的逻辑从 onLoad 挪到了 onShow因为 onShow 每次页面显示都会触发。对列表这种需要实时反映最新数据的页面onShow 读取缓存是最稳妥的选择。还要提醒一句本地缓存不能存放密码、验证码、身份证号这类敏感数据。前端缓存本质上是明文存储任何能打开你设备的人都可能读到。这是安全底线问题Day10 的待办清单里虽然都是“买菜”“写文章”这类无关紧要的内容但早一点养成“敏感数据不入缓存”的意识后面做登录注册时就不会踩出大坑。6. 复盘十天我对“零基础自学前端”的真实建议最后这段不是课程总结而是我作为一个刚刚迈过“零基础”门槛的人手把手复盘 Day10 全过程后想清楚的三件小事。它们不是从培训班老师那里听来的大道理而是这十天我踩了无数坑后用时间换来的实际操作原则。第一句话是零基础学小程序不要试图把所有 API 背下来而要把一条功能链路走穿。我今天是做完待办清单之后才真正理解了什么叫做“数据从输入框进来→经过 JS 处理→通过 setData 驱动页面更新→通过缓存持久化→下次启动再读取”。这条链路看起来简单但它映射了一个前端页面最核心的生命周期。以后再遇到登录、列表筛选、搜索、分页底层走的全是这一套。见过很多自学的人每天翻文档看到 fetch 就顺手记一下看到 storage 又背一段笔记记得密密麻麻但让他独立写一个“点击按钮把输入内容显示到页面”的 demo 都费劲。问题就出在没把链路走通只停留在“认识 API 名字”的层面。第二句话是报错不可怕可怕的是不读报错信息。我前五天一看到控制台里的红色报错就心里发紧总觉得自己是不是不适合编程。后来被逼得没办法开始尝试把报错信息复制到浏览器搜索框里搜才发现大部分问题五分钟内就能找到答案。Day10 这一天我遇到最蠢的一个 bug 是“addTodo 点击后没有任何反应”我盯着代码看了十分钟都没看出问题最后打开控制台才看到“Cannot read property trim of undefined”。原因是 input 组件的 v-model 我只在 WXML 里写了bindinput但认真看发现我把事件名写成了bindinputonInput却忘了在 data 里初始化inputValue: 字段。就是这么基础的问题看代码很难看出来但控制台一秒钟就告诉你了。所以我的实操建议是每写一个交互功能就必须打开调试器里的 Console 和 WXML 面板console.log 要勇敢打出来尤其要把事件对象 e 完整打印一次看看里面到底有什么字段。第三句话是用输出倒逼输入比每天打卡式学两个小时有效得多。我前九天有一个明显感受同样是看一个小程序开发教程如果我只是点着鼠标跟着视频做过三天就全忘但如果我先给自己定一个“今天必须做出某功能”的目标再倒回去查文档那个 API 我能记很久。Day10 能够独立做完整个待办清单就是因为我前一天晚上给自己下了死命令“明天必须做一个不用看教程的完整功能”。在这个目标驱动下我不仅接触了 setData、bindinput、wx.setStorageSync 这些 API还顺带搞懂了盒子模型和异步更新的原理。后面我打算把学到的内容整理成自己的知识笔记再往 uni-app 和更完整的小程序项目方向深入毕竟小程序最终要做出能给别人用的产品前端这条路还很长。希望这篇 Day10 的记录能帮到同样在自学前端和小程序开发的你。如果哪一步卡住了先在控制台敲个 log再想把事件和数据的关系理一遍——很多时候问题就已经解决了一半。