我一直觉得,“如何从零开始写小程序”这个问题,真正的难度不在代码,而在信息差。你搜到的内容,大部分在讲某个具体功能怎么实现,却很少有人告诉你:在动手写第一行代码之前,你需要先在账号类型、技术路线、功能范围这三个问题上做决策。一旦选错,后面就是连环坑——代码写完了发现支付开通不了,界面做完了发现类目没有营业资质,开发到一半发现备案备注不会填又被打回来。我这些年从小程序商城、婚礼邀请函到内部工具类小程序都做过,想按一条真实走过的路径,把这些前置问题、开发过程中的高频坑、以及上线前的注意事项串起来,给还在迷茫的人一条能直接照着走的路。
1. 动手写之前先想通三件事:路线、技术栈和“最小范围”
很多人打开微信开发者工具之前,根本不知道自己的目标是什么,只看到一个“小程序商城”,就觉得别人怎么写我也怎么写。这是新手最大的误区。在注册账号之前,先回答三个问题:你是打算自己从零开发,还是买现成的模板平台?你的前端基础在哪个水平?第一版上线你至少需要几个页面?
1.1 根据目的选择正确路线:自研、模板还是外包
先别急着当程序员,先当自己的项目经理。我把目前主流的做法分成三条路,适合完全不同的情况:
| 路线 | 适用场景 | 优点 | 代价 |
|---|---|---|---|
| 完全自研 | 想长期迭代、有技术团队或自己愿意学代码 | 完全可控、数据归自己、定制空间大 | 开发周期长,对新手不友好 |
| 购买小程序平台 | 快速上线、功能通用、不想养技术 | 几天就能上线,便宜 | 定制能力弱,核心数据在别人手里,后续扩展受限 |
| 找公司/外包半定制 | 有预算、有明确业务需求 | 按需开发,交付质量相对有保障 | 贵,沟通成本高,后期维护要看合同 |
如果你的目标是“把线下的生意搬到微信里”,小程序商城确实是最常见的选择。但你要清楚,小程序商城和淘宝这类公域电商逻辑完全不同:淘宝靠平台流量,小程序商城靠你自己的私域运营,比如微信群、公众号、线下扫码。所以小程序商城第一版不需要做成淘宝那样的大而全,能把商品展示、在线支付、订单管理跑通就够了。
我的建议是:想靠小程序赚钱的人,第一版别自研;想靠小程序积累长期资产、后续不断调功能的人,从零自研是值得投入的。最怕的是两种心态:一种是觉得写代码很简单,一上来就想做复杂商城;另一种是花钱买了模板,结果连后台都懒得改。
1.2 主流技术栈对比:原生微信小程序、uni-app还是Taro
确定自研之后,你需要选技术栈。目前最主流的三个方向:
原生微信小程序:用官方提供的WXML、WXSS、JS来写。优点是官方文档最全、调试最直接、性能最好,缺点是只能跑在微信里,换到支付宝小程序或抖音小程序要重写。适合只做微信生态、且想深入理解小程序底层机制的人。
uni-app:基于Vue语法,用HBuilderX编辑器开发。编译后可发布到微信小程序、App、H5等平台。如果你未来有App需求,或者已经会Vue,这个选型很香。但要注意,它是“编译到微信小程序”,排查底层问题时,你还是要理解小程序本身的运行机制。
Taro:京东开源,基于React语法,也能一套代码多端发布。适合React技术栈的团队或个人。
作为从零起步的小白,我给一个反直觉的建议:先学原生微信小程序,哪怕以后要用uni-app。原因很简单——uni-app的Bug最终都要小程序开发者工具来解释,你不懂原生原理,看到编译后的报错根本无从下手。而原生逻辑熟练之后,再去看uni-app的文档,基本是降维打击。
1.3 一份真正能落地的MVP功能清单
别一上来就做十几个页面。我见过最夸张的新手规划,第一版就要做会员积分、分销裂变、直播带货、多商户入驻,结果代码写了一万行,连支付都没调通。
第一版小程序,请控制在这个范围:
- 首页:展示你的核心商品或服务,放一个清晰的转化入口
- 列表页:分类或筛选功能
- 详情页:图文信息、价格、规格选择
- 用户登录区:授权手机号或微信身份
- 下单结算流程:支付、订单状态
- 个人中心:订单查看、联系客服
五个以内页面,够检验你的产品逻辑是否成立。我见过很多第一版做复杂的项目,最后连审核都过不了,因为类目或资质根本不匹配。你先用最简单版本跑通全流程,拿到用户真实反馈,再迭代第二版,这才是高效路径。
2. 开发环境搭建:从注册账号到跑通“Hello小程序”
路线定了,开始搭环境。这一步的坑非常隐蔽,很多人卡在“注册”和“备案”上,甚至卡了一周时间。我尽量说清楚。
2.1 小程序账号的主体类型与备案意识
到微信公众平台注册小程序账号,第一步是选主体类型:个人、企业、个体工商户、政府等。
这里有一个决定后续命运的选择:个人主体无法开通微信支付。你只要想做商城、卖东西、收任何钱,就必须是企业或个体工商户主体。我看到太多人用个人身份证注册完账号,做完了界面,才发现开通支付时被驳回。到这一步,基本等于重来,因为主体类型不能后期随意更改。
另外,现在小程序上线前都要完成备案。这不是上架之后才想的事,而是开发之前就要准备好的资料。企业主体需要营业执照、法人信息,个体工商户也需要营业执照。具体流程在小程序后台“设置-基本设置-小程序备案”里填写。备案备注信息怎么填?不要写“测试”,不要写“个人练习”,最好按照实际用途写,比如“用于展示公司产品信息并提供在线咨询”“用于餐饮门店扫码点餐和会员管理”,类目和备注信息要一致。否则会被审核退回,浪费时间。
2.2 下载开发者工具,拿到AppID,创建第一个项目
注册完成后,到微信官方下载“微信开发者工具”稳定版。安装好后,用管理员或项目成员身份的微信扫码登录。创建项目时,要填写AppID——这个ID在小程序后台“开发-开发管理-开发设置”里可以找到。
新手容易犯的一个错误:为了省事,选择“测试号”创建项目。测试号确实能让你快速看效果,但它无法调用支付、跳转、部分设备能力。我建议直接用自己的AppID,哪怕主体资料还在审核中,也可以先用测试号跑通代码,等AppID下发后,再做一次替换。
创建项目时的模板选择,我建议选“不使用模板”,自己建一个空目录,然后手动创建四个同名文件(js、json、wxml、wxss)。这样你能彻底搞懂页面是怎么被组织起来的,而不是被模板带着走。
2.3 “登录用户不是该小程序的开发者”的排查闭环
这是开发群里的高频报错:用微信扫码后,开发者工具提示“登录用户不是该小程序的开发者”,或者后端接口返回“错误: 登录用户不是该小程序的开发者”。
完整的排查链路应该是这样:
- 先在微信公众平台后台,左侧菜单找到“成员管理”,把当前微信添加为“项目成员”或“体验成员”。
- 注意,你注册的账号是管理员,管理员一定可以登录;但如果你是被别人拉进去的成员,需要管理员在微信里确认邀请。
- 添加完后,退出开发者工具,重新用微信扫码登录,而不是刷新页面。工具经常缓存权限,不彻底退出没用。
- 还不行就删掉项目,重新导入。进入到项目详情页,检查AppID是否真的填写正确,是不是复制到了别人的项目ID。
这套排查我做过太多次,90%的情况是第一步和第三步没做对。权限问题别急着改代码,先检查后台配置。
3. 第一个业务页面的完整链路:结构、生命周期、标题与导航栏
环境好了,开始写页面。这里我带你完整走一遍“页面是怎么跑起来的”,同时把动态设置标题、顶部导航栏高度这两个高频需求一并讲了。
3.1 小程序页面文件结构与app.json配置
一个原生小程序的页面由四个同名的文件组成:index.js(逻辑)、index.wxml(结构)、index.wxss(样式)、index.json(页面配置)。页面所在目录要注册到app.json的pages数组里。例如:
{ "pages": [ "pages/index/index", "pages/list/list", "pages/detail/detail" ] }app.json里还可以配置window、tabBar、networkTimeout等。小程序是全局配置驱动的,很多时候你找不到某个页面为何没生效,就是因为在错误的层级配了参数。页面自己的json配置会覆盖app.json里的同名配置,比如那个页面要单独设置标题或背景色,写在页面的json里就够了。
首页其实是小程序的“脸面”,我建议第一版首页直接使用开发者工具自带的模拟器调试,把wxml和wxss写熟,再进入真实手机预览。手机预览时,注意开启“真机调试”,真机上才能看到网络请求和部分组件在不同机型的表现。
3.2 onLoad、onShow、onReady的执行顺序决定代码写在哪
小程序的页面生命周期是新手最容易懵的地方。我帮你记住一个口诀:一个页面第一次打开,先走onLoad,再走onShow,最后走onReady。但如果页面被切到后台再回来,只走onShow,不再走onLoad。
这意味着:
- 数据请求写在哪里?第一次打开需要的数据,写在onLoad里;每次进入页面都需要刷新的数据,写在onShow里。
- DOM相关的操作写在哪?要等到onReady之后,页面真正渲染完成才能操作。你可以用wx.nextTick等待渲染完成。
- 页面间通过参数跳转,参数在onLoad的options里拿到。
我踩过的一个坑:把请求写在onLoad里,然后从详情页返回列表页时,列表数据没有刷新。原因就是返回时不会重新触发onLoad,只能触发onShow。遇到这种情况,把刷新逻辑挪到onShow即可。
3.3 动态设置标题与顶部导航栏高度适配
“小程序动态设置标题”是搜索热词,我重点讲。
静态标题:在页面json里写navigationBarTitleText,打开页面就是这个标题,简单可靠。
动态标题:想根据页面内容变化,用wx.setNavigationBarTitle:
wx.setNavigationBarTitle({ title: '订单详情 - 订单号123' });但这里有一个很多人不知道的坑:如果页面是tabBar页面,也就是在app.json的tabBar配置里注册过的页面,调用setNavigationBarTitle会不生效。因为tabBar页面的标题必须在tabBar里统一配置。解决办法:要么改用非tab页面,要么接受统一标题,或者用自定义导航栏完全自己控制渲染内容。
顶部导航栏高度适配,主要发生在你要做自定义导航栏的时候。设置页面json:
{ "navigationStyle": "custom" }然后手动计算导航栏高度。获取状态栏高度用wx.getSystemInfoSync().statusBarHeight。导航栏内容区域高度,通常是胶囊按钮高度(约32px)加上上下间距。我常用的公式:
const systemInfo = wx.getSystemInfoSync(); const capsule = wx.getMenuButtonBoundingClientRect(); const navBarHeight = (capsule.top - systemInfo.statusBarHeight) * 2 + capsule.height;拿到高度后,顶部占位视图的高度就是statusBarHeight + navBarHeight。这个公式在大多数安卓和iOS机型上都能适配,但iPhone带安全区域的机型,还要配合env(safe-area-inset-top)做兜底。
4. 写业务逻辑绕不开的四道坎:请求、登录、支付与参数编码
页面结构写好后,你就要面对小程序真正“有技术含量”的部分:网络请求、登录鉴权、支付能力和参数编码。这四个点几乎决定一个项目能不能上线。
4.1 wx.request封装和常见的网络错误
小程序不能直接使用浏览器的XMLHttpRequest,要用wx.request。我建议第一件事就封装一个统一请求函数,别想到哪写到哪。
function request(url, method, data) { return new Promise((resolve, reject) => { wx.request({ url: getApp().globalData.baseUrl + url, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json', 'Authorization': wx.getStorageSync('token') || '' }, success: (res) => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data); } else { handleError(res.statusCode); reject(res); } }, fail: (err) => { handleError(err.errMsg); reject(err); } }); }); }常见错误有两个:一是域名没有配置到小程序后台的request合法域名里。开发时可以在开发者工具里勾选“不校验合法域名”,但真机预览就无法绕过,必须去后台“开发管理-服务器域名”里添加域名。二是请求全部走HTTPS。微信小程序不允许明文HTTP请求(开发工具里可以临时打开),线上必须HTTPS。
4.2 GET参数里的等号和中文为什么变成百分号
有一个经典问题:使用GET参数时,参数里边有等号,结果被转换成百分号,问怎么避免。
直接说结论:这不是错误,而是URL编码的标准行为。URL里不允许出现原始的中文、等号、空格这些字符,所以客户端会把它们变成百分号形式的编码。等号(=)变成%3D,中文按UTF-8编码成几组百分号字节。这是所有现代网络库的标准处理方式,不需要“避免”。
服务端收到请求时,框架会自动解码。如果你在服务端拿到了百分号,说明服务端没有调用解码方法。比如在Node.js里没有用decodeURIComponent解析参数,而是直接取了rawQuery字符串。搭配场景再提醒一个要点:对参数的每个值使用encodeURIComponent,而不是对整个URL编码。举个反面例子,如果你把整个URL都encodeURIComponent掉,那么&和=会被全部编码,服务端就无法拆分参数了。正确做法:
const key = encodeURIComponent('a=123'); const url = 'https://api.example.com/getData?key=' + key;这样URL实际发送的是key=a%3D123,服务端解码后拿到的还是a=123。
4.3 支付能力到底要怎么开通,为什么会被限制
搜索里有句话很典型:“小程序对应支付能力已被限制”。我见过很多人在开发完成后才发现这个问题。
根本原因通常有三类:
- 主体不支持。个人主体不能开通微信支付,这是硬性规定,必须换成企业或个体工商户。
- 后台没开通。在小程序后台左侧菜单“微信支付”入口,要完成商户号申请或绑定。支付能力不是自动开放的,需要提供营业执照、经营资质等材料审核。
- 类目或风控问题。小程序的类目如果和你实际卖的商品不一致,比如你填的是“工具”,实际卖食品,支付审核会被驳回,甚至上线后风控会限制支付能力。
还有一个很隐蔽的点:如果你要做类似“骑手分账”“多商户入驻”这种模式,除了小程序支付,还要在微信支付商户平台开通“分账”能力。有些人连分账协议和隐私政策都看不到,后台就提示无法注册,大概率是主体类型或经营范围不匹配。这种情况没有捷径,先把主体资质和经营范围核实清楚,再到商户平台发起申请。
4.4 wx.login换token时容易踩的地基问题
微信小程序登录的标准流程是:前端调用wx.login拿到一个code,把code发给后端;后端拿code到微信接口换取openid和session_key;后端自己生成一个自定义登录态token返回给前端;前端把token存起来,后续请求带上,用它识别用户。
注意,code只能用一次,而且有效期很短。有些人把code存起来反复用,后端一直报40029 invalid code,这就是原因。正确的每次要重新调用wx.login获取新code。
另外,绝对不要把appid和secret写在小程序前端代码里。secret相当于你后端接口的钥匙,一旦暴露,别人可以冒充你的服务端。正确做法是secret只保存在后端服务器环境变量里,前端拿不到。
5. 地图、导出Excel、拖拽排序:三个高频需求的标准做法
从这一步开始,你已经不是小白了,开始接触“功能型页面”。我选了三个出现频率特别高的需求:接入高德地图、导出Excel表格、长按拖拽滚动。这三个方向都能直接套用到实际业务里。
5.1 高德地图在小程序里的接入步骤
小程序内接入高德地图,一般不是直接在高德官网申请,而是用小程序的map组件,再配合高德提供的微信小程序SDK来实现定位、POI搜索、路径规划等能力。
具体步骤:
- 去高德开放平台注册账号,创建应用,申请一个Key。类型选“微信小程序”。
- 在小程序后台配置服务器域名或下载高德官方的小程序SDK文件到项目里。
- 使用wx.getLocation获取当前坐标。
- 使用SDK的Geocoding和Regeocoding获取详细地址信息。
有一个容易踩的坑:小程序map组件本质是原生组件,层级会盖住普通页面元素。如果你要在页面盖一个自定义弹窗或按钮,需要用到cover-view组件。否则你会发现,弹窗总是在地图下面出不来。
5.2 前端导出Excel的两种可行方案
“微信小程序导出excel”这个需求,常见于订单数据导出、后台报表场景。方案有两种:
方案一:后端生成Excel文件,返回一个下载链接。前端用wx.downloadFile下载,再用wx.openDocument打开预览。这是最成熟可靠的方式,推荐优先使用。
方案二:纯前端用SheetJS(xlsx)库生成Excel的Base64数据,再通过wx.getFileSystemManager().writeFile写入本地文件,最后用wx.openDocument打开。适合数据量小、不想麻烦后端的情况。但要注意,小程序包体积会变大,框架库大约几百KB,最好用分包或动态加载处理。
我在实际项目里更推荐方案一。原因是纯前端导出Excel受限于小程序沙箱环境,处理复杂样式、多sheet时会有兼容问题,而且处理大数据量时容易内存溢出。
5.3 长按拖拽滚动与特殊组件在iOS和鸿蒙上的注意点
长按拖拽滚动,常见的实现是基于movable-area和movable-view组件。先渲染一个容器,然后把每个可拖拽项目封装成movable-view,在长按事件触发后,动态设置movable-view的偏移量。数据模型上,你还要维护每个项目的index,拖拽到位后重新排序。
这里要注意,微信小程序的拖拽排序没有现成的list组件,需要自己处理边界判断。一个偷懒但有效的方案是:长按开始时震动反馈,拖拽过程中不逐帧更新数据,而是利用transform做视觉位移,松手后再一次性更新数组,这样性能会好很多。
特殊组件兼容性问题,在iOS上最容易出现的是uni-datetime-picker放在scroll-view里选择器弹不出来的Bug。原因是iOS渲染机制对原生组件的层级和滚动容器有特殊处理。如果遇到,优先把picker移到滚动容器外层,或者使用微信官方picker组件替代。用鸿蒙系统手机测试时,视频播放异常的现象也比较常见,多半是video组件的真机兼容问题,建议降低码率或改用了live-player等组件测试。
6. 调试、白屏优化与上线:最后一个阶段的实战经验
功能写完了,不代表能上线。在这个阶段,你会遇到很多“开发环境正常,真机就抽风”的问题。我挑三个最常见的,用实际经验告诉你怎么处理。
6.1 用Charles和Wireshark抓包小程序请求
开发阶段有个让人头疼的场景:后端说“我接口没问题”,你看到前端报错但不知道请求到底长什么样。这时候抓包工具就派上用场了。
Charles是调试HTTPS请求的主力工具。抓包步骤:手机和电脑连同一个Wi-Fi,电脑端打开Charles,查看本机IP(在Help-Local IP Address里)。手机Wi-Fi设置里,把HTTP代理改为手动,服务器填电脑IP,端口默认8888。手机首次访问任意网页,会弹出证书安装提示,安装并信任Charles根证书。然后在Charles里开启SSL Proxying,并添加你要抓的域名。这样小程序的所有HTTPS请求就都能在电脑上看到明文内容了。
Wireshark则是更底层的网络抓包。它能看到TCP三次握手、TLS握手过程、连接是否被中断。业务调试一般用不上,但如果遇到网络时通时不通、上传下载卡住这类问题,Wireshark能帮你判断是服务器主动断开还是网络层面丢包。
一个重要提示:抓包结束后一定要关闭手机代理设置,不然手机会上不了网。另外,证书只应在自己测试环境安装,不要下载来路不明的证书文件。
6.2 tab切换白屏闪动的排查思路
“小程序底部tab切换页面一瞬间白屏闪动”这个现象,特别像格式化故障,常见原因和排查方向如下:
第一,页面首次创建时onLoad里做了太多同步任务,比如同步读取大量Storage数据、执行复杂计算。每个tab页首次创建都会加载,如果加载时间过长,切换时就会出现白屏。解决方案:把非必要的初始化逻辑放到onReady或setTimeout里延迟执行,页面顶部先渲染骨架屏。
第二,原生组件与普通组件层级冲突。比如首页用了map或canvas,切换tab时原生组件被强制重新绘制,视觉上就像闪白。这类问题要测试排查:单独去掉某个组件看是否消失。
第三,页面数据在onShow里同步刷新,导致切换时先渲染空数据再渲染完整数据。这种“白屏闪动”其实是数据驱动的。优化方案:缓存上一次的页面数据,在onShow里先显示缓存,请求完成后再更新。
排查这类问题的核心思路是“二分法”:先禁用tabBar改成普通页面跳转,看是否还闪;再逐一注释页面里的组件和代码块。别一上来就怀疑框架,很多问题出在你自己的渲染逻辑上。
6.3 备案备注信息怎么填,以及上线前哪些问题必须自查
备案备注信息是个看似简单但翻车率极高的字段。搜索里问“小程序备案备注信息怎么填”的人特别多。我的建议是:直接写清小程序的实际用途,并和你选择的类目保持一致。例如,你选了“餐饮-餐饮服务”类目,备注就写“用于为用户提供餐饮门店信息展示和在线点餐服务”;你选了“电商平台”,备注就写“用于商家在平台展示商品与用户完成在线交易”。千万别写“个人学习”“空”“无”,否则审核人员无法判断你的用途,很容易退回。
上线前我还建议你自查以下项目:
- 隐私保护指引:后台要配置“用户隐私保护指引”,特别是你收集了手机号、位置、头像昵称等信息,必须明确告知用户用途。
- 用户协议:商城类小程序一定要有用户协议页面,说明注册、交易、售后和争议处理规则。
- 类目资质:上传的营业执照经营范围要覆盖你实际售卖的商品和服务。
- 体验版测试:把代码上传为体验版,让至少三个不同机型的手机实测一遍。大部分审核驳回都发生在真机测试阶段,而不是代码本身。
这一套流程走下来,你的“从零开始写小程序”就不只是一个口号,而是一条实际跑通的路径。能把上面这些坑都趟过去的人,才有资格说一句“我真会做小程序了”。