1. 第一周学小程序,先别急着写代码:把生态位置看清楚
如果说前端开发里还有什么东西是“看起来简单、上手就懵”的,微信小程序绝对排得上号。很多新人第一天打开开发者工具,看到左侧模拟器、中间代码区、右侧调试器,再扫一眼默认生成的 index 页面和 logs 页面,第一反应往往是“这不就是写网页吗?”——然后一写就发现完全不是那么回事。页面路径要注册、样式单位换成了 rpx、数据更新必须走 setData、很多 HTML 标签不能用、图片路径处理方式也不一样,到处都是“潜规则”。
所以我给建议:第一周别急着写代码,先把小程序在微信生态里的位置搞清楚。它不是网页,也不是原生 App,而是一种运行在微信客户端里的“特定容器应用”。用户不需要下载安装,扫个码、搜一下、从聊天记录里点进来就能打开,用完关掉也不用卸载。这种形态解决的核心问题是“触达成本”:一个 App 动辄几十 MB,一个网页又没法调用系统能力,小程序正好卡在中间——体积小、加载快、能拿手机硬件能力,同时开发成本比双端原生低不少。
从技术实现上说,小程序走的是“双线程模型”:逻辑层运行在 JavaScript 引擎里,视图层由原生组件渲染,两边通过一套异步消息机制通信。这也是为什么小程序里不能像网页那样直接操作 DOM,所有界面更新都要靠数据驱动。第一周你不必把双线程的每个细节都吃透,但必须建立这个意识:它不是“套壳网页”,你想用 jQuery 那种“直接改 DOM”的思路去写,注定踩坑。
那第一周的目标应该定成什么?按我带过不少新人的经验,第一周能完成三件事就算合格:第一,注册账号、装好工具、跑通默认项目;第二,看懂一个页面从“文件”到“显示在屏幕上”的全过程;第三,亲手写一个能响应点击、能输入、能更新列表的小页面。至于组件库、云开发、分包加载、自定义组件,那是后面几周的事,第一周碰它们只会让你心态崩。
2. 环境搭建的细节:注册、AppID和开发者工具首跑
2.1 账号和 AppID 到底卡在哪里
整个环境搭建流程里,九成新人卡在同一个地方:搞不清楚 AppID 是什么、去哪拿、为什么必须要。简单说,AppID 就是你的小程序身份证,在微信公众平台注册账号之后,创建一个小程序应用,系统会分配一串唯一的 ID。它在后续所有环节都要用:开发者工具登录、真机预览、上传代码、配置服务器域名、开通云开发,全都认这个 ID。
这里有一个第一周新手最常用的“捷径”:注册账号的时候,如果只是本地练习,可以选“测试号”,不需要绑定小程序。但我的建议是,只要你确定要长期学,老老实实注册一个个人主体的小程序账号,拿到正式的 AppID。原因很简单:测试号很多能力是残缺的,比如没法开通云开发、某些 API 调用会受影响、后续你想把代码传到手机上看真实效果,流程也会绕。注册过程本身不复杂,按公众平台引导填信息就行,个人主体不需要营业执照,身份证就能搞定。
提示:注册时“小程序名称”可以先随便起一个,后面还能改名。但注意个人主体的小程序在类目上有限制,第一周不涉及发布,不用纠结这一点。
2.2 下载工具和新建项目的正确姿势
开发者工具直接去微信官方文档页面下稳定版就好,不要追预览版,除非你特别喜欢当小白鼠。装好之后用微信扫码登录,然后新建项目,这一步有几个选项值得说清楚:
- AppID 填正式的,不要选“测试号”;
- 后端服务选“不使用云服务”;
- 模板选 JavaScript 基础模板,而不是 TypeScript 模板或云开发模板。
如果你是第一次接触,我强烈建议第一周先用 JavaScript 基础模板。TypeScript 本身没错,但它在第一周会给你增加一层“类型系统”的认知负担,而你现在需要的是先搞懂“小程序本身的工作方式”。云开发模板同理,它默认给你塞了一堆云函数代码,新手根本分不清哪些是前端、哪些是云端,很容易糊里糊涂。
新建完项目,你会看到默认生成了一堆文件。先不要急着删,每一个都有意义。接下来要做的事情只有一件:点“编译”,看模拟器里出现一个带头像和“Hello World”字样的页面。这一步跑通了,说明你的工具链没问题,整个第一周的技术路线就有了地基。
2.3 工具栏里最该先认识的三个区域
项目跑起来之后,先把开发者工具的界面布局认一遍,这比多写十行代码更值。
模拟器在左边,它模拟的是手机屏幕上的渲染效果,第一周你的代码好不好看、布局对不对,全看这里。中间是代码编辑区,本质上是带语法高亮的文本编辑器。右边的调试器长得非常像 Chrome DevTools,里面有 Console、Sources、Network、Wxml 等面板,其中 Wxml 面板是小程序特有的,后面调试页面结构时你会经常用到它。
顶部工具栏还有两个高频按钮:“预览”和“真机调试”。预览会生成一个二维码,手机扫码后能在微信里打开你的小程序;真机调试则会在手机上打开带调试信息的小程序,可以在手机上看到 console 日志。它们的作用不一样:预览看效果,真机调试查问题。第一周只需要会用预览就够了,真机调试等到第二周再说。
3. 默认项目的结构拆解:全局配置、页面四件套和注册逻辑
3.1 一堆文件里,真正的主心骨是 app.json
新手打开项目目录,看到 app.js、app.json、app.wxss、pages 文件夹、utils 文件夹、project.config.json,很容易犯晕。这里我给你一个记忆锚点:小程序里,一切全局配置都写在 app.json,一切应用生命周期逻辑写在 app.js,一切全局样式写在 app.wxss。而项目里真正铺开的一片片页面,全部放在 pages 目录下。
重点讲 app.json,因为第一周你会在它身上踩最多的坑。它长这样:
{ "pages": [ "pages/index/index", "pages/logs/logs" ], "window": { "navigationBarBackgroundColor": "#ffffff", "navigationBarTitleText": "我的小程序", "navigationBarTextStyle": "black" }, "style": "v2", "sitemapLocation": "sitemap.json" }这里最核心的是pages字段。它是一个数组,里面每一项都是一个页面的路径,数组第一项就是小程序启动后展示的首页。很多人第一周会犯一个错:新建了一个页面文件,但没有写进pages数组,结果编译报错“page not found”,或者明明写了文件但模拟器里死活不显示。记住,在小程序里,页面文件要生效,必须先在这里注册。
window字段管的是全局窗口样式,比如顶部导航栏的背景色、标题文字、文字颜色。它属于全局默认值,但注意,每个页面自己的.json文件可以覆盖它,这个后面再说。
3.2 页面四件套:WXML、WXSS、JS、JSON 的分工
一个页面由四个同名的文件组成,比如 index 页面就是 index.wxml、index.wxss、index.js、index.json。这个设计是整个小程序开发最重要的心智模型,没有之一。
WXML 负责结构,类似 HTML,但标签是受限的,比如用view代替div,用text代替span,还有image、input、button等一套组件。写 MDN 上的那些 HTML 标签在这里基本不能直接用,会报“unknown component”之类的错误。
WXSS 负责样式,语法和 CSS 高度一致,但多了一套尺寸单位 rpx,也多了一些小程序特有的选择器限制。WXSS 里你也可以用@import引入其他样式文件,但第一周先不玩这些,专心把基础语法弄熟。
JS 负责逻辑,包括页面数据、生命周期函数、事件处理等。它是整个页面的“大脑”,没有 JS,页面就只是一堆静态的骨架和样式,点什么都不会有反应。
JSON 负责页面级配置,比如这个页面自己的标题、导航栏样式、是否允许下拉刷新等。它是可选的,如果你的页面没有特殊需求,一个空对象{}也能正常跑。
这四份文件靠“同名同路径”绑定在一起。你删掉其中任何一个,另外三个就只能“单腿走路”,页面很可能直接报错或空白。我在实践中发现,很多第一周的问题并非逻辑写错,而是文件结构不完整导致的。
3.3 页面是怎么被“找到”的,以及为什么 index 和 logs 能无缝切换
从打开小程序到看到页面内容,整个链路其实是这样的:app.json 里的pages数组指定了页面路径,小程序启动后先加载数组第一项对应的 JS 文件,执行里面的生命周期函数,拿到data,再把 WXML 模板和数据“合成”渲染视图。
默认项目里的 index 页面有一个“点击跳转 logs”的入口,用的其实是wx.navigateTo这个 API。你可以大概看一下它的调用方式:
wx.navigateTo({ url: '/pages/logs/logs' })这个url里写的路径,必须和 app.jsonpages数组里的路径完全一致,少个斜杠、多个.wxml后缀都会白屏。这种“注册—跳转—展示”的闭环,第一周一定要亲手走通几次,后面写任何页面都离不开这个套路。
4. 第一周实战:做一个“今日清单”页面
4.1 先写静态结构,感受 WXML 和 HTML 的差别
理论看太多没用,是时候动手了。第一周我推荐做一个“今日清单”:一个输入框、一个添加按钮、一个列表、一个清空按钮。功能虽然简单,但数据绑定、列表渲染、事件绑定、输入处理这几个核心知识点全都能练到。
先在 app.json 的pages数组里加一项"pages/todo/todo",建好对应目录和四个文件。然后打开 todo.wxml,写初始结构:
<view class="page"> <view class="header"> <text class="title">今日清单</text> </view> <view class="input-area"> <input class="input" placeholder="输入要做的事" /> <button class="add-btn" size="mini">添加</button> </view> <view class="list"> <view class="list-item" wx:for="{{list}}" wx:key="id"> <text>{{item.name}}</text> </view> </view> <button class="clear-btn" size="mini">清空</button> </view>注意两个地方:第一,HTML 里用来包文字的div和span,在这里换成了view和text。text里如果要用空格或换行,也没有 HTML 那么随意,它默认会压缩连续空格,需要时得用decode属性或写实体字符。第二,wx:for="{{list}}"就是循环渲染,列表里的每一项通过item变量访问,这比在 JS 里拼字符串优雅太多了。
4.2 数据绑定和列表渲染:不要把页面写死
静态结构写完,接下来是核心中的核心:数据驱动视图。打开 todo.js,先给页面定义初始数据:
Page({ data: { list: [], inputValue: '' } })页面的数据都放在data对象里,而 WXML 里凡是{{ }}包裹的东西,都会去data里找对应的值。这种“写数据,页面自动变”的思路对做过 Vue 或 React 的人来说很亲切,但小程序有个关键区别:你不能直接给 data 里的属性赋值来更新视图,必须用this.setData()。
比如下面这种写法是无效的:
this.data.list.push({ id: 1, name: '写代码' }) // 数据变了,视图不动正确写法是这样的:
this.setData({ list: [...this.data.list, { id: Date.now(), name: '写代码' }] })setData会把你传入的新数据同步到逻辑层,然后通知视图层重新渲染。第一周可以先把“视图更新必须走 setData”这句话背下来,后面踩过的很多坑都是从这里来的。
4.3 输入框和按钮的事件绑定:从“看”到“动”
静态列表没意思,现在让它动起来。先给输入框绑定一个输入事件,再给按钮绑定点击事件:
<input class="input" placeholder="输入要做的事" value="{{inputValue}}" bindinput="onInput" /> <button class="add-btn" size="mini" bindtap="addItem">添加</button>对应的 JS 逻辑:
Page({ data: { list: [], inputValue: '' }, onInput(e) { this.setData({ inputValue: e.detail.value }) }, addItem() { const name = this.data.inputValue.trim() if (!name) { return } this.setData({ list: [...this.data.list, { id: Date.now(), name }], inputValue: '' }) } })这里有几个细节值得注意。第一,bindinput和bindtap是小程序自带的事件绑定写法,不需要 addEventListener。第二,事件处理函数里能拿到一个事件对象e,而输入框的当前值是e.detail.value,这个detail属性是理解组件事件的关键。第三,Date.now()在这里作为列表项的 id,虽然第一周不用考虑太复杂,但wx:key必须给,否则列表更新时可能出现渲染错乱,也会在控制台看到警告。
清空按钮就更好写了:
clearList() { this.setData({ list: [] }) }写到这里,一个能输入、能添加、能清空的页面就跑通了。虽然丑,但整套数据驱动逻辑你已经完整走了一遍。
4.4 样式和 rpx:为什么你的字号和手机上对不上
很多新手写完 WXML,马上开始写 WXSS,然后遇到第一个玄学问题:在 iPhone 模拟器上看着正好的字号,换个安卓手机就变大了,或者设计稿里是 750 宽,自己照着写了width: 375px,结果右边多出一大块空白。
原因是 rpx 这个单位。小程序规定,无论什么机型,屏幕宽度都按 750rpx 来算。也就是750rpx = 屏幕宽度。在 iPhone 6/7/8 这类逻辑宽度为 375px 的机型上,1rpx 等于 0.5px;在大屏安卓上,1rpx 对应更多 px。所以你写width: 375rpx时,它永远是屏幕宽度的一半,而不是死死钉在 375 像素。
第一周我把经验值给你:设计稿按 750 宽来做,写样式时直接用标注的 px 数值,把单位换成 rpx 就行。例如设计稿里字体是 32px,你就写font-size: 32rpx。这样开发效率最高,也不用做复杂的换算。
注意:rpx 只适合用来定尺寸和间距。涉及边框、阴影这类需要精确保 1px 的场景,建议用
px或1rpx,不然在某些低分辨率机型上边框会忽粗忽细。
5. Debugger 和 Console:第一周就学会看报错
5.1 常见报错第一梯队:路径没注册、组件不存在、AppID 不合法
写代码不报错是不可能的。第一周遇到报错不可怕,怕的是“对着屏幕发呆,不知道去哪看原因”。我先把第一周最高频的三类报错列出来,你可以直接对照着查:
第一类:page not found或Please check your pages config。这种基本就是你新建了页面文件,但忘了在 app.json 的pages数组里注册。解决方案就一句话:把页面路径加进去,保存,重新编译。
第二类:Unknown component。这是你在 WXML 里写了小程序没有的组件名,比如写了<div>、<span>、<a>,或者拼错了组件名字。正确做法是全部换成小程序组件:view、text、image、button、input等。
第三类:AppID 不合法或invalid appid。这是项目配置里的 AppID 和登录账号不匹配。如果你用的是测试号,却在代码里调用了需要正式 AppID 的功能,也会报这类错误。解决方法是打开 project.config.json 检查 appid 字段,或者干脆“重新登录”开发者工具。
不管报什么错,第一件事都是看 Console。小程序开发者工具的 Console 和浏览器的几乎一样,红色是错误、黄色是警告,点开能看到详细堆栈。养成“一报错就看 Console”的习惯,这个动作能帮你节省第一周至少一半的卡壳时间。
5.2 Console 不只是看错:用它验证你的每一个假设
我见过不少新人,Console 打开是用来“等报错”的,不报错就不看。其实 Console 更大的价值是验证逻辑。第一周写“今日清单”的时候,我建议你在关键位置打印日志:
onInput(e) { console.log('输入内容为:', e.detail.value) this.setData({ inputValue: e.detail.value }) }这样你就能清楚看到,每一次输入事件到底触发了没有、拿到的值是什么。很多“我写了代码但没反应”的问题,本质都是“事件根本没绑定上”或“执行顺序和想象的不一样”,一打印日志立刻现形。等你把事件、数据、渲染这个链路摸熟了,自然会越来越依赖断言式的验证,而不是靠肉眼看页面猜。
5.3 用 Wxml 面板审查渲染结果
Console 负责逻辑,Wxml 面板负责看视图结构。点击调试器里的 Wxml 标签,你会看到当前页面渲染出来的完整节点树。这个面板能帮你确认两件事:一是数据是否真的渲染上去了,二是节点结构是否符合预期。
举个例子,假设你wx:for写错,页面一片空白,但 Console 没有报错。这时候切到 Wxml 面板,如果里面什么都看不到,说明数据结构有问题;如果看得到节点但文字空白,说明是取值路径错了。这种“把问题二分”的排查思路,第一周就要开始练。
6. 第一周避坑清单:五个让你卡到半夜的问题
6.1 setData 过度传参,页面卡得不能动
新手刚学会 setData 之后,很容易走另一个极端:数据往 data 里堆,setData 一次传一大堆。最典型的例子是把一个很大的表单对象甚至从接口拿来的整个列表原封不动 setData。小程序里 setData 每次调用都会做“逻辑层到视图层”的数据传输,数据量越大、频率越高,页面越卡。
我给你的建议是三条:能局部更新就不要整体更新;和渲染无关的数据不要放 data;频繁变化的数据(比如拖拽坐标、动画参数)尽量合并处理。第一周做今日清单完全碰不到性能瓶颈,但提前养成这个习惯,后面做复杂页面时你会感谢自己。
6.2 wx:if 和 hidden 用混了
wx:if和hidden都能控制元素显示隐藏,但原理完全不同。wx:if是“条件不满足直接不渲染”,hidden是“渲染了但藏起来”。如果需要频繁切换显示状态,用hidden更合适,因为不用反复创建销毁节点;如果条件很少改变,用wx:if更好,省去初始渲染开销。
第一周你只需要记住结论:高频切换用 hidden,低频切换用 wx:if。等第二周学性能优化时,再回头探究这两者在渲染层上的差异都来得及。
6.3 图片路径:本地图还是网络图,要分清楚
小程序的 image 组件支持本地图片和网络图片,但规则不一样。本地图片要放在项目目录下,写相对路径或绝对路径都能用;但要注意,放在 CSS 里的本地图片路径,某些情况下会失效,比如背景图写在 WXSS 里且路径用了相对定位,真机上报错是常有的事。
网络图片则要求域名必须在小程序后台的“合法域名”里配置过,否则线上环境加载不出来。第一周你如果只是想用图片,优先把图片放到images目录,在 WXML 里用/images/xxx.png这种绝对路径引用,简单省事。
6.4 页面 JSON 覆盖全局 window 时,你觉得“改了没生效”
还有一种常见状况:在 app.json 里把导航栏标题改成了“我的小程序”,跑起来发现某个页面的标题还是旧名字。问题出在页面自己的 json。如果你在页面的 json 里也配置了navigationBarTitleText,它会把全局配置顶掉。小程序配置遵循“就近原则”:页面级配置优先级高于全局配置。
所以“为什么改了没生效”这个问题,第一周出现时,先去翻对应页面的 json 文件,而不是怀疑编译器出 bug。等你习惯了这套覆盖规则,这类问题基本就算绕过一大半了。
6.5 生命周期光是听说过,不知道从哪看起
index.js 里默认写着onLoad,logs.js 里写着onLoad和onShow,很多新手不太明白这些函数什么时候被调用。第一周你不用全都背,只要理解一个最简单的顺序:页面打开时,先 onLoad,再 onShow,再 onReady;页面被切入后台再回来,会触发 onShow。像“今日清单”这个例子,数据初始化写在 onLoad 里就行,因为页面每次进入只创建一次。
如果你想知道“这个生命周期到底触发没”,和前面一样,用 console.log 打印即可。实践中我发现,把生命周期函数的调用时机用日志打出来,比看十遍文档都管用。
7. 第一周结束后,下一步怎么走
到了这里,你已经具备了小程序开发的最基本骨架:知道怎么建页面、怎么注册、怎么用 WXML 和 WXSS 拼界面、怎么用数据和事件让页面动起来、遇到报错知道去哪看。这周最重要的不是做出一个多完整的功能,而是建立“数据驱动界面”的思维方式。
接下来第二周,我建议按这个顺序往下学:先搞懂页面生命周期和组件传值,也就是父组件怎么把数据传给子组件、子组件怎么把事件抛给父组件,这是所有复杂页面交互的基础;然后接触wx.request,让页面能真的和服务器通信,你会发现 list 不再只是写死在 data 里的假数据,而是来自接口的真实内容;再往后可以学习简单自定义组件,把重复出现的页面片段抽出来复用,这时你的模块化意识才算真正建立。
第一周结束前,给自己做个能力自检:能不看文档说清app.json里pages数组的作用吗?能解释为什么更新页面必须用setData吗?能区分wx:for、wx:if和事件绑定的写法和用途吗?如果都能,下一周尽管放心往下冲。
我自己的体会是,小程序入门最难的一关其实不是语法,而是“接受它的独特规则”。HTML 里那句话怎么说的?用错误的方式也能写出看起来对的东西。小程序不行,它的规则比网页开发严格得多。所以尽早把“为什么这套机制长这样”想通,后面的路会顺很多。