简介:一套包含123个微信小程序源码的zip压缩包,大小179.32MB,所涉案例覆盖视频、音乐、商城、资讯、工具、游戏等多类常见场景,适合小程序入门者、前端开发者以及需要快速搭建Demo的爱好者参考。资源中既有“芒果TV”“AppleMusic”“B站首页界面设计”等热门界面仿写,也有“仿网易云音乐”“知乎日报”“2048”“别踩白块”等完整小游戏与社区案例,可集中学习组件化结构、tab切换、轮播图、瀑布流、侧滑布局、canvas绘图、地图定位、手势解锁、富文本解析等技术点。全部项目按目录独立拆分,便于按需检索和对照调试,能省去自行收集整理的时间。目前已有6968人学习下载,使用热度较高,想系统刷案例或找设计灵感的人可从中获得较丰富的模板与改造基础。
1. 微信小程序源码包到手先别急着导入,123 个项目是要筛着用的
这批压缩包里是 123 个微信小程序工程,从 B站首页、芒果TV、今日头条这类界面还原,到移动小商城(node 前后台)、飞翔的小鸟(canvas + java 后端)这类完整闭环,再到手势解锁、二维码生成器、瀑布流布局这种单点功能。直接全部导入微信开发者工具会发现一半以上报错,原因大多是老基础库的 API 写法过期,但这不是坏事——它逼着你把项目当成「解剖样本」而不是「成品」。
这堆源码真正值钱的是给两种人看:刚入门的开发者需要大量真实工程做参照,理解页面结构、数据流、组件边界;一线工程师则能在给客户交付 demo 时,从这里快速抽取界面骨架和功能模块做拼接。下文按「项目盘点 → 组件复用 → 前后端联调 → 批量验证」的顺序拆,每一层都配可抄作业的命令和代码。
2. 项目级源码怎么盘:按目录、启动页与老接口迁移做筛选
拿到压缩包先别急着解压后一个个双击打开。微信小程序工程的体积差别极大,有完整商城的项目带后台、带图片资源能到几十 MB,单页 demo 只有几 KB。正确做法是在命令行层面先做一次全量体检,把项目分类、找出哪些工程值得深入研究,再决定导入顺序。
2.1 先给 123 个项目做体检:三条命令完成分类
解压后第一件事不是打开编辑器,而是用三条命令把这批工程盘清楚。常见做法是到终端里做目录级统计,比人工翻文件快得多,而且能直接暴露一批文件的真实物理分布。
unzip 123个微信小程序源码.zip -d mp_projects cd mp_projects # 只看两级目录,避开 node_modules 和构建产物 find . -maxdepth 2 -type d | sort | head -80 # 按体积排序,找出完整项目与单页 demo 的差异 du -sh */ | sort -h | tail -20 # 检查还有多少工程在用老授权接口,这些必须改造 grep -rl "wx.getUserInfo" --include="*.js" . | head -30第一行find -maxdepth 2把嵌套过深的目录滤掉,拿到每个小程序的第一层结构,此刻能看到谁有pages、谁有components、谁带server目录。第二行du -sh */会瞬间拉开差距,移动小商城、创客+这类整站项目体积显著偏大,而圆形菜单、摇一摇换文章这类单功能 demo 只有几十 KB。第三行grep -rl指向授权接口的地毯式扫描:wx.getUserInfo在微信收紧隐私策略后从弹窗授权退化为静默失败,还在用它的老工程必须改造成wx.getUserProfile或wx.login体系,它直接影响项目能不能在真机上联动后端。
我做归类时习惯把 123 个项目压成三张表。整站级且带后端的先看移动小商城(node 前后台完整)、飞翔的小鸟(canvas 游戏 + java 后端)、腾讯云一站式解决方案;界面还原级重点看 B站首页、芒果TV、星巴克中国、掘金首页信息流;组件级则挑手势解锁、二维码生成器、瀑布流布局、富文本解析。
| 分类维度 | 代表项目 | 主要学习点 | 改造风险 |
|---|---|---|---|
| 整站级带后端 | 移动小商城、飞翔的小鸟、腾讯云一站式方案 | 登录鉴权、接口分层、真机联调 | 需要本地起后端服务,环境变量多 |
| 整站级纯前端 | 创客+、电商-拼团、车源宝、同乐居商城 | 业务页面流、状态管理、tabBar 设计 | 依赖大量网络图片,域名需换 |
| 界面还原级 | B站首页、芒果TV、今日头条、星巴克中国 | WXML 布局、轮播动画、导航栏适配 | 视觉资源来自第三方,本地会裂图 |
| 组件/交互级 | 手势解锁、二维码、瀑布流、城市切换 | 组件封装、canvas 绘制、触摸事件 | 单个文件即可抽走,风险最低 |
这里有个判断标准:体积低于 100 KB 的直接按组件看待,拆出你要的单文件就行;体积几 MB 且带server或api目录的,才需要完整跑起来。
2.2 每个工程导入前先核对 app.json:启动页与 tabBar 配置
把工程拖进微信开发者工具之前,我习惯先手写看一遍app.json再放行。这个文件决定了小程序启动后第一屏是谁、底部 tab 有几个、导航栏长什么样。很多源码包打开后白屏,追根到底就是pages数组指向的文件不存在,或者文件路径大小写和真实目录不一致,而 Windows 和 macOS 的文件系统大小写敏感性不同,老项目在 Windows 上解压后最容易踩这个坑。
{ "pages": [ "pages/index/index", "pages/category/category", "pages/cart/cart", "pages/mine/mine" ], "window": { "navigationBarBackgroundColor": "#ff2d4b", "navigationBarTitleText": "电商小程序示例", "navigationBarTextStyle": "white" }, "tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/category/category", "text": "分类" }, { "pagePath": "pages/cart/cart", "text": "购物车" }, { "pagePath": "pages/mine/mine", "text": "我的" } ] } }pages数组第一项决定编译后加载的首页,想快速预览某个页面,把它挪到第一位就能直达。tabBar.list最少 2 项、最多 5 项,text 超过 4 个汉字会出现省略号,iconPath 不配置也可以运行但视觉上只剩文字。navigationBarTextStyle只有 black 和 white 两种取值,深色背景配 white,浅色背景配 black,这个字段配错会直接导致导航栏标题看不见。
对这批源码里的老项目,还要额外检查顶部导航栏与胶囊按钮的兼容性。小程序右上角胶囊按钮的位置随机型变化,保险的做法是用wx.getMenuButtonBoundingClientRect()拿到胶囊坐标再动态计算自定义导航栏高度,直接硬编码 64 px 的方案在刘海屏和折叠屏上都会错位。
2.3 老工程必改三处:授权逻辑、基础库版本与 rpx 适配
这 123 个源码里不少工程是 2018-2020 年前后写的,导入后的报错集中在三块,提前批量修比逐个等报错效率高得多。
第一处是用户信息授权。微信在 2021 年 4 月之后不再支持wx.getUserInfo直接弹窗,需要用户先点击某个按钮,在回调里调用wx.getUserProfile才能拿到头像昵称。老代码的onLoad里自动弹窗逻辑全部失效,改造要点是把取用户信息的动作绑到按钮 tap 事件上。
// 老写法:onLoad 里直接调,2021 年后真机必然失败 // wx.getUserInfo({ success: res => this.setData({ userInfo: res.userInfo }) }) // 新写法:必须由用户的点击行为触发 bindGetUserProfile() { wx.getUserProfile({ desc: '用于完善会员资料', success: (res) => { this.setData({ userInfo: res.userInfo }); wx.setStorageSync('userInfo', res.userInfo); }, fail: () => wx.showToast({ title: '需要授权才能继续使用', icon: 'none' }) }); }中文缩略语注意一下,代码里的desc字段在wx.getUserProfile里是必填的,微信审核时会审核这个文案是否与实际用途一致。老项目的授权拿到的userInfo不再包含手机号,手机号需要走getPhoneNumber按钮能力,这是另一套接口,不要混用。
第二处是基础库版本。开发者工具的「详情 - 本地设置」里把调试基础库切到 2.x 以上,大部分老报错会直接消失,wx.getSystemInfo这类 API 虽然废弃但还在兼容期,只是控制台会刷警告。不兼容的是open-type="getUserInfo"这类按钮行为,它与上述授权策略属于同一批变更。
第三处是 rpx 动态单位。老项目里大量用固定 px 布局,在不同屏幕宽度下要么挤压要么留白,新手拿到手不要急着全改,优先处理首页首屏和商品列表卡片,其余按需迁移。真正要修的地方是 canvas 类组件,canvas 的尺寸单位是 px,绘制前先通过wx.createSelectorQuery拿到节点实际宽高再做换算,原样照搬大概率在真机上偏位。
3. 从看页面到用组件:把口罩源码里的功能模块抠出来做复用
分类筛选之后,下一步是挑出「能拿走直接用」的模块。这批源码里真正的宝贝不是整站代码,而是手势解锁、二维码生成器、瀑布流布局、富文本解析、城市切换、圆形菜单这类只解决一个问题的独立模块。它们大多存在于完整项目内部,需要你手动抠出来封装成小程序自定义组件,才能在新项目里即插即用。
3.1 为什么 demo 里的代码不能直接复制:页面与组件有本质差异
源码包里多数功能模块是用Page()写进某个业务页面的,它和业务数据、事件回调、样式耦合在一起。直接复制文件只能带走界面,带不走复用能力。微信小程序的组件化单位是Component(),页面与组件的核心差异在于三条:页面有onLoad、onShow这类生命周期,组件另有attached、detached节点生命周期;页面通过setData直接改自己的 data,组件靠properties接收外部参数并通过triggerEvent向外部抛事件;组件的样式默认隔离,页面样式无法污染组件内部。
判断一个模块能不能抽成组件,看它和原有页面的耦合深度。二维码生成器只要输入一段字符串,输出一张 canvas 图,这种输入输出清晰的模块是最高优先级的抽取对象。而「你画我猜」和传感器、房间、计分都有关系,抽组件的性价比就低很多,更适合留在原工程里做整体阅读。
3.2 手写一个可复用的二维码生成组件:Component 四件套结构
以资源包里的二维码生成器为例,把它从原工程中抽出来做成通用组件,需要四个文件:qr-gen.js、qr-gen.json、qr-gen.wxml、qr-gen.wxss,放在components/qr-gen/下。组件对外暴露两个属性:内容content和尺寸size,业务方传入任意字符串,组件渲染出对应二维码。
// components/qr-gen/qr-gen.js Component({ options: { multipleSlots: false }, properties: { content: { type: String, value: '' }, size: { type: Number, value: 200 } }, data: { codePath: '' }, observers: { 'content, size': function (content, size) { if (content) this.generate(content, size); } }, methods: { generate(content, size) { // 调用压缩包内 qrcode.js 的绘制逻辑,输出图片临时路径 const codePath = drawQrcode({ content, size, canvasId: 'qrCanvas' }); this.setData({ codePath }); }, onTap() { // 向父组件抛事件,比如让外部拿到二维码内容去做分享 this.triggerEvent('scan', { content: this.data.content }); } } });逻辑说明:properties是组件的对外接口,type: String表示这个属性接受字符串,value: ''是缺省值。observers类似 Vue 的 watch,content, size两个字段任一变化都会触发回调,适合二维码内容由接口动态返回的场景——数据到了才绘制,避免首帧空白。画好的二维码先存成图片路径放到data.codePath,再在 WXML 里用<image>渲染,比直接操作 canvas 更方便做长按保存和分享。
对应 WXML 只需三行,绑定点击事件,把图片路径交给 image 组件:
<view class="qr-wrap" bindtap="onTap"> <image wx:if="{{codePath}}" src="{{codePath}}" style="width:{{size}}px;height:{{size}}px;"></image> <canvas wx:else canvas-id="qrCanvas"></canvas> </view>补充一个细节:首次渲染时codePath为空,canvas 作为占位先出现,绘制完成后再切换成 image,保证使用方拿到的始终是图片。options.multipleSlots默认关掉多插槽,只有需要向组件内部插入多个自定义区块时才必须开启,开多了会影响组件性能,按需设置。
3.3 组件传参的隐藏坑:Boolean 属性与默认值陷阱
把模块组件化之后,最先遇到的坑在传参类型。WXML 里传布尔值和传字符串的写法不一样,这一点在从老代码里抽组件时很容易踩中。
<!-- 错误写法:双引号里是字符串,isShow 会被转成 true --> <nav-bar is-show="false"></nav-bar> <!-- 正确写法:必须用花括号包裹才能传布尔值 --> <nav-bar is-show="{{false}}"></nav-bar> <!-- number 同理,直接写数字也会被当字符串 --> <progress-bar percent="{{80}}"></progress-bar>这是因为 WXML 的静态属性一律按字符串解析,只有双花括号绑定才保留原始类型。抽老组件时我见过太多maxLength="12"的写法,类型被转成 String 后,组件内部的数值校验和默认值逻辑全部跑偏。属性默认值也值得关注:给Boolean类型的属性设置默认false,外部不传时正常为关闭态;一旦外部写了is-show="{{false}}",等同于显式赋值,覆盖默认值,两者语义不同,调试时别被这个绕进去。
顺手整理了一张组件参数排查表,针对这批资源里的高频组件:
| 原始模块 | 建议属性 | 常见错误 | 正确姿势 |
|---|---|---|---|
| 富文本解析 | html: String | 直接渲染富文本里的 script | 先做标签过滤再传入 |
| 瀑布流布局 | columns: Number | 固定写死两列,不给外部改 | 暴露列数属性,内部按列分桶 |
| 手势解锁 | points: Array | 只在模拟器测,真机坐标偏 | 用createSelectorQuery动态取宽高 |
| 城市切换 | list: Array | 索引字母与头部吸顶不同步 | 观察者监听 list 变化后重算索引 |
城市切换这类模块,原实现往往是页面里几百行 WXML 加 JS。抽组件时把「当前选中城市」和「城市列表」作为属性传入,内部维护滚动位置,选中结果通过triggerEvent抛出去,外部不用关心它的滚动计算逻辑。
4. 前端的联调与数据链路:从 wx.request 到 node 后端
组件抽完之后,真正让这批源码跑起来的是数据链路。123 个项目里移动小商城带 node 前后台,飞翔的小鸟带 java 后端,腾讯云一站式方案则把环境配置也做成了模板。这三类工程的共同点是你不能只改前端,还必须把本地后端拉起来,小程序才能有数据可渲染。这一章把从封装请求到联调决算的完整链路走通。
4.1 源码包里的 request 封装通常差在哪
查看这批源码的网络层代码,会发现一个共同短板:请求封装过于简单,多数只有一层wx.request包成 promise,没有统一处理登录态、错误码和超时。移动小商城这类的utils/request.js稍好一些,但依然把 baseURL 硬编码在业务文件里,换环境要全局搜索替换。后端项目在本地跑通后,前端必改的就是这个文件。
4.2 一份可以直接换用的 request 封装:统一收口与拦截处理
我一般会建议把网络层收敛到一个文件里,所有页面只依赖它。下文是一份可落地的封装,代目录结构可直接覆盖源码包里的utils/request.js,业务代码无需大改。
// utils/request.js const request = (url, method = 'GET', data = {}) => { const token = wx.getStorageSync('token') || ''; return new Promise((resolve, reject) => { wx.request({ url: `${getApp().globalData.baseURL}${url}`, method, data, header: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, timeout: 10000, success: (res) => { // 后端统一返回 { code, data, message },code 为 0 表示成功 const { code, data: payload, message } = res.data || {}; if (code === 0) { resolve(payload); } else { wx.showToast({ title: message || '业务异常', icon: 'none' }); reject(res.data); } }, fail: (err) => { wx.showToast({ title: '网络异常,请检查后端服务', icon: 'none' }); reject(err); } }); }); }; module.exports = { request };逻辑说明:这段代码把三个关键点做了统一收口。第一个是 token,从本地缓存取出后放进Authorization请求头,后端按 Bearer 格式解析,一套模板适配多个老项目。第二个是超时控制,timeout字段在小程序里默认 60 秒,对商城的商品列表这类接口太慢,统一设置为 10 秒能更快暴露后端假死问题。第三个是响应体约定,所有后端返回都拆成code、data、message三层,code === 0才算成功,其余错误直接 toast 提示并让 promise 进入 reject,避免业务代码里到处写if (res.data.code !== 0)。
与后端联调时,我习惯在getApp().globalData里维护一份环境映射:
| 环境 | baseURL | 适用场景 | 注意事项 |
|---|---|---|---|
| 本地开发 | http://127.0.0.1:3000 | 开发者工具模拟器 | 工具内要关掉域名校验 |
| 真机调试 | http://192.168.x.x:3000 | 手机与电脑同一局域网 | 后端监听需绑定0.0.0.0 |
| 测试环境 | https://test-api.example.com | 多人联调 | 域名需在小程序后台白名单 |
4.3 本地联调的硬性条件:域名校验、局域网 IP 与附件保存
小程序与普通 Web 页面的最大不同,是它强制校验请求域名必须配置在后台白名单,且只认 https。开发阶段要绕开这个限制,操作路径是:微信开发者工具右上角「详情」-「本地设置」-「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」,勾上这一项后 localhost 和局域网 IP 都可以直连。
模拟器上跑http://127.0.0.1:3000没问题,真机就不行,因为手机的 127.0.0.1 指向手机自身。这时候需要把电脑的局域网 IP 填进 baseURL,并保证后端监听所有网卡。node 端对应的修改:
// app.js,老商城项目的入口文件 app.listen(3000, '0.0.0.0', () => { console.log('API server listening on port 3000'); });说明:app.listen的第二个参数0.0.0.0表示监听所有网络接口,省略它时 node 默认只监听本机回环地址,局域网里的手机自然连不上。改完之后用ifconfig或ipconfig查本机 IP,填回小程序的 baseURL 就能联调。另外文件下载类接口,小程序保存附件用wx.downloadFile加wx.env.user_data_path拼路径落到本地,这个 API 在开发者工具里路径是模拟的,真机上才是真实沙箱目录,调试时不要被路径不一致误导。
4.4 长连接的正确姿势:素材里的 TCP/IP 长连接项目怎么落地
资源包里有「TCP,IP长连接」这个专题工程,需要提前澄清一个技术前提:小程序运行在微信客户端提供的 WebView 与原生层混合环境里,页面 JS 没有操作裸 socket 的能力。TCP/IP 这种传输层协议在小程序端不是不能提,而是你只能通过wx.connectSocket发起 WebSocket 连接,由微信客户端完成底层的 TCP 握手和收包,业务侧拿到的是经过解析的消息事件。
const socket = wx.connectSocket({ url: 'ws://192.168.1.20:3000/ws', header: { token: wx.getStorageSync('token') } }); socket.onMessage((res) => { const msg = JSON.parse(res.data); // 按消息类型分发到页面,长连接心跳由服务端主动下推 console.log('recv msg type:', msg.type, msg.payload); });源码包里所谓「TCP/IP 长连接」,真正落地时几乎都是 WebSocket 模拟出来的长连接语义。要设计可靠的长连接,需要关注断线重连与心跳。心跳不能只靠前端定时发 ping,要记录服务端最后一次下推时间,超过阈值后主动重连并做消息补偿。IM 类应用必须带上自增序列号,否则断线期间的离线消息会丢。这套逻辑在「会议精灵」「你画我猜」这类实时项目中都要自己补齐,原包里基本没有。
5. 批量验证与打包:让 123 个项目都能在本地快速起服务
最后一章聚焦收尾动作。面对 123 个工程,逐个手工验证不现实,写一个 Node.js 脚本扫描所有子目录的 app.json,检查pages数组指向的页面文件是否真实存在,可以一次暴露大部分启动即白屏的病根。
const fs = require('fs'); const path = require('path'); const rootDir = './mp_projects'; fs.readdirSync(rootDir).forEach((name) => { const appJsonPath = path.join(rootDir, name, 'app.json'); if (!fs.existsSync(appJsonPath)) return; const app = JSON.parse(fs.readFileSync(appJsonPath, 'utf8')); const missing = (app.pages || []).filter((p) => { return !fs.existsSync(path.join(rootDir, name, `${p}.js`)); }); if (missing.length) { console.log(`[${name}] 缺少页面文件: ${missing.join(', ')}`); } else { console.log(`[${name}] 基本结构完整`); } });这段脚本先读每个子目录的app.json,如果连这个文件都没有,说明它不是独立小程序工程,跳过即可,例如纯组件演示目录或纯素材目录。然后遍历pages数组,把每个页面路径拼接成.js文件检查存在性,一旦缺失就打印出来。这类缺失常见原因是源码包解压时大小写错乱,或者页面路径在迁移后被改名却漏改 app.json。脚本跑完后,再配合微信开发者工具「详情-本地设置」开启 ES6 转 ES5,以及调试基础库版本切换到 2.x release 版本,大部分老项目都能正常启动。
打开后如果还有报错,按这条顺序排查:白屏优先看控制台有没有脚本报错,脚本报错优先看 API 版本;图片裂开看 network 面板里的请求域名,把「不校验合法域名」勾上即可临时解决;自定义导航栏错位就检查是否用了getMenuButtonBoundingClientRect动态算高度。
最后留一个这批源码里最容易忽略的小技巧:像芒果TV、B站首页这类仿制项目的navigationBarTitleText和分享卡片文案,在app.json的window与页面级 json 里都能改,改成自己的品牌名后记得保留navigationStyle的默认配置,否则胶囊按钮与自定义导航栏会重叠,下拉刷新也会失去原生加载动画。
本文还有配套的精品资源,点击获取