
1. 先想清楚小程序到底是个什么形态的东西做前端这几年被问得最多的一个问题就是——想学微信小程序开发从哪下手。问的人里有刚毕业的学生也有写了五六年 Web 想扩一条技术线的老手。我发现大家的困惑点其实不在难不难而在它跟我会的东西差在哪。所以这篇不打算从你好世界开始念文档而是按我自己带人、自己也踩坑的顺序把微信小程序开发这条线从头到尾捋一遍包括环境搭建、核心配置、登录链路、扩展组件以及那些文档里不会写、但真上手一定会撞上的问题。先把定位说清楚。微信小程序不是网页也不是原生 App它是一套运行在微信客户端里的应用形态。官方给的架构是双线程渲染层跑在 WebView 里负责画界面逻辑层跑在独立的 JS 引擎里负责跑业务代码两者之间靠客户端原生层做消息中转。这个架构直接决定了几件事——你写小程序的时候没有window、没有document、拿不到真实的 DOM 节点jQuery 那一套完全用不了document.getElementById这类写法在小程序里是直接报错的。这个隔离设计不是为了折腾人。它的好处是把逻辑层的 JS 执行和渲染层的界面绘制解耦逻辑层卡住不会直接冻住界面同时平台方可以严格控制渲染层的权限比如禁止随意跳转外链、禁止动态执行远程脚本。代价就是你得重新学一套通信方式数据从逻辑层流向渲染层靠setData渲染层的事件回传给逻辑层靠bind系列的事件绑定。还有个很多人一开始不在意、后面被卡住的点包体积限制。微信小程序规定主包不能超过 2MB整个小程序所有分包加起来不能超过 20MB单个分包同样是 2MB。这个限制存在的理由是启动速度——用户点开一个小程序平台希望它是秒开的而不是像装 App 那样等下载。所以图片、字体、第三方库这些大体积资源要么上 CDN要么放进分包按需加载。我自己第一次做一个带图表的小程序时直接把 echarts 整个塞进主包结果就是预览能跑、上传直接被打回那种懵的感觉至今记得。搞明白这三点——双线程、无 DOM、分包体积限制——你后面遇到的绝大多数为什么它能跑但我的不行基本都能自己推出答案。1.1 学习路线的取舍原生、跨端框架还是云开发新手最容易在这里走弯路。市面上的路线大致三条微信原生开发、跨端框架uniapp、Taro 这类、以及微信云开发。原生开发用官方那套 WXML、WXSS、JS、JSON 四件套。我个人的建议是第一遍一定学原生。原因很实在跨端框架的报错信息最终都会落到官方 API 上你如果不懂原生看到框架抛出来的错就只能靠猜而且开发者工具的调试面板、性能面板、真机调试全都是按原生结构设计的用框架的时候你依然要在这些面板里排查问题。原生还有个隐形好处——官方文档里所有的示例代码都是原生的你抄过来改改就能用学习反馈特别快。跨端框架值不值得学值得但要放到第二步。uniapp 用 Vue 语法一套代码编译到小程序、H5、AppTaro 走 React 路线。它们的核心价值是一套代码多端跑适合团队里已经有 Vue 或 React 技术栈积累的情况。用 HBuilderX 发行到微信小程序的流程其实很顺在 HBuilderX 里点发行 → 小程序-微信填上 AppID它会自动调起微信开发者工具并导入编译产物。但这个流程有个坑后面讲工具的时候细说。云开发是第三条路也是我觉得最容易被低估的。它把数据库、存储、云函数这些后端能力直接内置进来你写前端的同时就能写后端逻辑不用自己买服务器、配域名、搞备案。对于个人练手项目、校园类的小工具比如各种跑腿、报名、签到类的小程序云开发的效率优势非常大。它的限制也很明确——业务复杂到一定程度云函数的冷启动、数据库的查询能力都会成为瓶颈而且深度绑定微信生态将来想迁到别的平台成本不低。我的建议路线是原生打底云开发做个人项目跨端框架等你有明确的多端需求再上。这条路线我自己走过回过头看是最省时间的。1.2 学完之后能做什么几个真实的场景类型很多人学之前会问学这个能干嘛我给几个具体的、真实存在的类型你对照一下就知道自己想做的是哪一类。第一类是工具型比如校园跑腿、会议室预约、活动签到、报名收集。这类小程序的共同特点是有明确的数据流用户提交 → 后台记录 → 结果反馈技术难点集中在表单、列表、权限和消息通知上非常适合作为第一个完整项目。第二类是内容型比如婚礼邀请函、产品手册、展览导览。这类偏重展示核心是排版、动效和多媒体处理技术上要啃的是自定义导航栏、图片适配、视频播放这些细节。第三类是互动型涉及地图、图表、实时交互。比如带位置服务的门店导航、带数据可视化的个人记账本。这类的门槛在于第三方服务的接入地图 key、图表库、订阅消息坑多但每一个都能查到解法。第四类就是小游戏这个跟普通小程序是两套东西用的是 Canvas 加游戏引擎的思路跟本篇讲的开发流程差异较大先按下不表。把这四类想清楚你就知道自己的第一个项目该选什么了。选工具型别选内容型——内容型项目的美术工作量会吃掉你所有的学习时间。2. 环境搭建与项目初始化从注册到跑通第一个页面这一段是纯实操每一步我都写清楚为什么这么做。环境搭建这件事做对了十分钟做错了能卡你一整天。2.1 账号注册与 AppID 获取的那些细节第一步是注册小程序账号入口在微信公众平台的官网。注册用的邮箱这里有个常见误解——很多人以为必须用某个特定品牌的邮箱实际上官方并没有指定必须使用特定的邮件服务商你用任何品牌的邮箱都可以唯一的要求是这个邮箱没有注册过公众平台的账号。这一点可以放心别在这上面纠结。主体类型要认真选。个人主体和企业主体能做的事情差别不小个人主体做不了微信支付很多类目比如医疗、金融、教育培训的部分细分也开不了而且个人主体的名称和头像修改次数有限制。如果你只是自己练手个人主体完全够用但如果你知道这个项目将来要商用一开始就用企业主体注册后期主体变更的流程比重新注册麻烦得多。注册完之后AppID 在开发 → 开发管理 → 开发设置里。这个页面里还有一个关键东西AppSecret。它只在生成的时候完整显示一次关了页面就再也看不到了只能重置。所以生成之后立刻存到密码管理器里。更重要的是——AppSecret 绝对不能写在小程序前端代码里。小程序的 WXML/JS 是会被下载到用户设备上的虽然经过了打包编译但把它反解出来对于有心人来说并不困难AppSecret 一旦泄露别人就能以你的身份调用接口。这个红线记住前端只放 AppIDAppSecret 只存在你自己的服务器上。2.2 开发者工具安装与项目创建工具就下一个微信开发者工具官网直接下分 Windows 和 macOS 两版。版本上选稳定版就行预发布版虽然新功能多但偶尔会有兼容问题学习阶段没必要给自己找麻烦。新建项目的时候有几个选项要留意。第一个是后端服务选择——如果你打算用云开发就选微信云开发否则选不使用云服务。很多人新建的时候随手选了云服务后面想改成普通项目发现在新版本的工具栏里找不到云开发入口了其实是项目类型决定的。这里补一句如果你项目已经建好了想启用云开发可以在project.config.json里加上cloudfunctionRoot: cloudfunctions/字段然后在工具栏的云开发按钮里初始化。反过来新建时选了云开发但不想用把那个字段去掉、把云函数目录删掉即可。所以开发者工具里怎么没有云开发了这个问题九成是项目类型或者配置字段的问题不是工具坏了。第二个要注意的是项目目录。开发者工具会在这个目录下生成项目文件如果目录里已经有别的项目文件它会提示是否覆盖或者新建别手滑点到覆盖把之前的东西冲掉。关于用别的编辑器写代码这件事我实话说IDEA、VS Code 都能装微信小程序的插件来写代码但预览和调试最终还是要回到开发者工具。所以常见的工作流是在顺手的编辑器里写在开发者工具里预览调试。至于网上有人问IDEA 怎么启动微信小程序本质上就是装个插件做语法高亮和代码提示真正运行还是要靠开发者工具。如果你用 HBuilderX 做 uniapp那流程不一样它是先在 HBuilderX 里编译再调起开发者工具所以HBuilderX 里必须先配置好微信开发者工具的安装路径否则发行那一步会卡在找不到开发者工具上。2.3 项目目录结构与核心配置文件新建完项目你会看到这么一套结构。我按重要性排一下方便你决定先看哪个文件/目录作用新手必须搞懂的点app.json全局配置pages数组的第一项就是启动页window控制导航栏、背景色tabBar配底部标签app.js全局逻辑App({})里的onLaunch是冷启动入口适合做登录、全局状态初始化app.wxss全局样式这里的样式对所有页面生效但页面自己的 wxss 优先级更高pages/页面目录每个页面一个文件夹里面是.wxml.wxss.js.json四个文件project.config.json项目配置存 AppID、编译设置、调试基础库版本这个文件不要手动乱改sitemap.json索引配置控制页面是否允许被微信搜索索引不关心搜索可以不管app.json是重中之重我给你一个可以直接抄的最小配置{ pages: [ pages/index/index, pages/logs/logs ], window: { navigationBarBackgroundColor: #ffffff, navigationBarTitleText: 首页, navigationBarTextStyle: black, backgroundColor: #f5f5f5 }, style: v2, sitemapLocation: sitemap.json }这里有两个细节值得说。style: v2是启用新版组件样式官方推荐保持开启否则部分组件的默认外观会跟文档不一致你会以为是自己的 CSS 写错了。pages数组里路径不要写.wxml后缀就写到文件名写错了会直接报页面不存在。另外新增页面的时候光在pages里加路径还不够你得同时在目录里建好那四个文件——当然直接在app.json里敲路径然后保存开发者工具会自动帮你把文件生成出来这个偷懒技巧挺省事。2.4 基础库版本、导航栏高度这类高频参数基础库版本在哪设置——这个问题被问到的频率高得离谱。位置在开发者工具右上角详情 → 本地设置 → 调试基础库下拉选一个版本。这里的选择逻辑是调高不调低。基础库版本决定了你能用哪些 API选低了会出现某个 API 未定义的报错。但也不能盲目选最新因为用户的微信版本决定了他手机上实际能跑的基础库版本你在工具里选了个特别新的真机上老版本微信打不开就会白屏。我现在自己的习惯是开发时选最新的稳定版然后在app.json或者后台的设置 → 基本设置里配置最低基础库版本通常设成比最新版低两三个大版本覆盖绝大多数用户。另一个高频参数是顶部导航栏高度。默认导航栏的高度是系统决定的iOS 上通常是 44ptAndroid 上是 48pt上面还要加状态栏高度所以整体高度并不固定。这带来一个问题——如果你要做沉浸式的头部效果比如整屏的背景图延伸到状态栏下面默认导航栏就会挡住内容。解决办法是把导航栏设为自定义{ navigationStyle: custom }设完之后页面顶部就完全交给你了但你要自己算出该留多少空间给微信的胶囊按钮。这里的标准做法是用胶囊按钮的位置信息反推const sys wx.getWindowInfo() const menu wx.getMenuButtonBoundingClientRect() // 导航栏内容区高度 (胶囊顶部 - 状态栏高度) * 2 胶囊高度 const navHeight (menu.top - sys.statusBarHeight) * 2 menu.height // 整个头部总高度 const totalHeight sys.statusBarHeight navHeight这个公式背后的逻辑很直观胶囊按钮上下留白是对称的所以用胶囊顶到状态栏底的距离翻倍再加上胶囊自身高度就是导航栏的内容区高度。算出totalHeight之后给你的自定义头部设这个高度内容就永远跟胶囊按钮对齐了。顺便回应一个很多人好奇的问题右上角那个胶囊按钮三个点和圆圈能不能关掉。答案是不能。它是微信客户端层面的控制区用来保证用户在任何小程序里都能找到关闭返回分享这些出口属于平台底线开发者没有权限隐藏。但是——你可以做的是让页面内容避开它而不是试图消掉它。3. 核心能力实操页面、数据、登录与扩展组件环境跑通之后真正的学习才开始。这一章挑四个你必须掌握的能力每个都按是什么 → 怎么做 → 为什么这么做的顺序讲。3.1 页面生命周期与数据绑定的实操理解先建立生命周期的心智模型。小程序的生命周期分两层应用级App({ onLaunch, onShow, onHide })onLaunch只在冷启动时触发一次之后的切后台再切回来只会触发onShow。页面级Page({ onLoad, onShow, onReady, onHide, onUnload })onLoad拿页面参数其他页面跳转时带过来的 queryonShow每次页面露脸都触发onReady是首次渲染完成onUnload是页面被销毁。这里最容易犯的错是把接口请求放在onShow里。看起来很方便切回来就能刷新数据但如果你是从这个页面跳到下一个页面再返回onShow会再次触发接口就会被重复调用一次。我见过有人用这个方式做列表页结果每次返回列表都全量拉一次数据流量和性能都遭不住。正确的做法是首次加载放onLoad需要感知从后台回来的场景才用onShow而且要配合一个标志位做节流。再说setData。这是逻辑层向渲染层传数据的唯一通道但它有明确的性能代价——每次调用都会经历一次跨线程通信数据量大的时候会明显卡顿。所以有几条纪律不要在一个循环里反复setData把数据拼好再一次性设进去不要setData整个列表对象只更新变化的那一项用this.setData({ [list[3].name]: 新值 })这种路径写法不要往data里塞无关的东西data里的每个字段都会参与渲染层的同步。setData还有一个坑它是异步的。你调用完setData紧接着this.data.xxx去读拿到的还是旧值。要拿新值得用它的回调this.setData({ count: 10 }, () { console.log(this.data.count) // 这里才是 10 })列表渲染是另一个高频点。wx:for加wx:key是标配wx:key一定不能省它决定了节点复用的效率。wx:key的值要用数据里唯一且稳定的字段通常用id不要用index——用index的时候列表发生插入或删除节点复用会错位表现就是改了数据但界面没跟着变或者输入框里的内容串行了。3.2 表单组件以单选框为例看事件模型表单是小程序里最常写的东西我拿单选框来拆一下这套事件模型因为它把数据绑定 事件回传的全流程都包含进去了。WXML 长这样radio-group bindchangeonRadioChange label classitem wx:for{{options}} wx:keyvalue radio value{{item.value}} checked{{selected item.value}} / text{{item.label}}/text /label /radio-groupJS 这边Page({ data: { options: [ { label: 学生, value: student }, { label: 教师, value: teacher }, { label: 其他, value: other } ], selected: student }, onRadioChange(e) { this.setData({ selected: e.detail.value }) } })几个必须知道但文档写得不显眼的点。第一radio-group的bindchange事件返回的是e.detail.value拿到的是被选中那一项的 value是一个字符串。第二value属性即使你写成数字取出来也是字符串如果后端要数字类型记得自己转一下这个坑我踩过——提交上去的数据类型对不上后端接口报 400查了半小时才发现是这里。第三label标签在这里的作用是扩大点击热区不加的话用户必须精准点到那个小圆圈才能选中体验很差。第四用checked做受控的时候注意checked只对初始渲染有意义后续交互靠的是bindchange回写selected两者配合才形成完整闭环。把这套模式套到输入框、多选框、开关、滑块上逻辑是一样的bindchange或bindinput拿值setData回写data里存状态。理解了这一层你就理解了小程序声明式渲染的实质。3.3 登录链路从 wx.login 到后端签发凭证这是小程序开发里最核心、也最容易被简化理解的一块。流程分四步我一步步说。第一步前端调wx.login拿 code。wx.login({ success: (res) { if (res.code) { // res.code 就是要发给后端的东西 wx.request({ url: https://your-domain.com/api/login, method: POST, data: { code: res.code }, success: (r) { wx.setStorageSync(token, r.data.token) } }) } } })这个code有几个特性必须记住有效期 5 分钟且只能用一次。所以拿到就得马上用缓存下来下次再用必然失败。第二步后端拿 code 去换 openid。这一步在你的服务器上完成用 AppID 和 AppSecret 调官方接口import requests APPID 你的AppID SECRET 你的AppSecret def code2session(code): url https://api.weixin.qq.com/sns/jscode2session params { appid: APPID, secret: SECRET, js_code: code, grant_type: authorization_code } resp requests.get(url, paramsparams, timeout5) return resp.json() # 返回结构里包含 openid、session_key、unionid如果绑定了开放平台第三步后端签发自己的登录凭证。官方给的session_key是敏感信息绝对不能下发给前端。正确做法是后端拿到openid之后去自己的用户表里查这个用户是否存在不存在就创建一条记录然后自己生成一个 tokenJWT 或者随机串 Redis 都行发给前端同时把前端传来的 token 映射到openid上。第四步前端把 token 存起来。用wx.setStorageSync存到本地之后每次请求都在 header 里带上wx.request({ url: https://your-domain.com/api/orders, header: { Authorization: Bearer wx.getStorageSync(token) } })为什么要绕这一圈、不直接拿openid当凭证因为openid是长期不变的一旦泄露就等于账号被永久冒用而自签 token 可以设过期时间、可以主动吊销、可以换发。这个设计思路跟普通 Web 登录是一样的小程序只是把账号密码这一环换成了微信授权。理解了这一点你再看那些登录相关的报错就知道该往哪个方向查了。3.4 扩展能力地图、图表、视频、订阅消息怎么接地图小程序内置了map组件基础展示不需要额外配置。但如果要做地址搜索、逆地址解析、路线规划就得配合第三方位置服务。流程是去对应的位置服务平台注册开发者、创建应用、申请 key然后在app.json里配置合法域名在代码里调它的 WebService 接口。注意一个细节地图组件的层级是最高的它会盖在其他组件上面如果你要在弹窗里放地图得用cover-view之类的覆盖组件否则弹窗会被地图吃掉。这个坑非常典型很多人第一次做点击弹出地图都会撞上。图表小程序里画折线图有两条路。一是引入第三方的 canvas 图表库生态里有专门适配小程序的版本优点是功能全、配置灵活二是自己用canvas或canvas 2d手写优点是体积小。我的建议是如果你的图表需求就两三种手写更划算因为图表库动辄几百 KB塞进本来就紧张的主包会很难受。手写的核心就三步拿 canvas 上下文、按数据算点位、连线。视频普通小程序直接用video组件就行src指向你的视频地址配好poster封面。要注意的是视频地址同样要遵守域名规则另外视频组件在小程序里也是高层的浮层会压住它。如果你做的是小游戏形态、想在游戏画面里播视频那思路完全不同需要走游戏引擎的媒体播放方案跟普通小程序不是一套东西。订阅消息这是触达用户最正规的方式。流程是前端调wx.requestSubscribeMessage发起订阅授权用户点了允许之后你的后端才能在特定时机下发消息。这里有几个硬约束用户必须主动授权不能后台静默订阅一次订阅默认只能下发一次长期订阅只对特定类目开放模板内容有严格的字段限制不能自定义文案。很多人问为什么消息发不出去排查顺序就是用户是否授权过 → 授权次数是否已用完 → 模板 ID 是否正确 → 后端调接口时的access_token是否过期。access_token需要你自己缓存并管理过期时间不能每次用都去拉一次否则很快会触发频率限制。PDF 之类的内容小程序本身不能直接渲染 PDF。纯前端的做法是把 PDF 塞进web-view里用网页版的 PDF 阅读器打开或者在后端把 PDF 转成图片序列再展示。web-view也有它的限制——只能加载配置过的业务域名且个人主体基本用不了。所以在选方案之前先确认你的主体类型能不能用不然做完才发现上不了线。4. 常见问题与排查技巧实录这一章是我自己攒下来的翻车记录按出现频率排序。每个问题都给出排查顺序你可以当速查表用。4.1 真机调试时请求到不了后端这是新人第一大坑表现就是开发者工具里跑得好好的一上真机接口全部超时或者报网络错误。原因几乎都出在合法域名上。开发者工具有个选项叫不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书勾上之后工具里畅通无阻。但这个选项只对开发者工具有效真机上不生效。真机上的规则是请求的域名必须是 HTTPS、必须有有效证书、必须提前在开发 → 开发管理 → 开发设置 → 服务器域名里配置好而且域名要经过备案。真机上不想配域名怎么办打开真机调试模式手机上的调试入口调试状态下会放宽域名校验。但要注意这个只在你自己的调试场景有用体验版和正式版必须配好域名不存在绕过。排查顺序我整理成表照着走就行现象优先排查说明工具正常真机超时域名是否配置后台服务器域名里有没有加这个 request 域名报证书错误证书链是否完整部分证书在浏览器正常但小程序不认需要补中间证书局域网 IP 请求失败真机能否访问该 IP手机和电脑不在同一网段或被防火墙拦了部分接口正常部分失败域名是否分别配置request、socket、uploadFile、downloadFile 是分开配的偶发失败超时设置默认超时较短弱网下要显式延长还有一个环境相关的问题值得一提有些请求在工具里会报handshake failed due to invalid upgrade header: null这类看起来很像协议错误的提示。这个十有八九不是你的代码问题而是请求被本地环境比如某些安全软件、抓包工具的证书拦截改写了。把那些工具关掉再试问题通常就消失了。4.2 组件报错、滚动失效与本地文件路径component pages/index/index does not have a method navigatorcl—— 这类报错的意思非常直白你在 WXML 里绑了一个事件处理函数但 JS 里找不到它。排查方向有三个一是拼写错误函数名少个字母、大小写不一致这种最常见二是位置放错在Component构造器里方法必须写在methods对象内部写到外面就不认识三是页面和组件搞混了用Page定义的东西当组件用或者反过来。养成习惯每次改完事件绑定名顺手在 JS 里搜一下函数名能省不少时间。苹果手机上不能滑动—— 这个问题的根源通常是滚动容器没有明确高度。scroll-view要能滚动必须在纵向上有一个确定的高度或者被一个确定高度的父元素约束住。常见的错误是父元素用了百分比高度但祖先链上没有确定高度一路算下来是auto自然就没得滚。另一个诱因是布局用了flex但没处理好flex: 1和overflow的关系安卓上表现正常、iOS 上就卡住。我的固定解法是给滚动区域设一个计算出来的固定高度用calc(100vh - 头部高度)这种写法简单可靠。保存附件的路径—— 小程序里要给用户存文件用的是本地临时目录和用户目录这套机制。临时文件比如刚下载下来的在本次启动内有效想长期保存就得写进用户目录const fs wx.getFileSystemManager() const filePath ${wx.env.USER_DATA_PATH}/report.pdf fs.writeFileSync(filePath, arrayBuffer, binary)wx.env.USER_DATA_PATH是小程序提供的用户目录根路径理论容量有上限用完要记得清理不然会写不进去。这个路径不要硬编码不同平台上的实际值不一样。懒加载与分包—— 项目一大主包就顶到 2MB 的天花板。这时候要做两件事一是把非首屏的页面拆成分包在app.json里配subpackages二是把大图片、大 JSON 从代码里挪出去。分包加载的过程可以配一个自定义的加载提示避免用户看到空白等待。分包的核心逻辑是用户真正走到那个页面了才下载所以拆分的依据应该是访问路径把首屏相关联的页面放主包把独立的、低频的功能放分包。4.3 上线前的审核与合规检查代码写完不等于能上线。这一节说几个必须守住的红线都是审核环节会直接卡住的。第一条不要对别人的小程序做抓包、反编译取素材或接口。这个行为在技术上有各种手段可以做到但性质上属于侵权拿别人的接口和资源做自己的产品风险是实打实的。真遇到想知道别人怎么实现的正确路径是看官方文档和公开的技术文章或者自己去理解它的产品逻辑再用自己的方式实现。第二条不做诱导分享和自动拉票。用脚本批量给某个活动刷票、或者用利益诱导用户分享到群这类玩法明确违规轻则功能被限制重则小程序被封禁。做运营活动之前先去看看官方的运营规范里哪些玩法是被禁止的比事后申诉强。第三条用户数据最小化。只收集业务必需的信息。获取用户信息要说明用途位置、相册这些敏感权限要按需申请不要一进首页就把所有权限弹一遍——用户会直接关掉。第四条类目和资质。有些功能支付、直播、地图的部分能力需要对应的经营类目和资质证明。个人主体开不了的类目技术上做出来了也上不了线。在动手之前先确认类目这是我交过学费的地方。第五条别想着记住账密这类绕过机制。小程序的登录流程就是走微信授权任何试图让用户在小程序里输入账号密码来模拟登录的做法既不合规也不安全。最后分享一个我自己的排查习惯遇到解决不了的问题先做最小复现——新建一个干净项目只放报错的那几行代码看还能不能复现。能复现说明是代码或配置的问题对着官方文档逐条比不能复现说明是你项目里的某个全局配置或者第三方库在捣鬼用二分法删代码定位。这个方法听起来笨但它对付那些报错信息完全看不懂的诡异问题成功率比我试过的任何技巧都高。还有一点心得是关于学习的节奏。小程序这套东西API 数量不少但本质上很规整真正需要记住的很少需要理解的也就那么几处——双线程模型、setData的代价、登录链路的边界、包体积的约束。把这四处啃透剩下的大部分时间你是在查文档而不是在学新概念。我自己做第一个能上线的小程序花了大概两周其中有一半时间耗在域名配置和真机调试上等这些环境问题跨过去之后写业务逻辑的速度会突然快起来。所以如果你现在正卡在某个环境问题上别怀疑自己的学习能力那只是每个做小程序的人都要过的一道关。