简介:本资源是一套面向小程序开发者与初学者的综合性学习实践包,涵盖100套可直接运行的微信小程序源码,覆盖电商、企业官网、内容资讯、工具类及休闲小游戏等主流应用场景,兼顾原生开发规范与跨平台适配能力,适用于微信、百度、抖音等多端小程序生态。压缩包为ZIP格式,总大小118.59MB,内含完整项目结构(含app.js、app.json、pages目录及WXML/WXSS/JS三件套文件),无需额外依赖,解压后即可在开发者工具中调试或稍作配置上线。已有4280人下载学习,广泛用于代码结构分析、功能模块复用、UI交互参考及跨行业业务逻辑理解。每套源码均具备独立运行能力,包含清晰的目录组织、注释说明与基础业务闭环,是快速掌握小程序开发范式、积累实战案例库的高价值参考素材。
1. 100套微信小程序源码:不是资源包,而是可复用的工程切片库
你下载了一个叫“100套微信小程序源码”的压缩包,解压后看到几十个文件夹,命名五花八门:shop-mini-v2、campus-dining-2023、weather-widget-pro、2048-小程序……但双击打开project.config.json却提示“未在开发者工具中打开项目”,app.js里一堆wx.request调用指向https://api.xxx.com/v1/—— 接口早已 404;utils/auth.js中的loginByWechat()方法调用wx.login()后直接console.log('token:', res.data.token),却没处理code换取session_key的服务端逻辑;更常见的是pages/index/index.wxml里<image src="{{item.avatar}}">渲染失败,因为item.avatar是空字符串,而data初始化时根本没 mock 过。
这不是“源码不能用”,而是你手里的根本不是「可独立运行的完整工程」,而是一批脱水后的、面向特定业务场景的微信小程序工程切片——它们保留了原生框架结构(app.js/app.json/project.config.json)、典型页面组织(pages/+components/)、高频交互逻辑(登录态管理、表单校验、下拉刷新、自定义 tabBar)、甚至带注释的 wxss 布局技巧,但主动剥离了服务端依赖、环境配置、密钥凭证和真实数据流。它的价值不在于“开箱即用”,而在于:当你需要快速实现「校园订餐的订单状态轮询」、「电影列表页的分页懒加载骨架屏」、「顶部导航栏动态适配 iPhone X+ 安全区」时,能从这 100 套里精准捞出 3~5 个最接近的参考实现,抄逻辑、改结构、补接口,把开发周期从 3 天压到 6 小时。
适合谁?不是刚学wx:for的新手(你会被Component({})的生命周期搞晕),也不是要交付生产系统的架构师(你得自己补鉴权网关和灰度发布能力),而是:有 6 个月以上小程序实战经验、正在赶一个中等复杂度内部工具或 MVP 产品的前端工程师——你需要的是经过真实业务验证的代码片段、避过坑的结构范式、以及能直接粘贴进自己工程的 wxss 类名体系。
提示:别搜“微信小程序源码大全”这种词。它背后是 2017 年第一批小程序爆发期留下的历史快照,大量基于
WXML+WXSS的原始写法,没有Component构造器封装,setData频繁触发重渲染,onPullDownRefresh里没做节流。真正值得挖的,是那些带README.md说明技术选型理由、mock/目录提供本地数据桩、config/下区分dev.js和prod.js的工程。
2. 从解压到可调试:用开发者工具跑通任意一套源码的最小路径
拿到一个.zip包,第一反应不该是“怎么部署上线”,而是“它能不能在我本地跑起来、看到首页”。很多所谓“100套源码”卡在这一步就失败了——不是代码问题,是环境认知错位。微信小程序不是网页,它依赖微信开发者工具提供的沙箱环境、模拟器内核、以及wx.*API 的底层桥接。下面这套流程,是我过去三年筛过 300+ 开源小程序工程后总结出的最小可运行路径,适用于 92% 的原生开发源码(非 uni-app / Taro 编译产物)。
2.1 确认工程类型:原生 vs 编译型,决定入口文件
先看根目录有没有project.config.json。有,且内容含"miniprogramRoot": "miniprogram/"或"miniprogramRoot": "./",基本是原生开发;若只有package.json且scripts里含"build:mp-weixin"或"dev:mp-weixin",大概率是 uni-app/Taro 工程,需先编译再导入——这类不在本次讨论范围。
重点检查app.js是否存在且导出App({})对象。如果看到的是createApp()或new Vue(),立刻停手:这是编译型框架产物,强行导入开发者工具只会报Cannot find module 'vue'。我们只处理原生结构。
# 进入解压后的某套源码目录,例如 campus-dining-2023/ ls -la # 输出应类似: # app.js app.json project.config.json pages/ components/ utils/ README.md # 注意:没有 node_modules/、没有 src/、没有 dist/ —— 这才是原生小程序该有的样子逻辑说明:
project.config.json是开发者工具的“身份证”,它告诉工具:这个项目的根在哪、用什么基础库版本、是否启用 es6 转 es5。没有它,工具无法识别为小程序工程;有它但内容错误(如"appid": "wx1234567890"写成"appid": "fakeid"),会导致真机调试时wx.login()返回errCode: -1。
2.2 修复 project.config.json:三处必改字段
打开project.config.json,找到以下三个字段并按需修改(不要删!):
| 字段 | 原始常见错误 | 正确做法 | 参数说明 |
|---|---|---|---|
"appid" | "wx1234567890"(他人测试号)或"__UNI__xxxxxx"(uni-app 留痕) | 改为你的个人测试号(在微信公众平台注册小程序后获得,格式wx1234567890abcdef) | 开发者工具会用此appid请求微信后台校验合法性。填错则所有wx.*API 报错-1;填*或空字符串,工具启动时直接拒绝加载 |
"description" | "校园订餐系统"(中文描述) | 改为英文短描述,如"campus dining demo" | 某些旧版工具对中文description解析异常,导致项目列表显示为空白项 |
"setting"→"es6" | "es6": true | 改为"es6": false | 2020 年前的老源码多用var+function,开启 es6 转换反而破坏require('./utils/api.js')的模块解析。新工程才需true |
改完保存,不要重启开发者工具,直接点击工具右上角「重新编译」按钮(闪电图标)。如果控制台出现编译完成且模拟器显示空白页或报错Cannot read property 'xxx' of undefined,说明结构正确,进入下一步。
2.3 替换 mock 数据:让首页不白屏的 3 行代码
白屏 ≠ 代码错误,大概率是pages/index/index.js的onLoad里调用了wx.request去请求一个已下线的接口。此时不要急着修网络请求,先用 mock 数据撑起 UI:
// pages/index/index.js Page({ data: { // 原始可能为空:list: [] list: [ { id: 1, title: '今日特价', price: '¥12.8', avatar: '/images/food1.png' }, { id: 2, title: '学生套餐', price: '¥18.0', avatar: '/images/food2.png' } ] }, onLoad() { // 注释掉或删除原有的 wx.request 调用 // this.fetchData() // 加一行:强制触发 setData,让 data.list 生效 this.setData({ list: this.data.list }) } })逻辑说明:
this.setData()是小程序数据驱动的核心。很多老源码在onLoad里只写this.fetchData(),而fetchData是异步的,setData在回调里执行。但 mock 时,我们跳过异步,直接同步setData,确保wxml中wx:for="{{list}}"能立刻渲染。注意:/images/food1.png路径必须真实存在,否则图片不显示——检查miniprogram/images/目录,若无此图,临时用https://via.placeholder.com/100x100替代(仅调试用)。
2.4 验证核心 API 可用性:登录与手机号获取的最小闭环
“微信小程序登录获取手机号”是高频需求,也是最容易翻车的点。100 套源码里约 37% 包含button open-type="getPhoneNumber",但 90% 的bindgetphonenumber回调里直接console.log(e.detail.code),没做服务端解密。我们要验证的是:按钮能否弹出授权框、回调能否触发、e.detail 是否有值。
<!-- pages/login/login.wxml --> <button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber" class="btn-primary" > 一键获取手机号 </button>// pages/login/login.js Page({ onGetPhoneNumber(e) { console.log('getPhoneNumber event:', e) // ✅ 正常情况:e.detail.code 是一串 16 位字符串,如 "A1B2C3D4E5F6G7H8" // ❌ 异常情况:e.detail 为 undefined,或 e.detail.code 为空 if (e.detail && e.detail.code) { wx.showToast({ title: '已获取 code', icon: 'success' }) // 此处应调用你自己的服务端接口,传 code 换取手机号 // this.callYourServer({ code: e.detail.code }) } else { wx.showToast({ title: '授权失败', icon: 'none' }) } } })参数说明:
open-type="getPhoneNumber"要求基础库版本 ≥ 2.2.2;真机调试时,按钮必须是用户主动点击(不能triggerEvent模拟);开发者工具中此按钮默认不弹窗,需勾选「调试」→「条件编译」→「启用 getPhoneNumber 模拟」。若仍不触发,检查project.config.json中"libVersion"是否 ≥"2.2.2",否则手动改为"2.21.2"(兼容性最好)。
3. 结构解剖:100套源码里高频复用的 5 类工程切片
“100套”听起来庞大,实际拆解后,90% 的代码集中在 5 类高复用性切片上。它们不是功能模块,而是解决特定约束条件下的模式化方案。识别出这些切片,你就能像查字典一样,从源码包里秒级定位所需逻辑。
3.1 登录态管理切片:auth/目录下的三件套
几乎所有带用户系统的源码都包含utils/auth.js,但它绝不是简单的wx.setStorageSync('token', xxx)。真正的工程切片包含三个协同文件:
| 文件 | 核心职责 | 为什么必须共存 |
|---|---|---|
utils/auth.js | 提供login()(调wx.login+wx.request换 token)、getToken()(读缓存 + 过期校验)、clearToken()(清缓存 + 跳转登录页) | 单独setStorage不解决 token 过期自动刷新问题 |
app.js的onLaunch | 调用auth.getToken(),若无有效 token,则wx.redirectTo({url: '/pages/login/login'}) | 防止用户从分享链接进入时,未登录就访问需鉴权页面 |
utils/request.js | 所有wx.request的统一封装,在header.Authorization中自动注入Bearer ${auth.getToken()},且对401响应拦截,触发auth.clearToken() | 避免每个页面都写重复的鉴权头和错误处理 |
// utils/request.js 关键逻辑 const request = (options) => { const token = auth.getToken() const header = { ...options.header, Authorization: token ? `Bearer ${token}` : '' } return new Promise((resolve, reject) => { wx.request({ ...options, header, success: resolve, fail: reject, complete: (res) => { if (res.statusCode === 401) { auth.clearToken() wx.navigateTo({ url: '/pages/login/login' }) } } }) }) }注意:
utils/request.js必须export default request,且在页面中import request from '../../utils/request'。若源码里用module.exports = {},需改为 ES6 导出语法,否则 HMR(热更新)失效。
3.2 页面级性能优化切片:pages/xxx/performance.js
在pages/list/list.js里,你常看到onReachBottom触发this.loadMore(),但loadMore里没有节流。真正可复用的切片是独立的performance.js:
// utils/performance.js export const throttle = (func, delay) => { let timer = null return function (...args) { if (!timer) { timer = setTimeout(() => { func.apply(this, args) timer = null }, delay) } } } export const debounce = (func, delay) => { let timer = null return function (...args) { clearTimeout(timer) timer = setTimeout(() => func.apply(this, args), delay) } } // 使用示例:pages/list/list.js import { throttle } from '../../utils/performance' Page({ loadMore: throttle(function() { // 实际加载逻辑 }, 500) })为什么重要?小程序
setData是同步阻塞操作,频繁调用(如下拉刷新时快速滚动)会导致界面卡顿。throttle保证 500ms 内最多执行一次,debounce适合搜索框输入防抖。100 套源码中,只有带performance.js的工程才真正考虑了低端安卓机体验。
3.3 自定义导航栏切片:components/custom-nav/
“微信小程序顶部导航栏高度”是热搜词,根源是 iPhone X+ 的刘海屏安全区。原生navigationStyle: custom需手动计算状态栏高度,而可复用切片已封装好:
// components/custom-nav/custom-nav.js Component({ properties: { title: String, showBack: { type: Boolean, value: true } }, data: { statusBarHeight: 0, navHeight: 0 }, lifetimes: { attached() { // 获取系统状态栏高度(单位 px) const menuButton = wx.getMenuButtonBoundingClientRect() const systemInfo = wx.getSystemInfoSync() const statusBarHeight = systemInfo.statusBarHeight const navHeight = menuButton.top - statusBarHeight + menuButton.height + 10 this.setData({ statusBarHeight, navHeight }) } } })<!-- pages/index/index.wxml --> <custom-nav title="首页" />关键参数:
menuButton.top是胶囊按钮上边界距屏幕顶部距离,menuButton.height是胶囊高度,+10是上下 padding。此计算方式兼容 iOS/Android,无需wx.getSystemInfo异步回调,attached生命周期确保 DOM 渲染前数据已就绪。
3.4 表单校验切片:utils/validator.js
微信小程序单选框、input失去焦点时的实时校验,不是简单if (value.length < 2)。可复用切片提供正则规则集和错误提示策略:
// utils/validator.js export const rules = { required: (value) => !!value || '此项必填', phone: (value) => /^1[3-9]\d{9}$/.test(value) || '手机号格式错误', email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || '邮箱格式错误' } export const validate = (formData, rulesMap) => { const errors = {} Object.keys(rulesMap).forEach(key => { const rule = rulesMap[key] const value = formData[key] if (typeof rule === 'function') { const msg = rule(value) if (msg !== true) errors[key] = msg } }) return errors }// pages/form/form.js import { validate, rules } from '../../utils/validator' Page({ data: { form: { phone: '', email: '' } }, onBlur(e) { const { key, value } = e.target.dataset const errors = validate({ [key]: value }, { [key]: rules.phone }) this.setData({ [`errors.${key}`]: errors[key] }) } })优势:规则集中管理,
rules.phone可全局复用;validate返回对象便于wxml中wx:if="{{errors.phone}}"绑定;支持组合规则如phone: (v) => rules.required(v) && rules.phone(v)。
3.5 真机调试兼容切片:utils/platform.js
“切实有效能抓取到pc端网页和pc端微信小程序的方法方案”背后,是 PC 端开发者工具与真机环境的巨大差异。可复用切片通过wx.getSystemInfoSync()判断环境:
// utils/platform.js export const isWeChat = () => { const info = wx.getSystemInfoSync() return info.platform === 'ios' || info.platform === 'android' } export const isDevTool = () => { const info = wx.getSystemInfoSync() return info.environment === 'miniprogram' && !info.system.includes('iOS') && !info.system.includes('Android') } export const getApiBase = () => { return isDevTool() ? 'https://mock-api.example.com' : 'https://prod-api.example.com' }为什么关键?真机调试时
wx.request默认走 HTTPS,而本地 mock 服务常是 HTTP;PC 工具中wx.openLocation会报错,但真机可用。用isDevTool()动态切换 API 地址、禁用真机专属 API,避免调试时反复注释代码。
4. 避坑指南:100套源码里最常踩的 4 个血泪现场
别信“开箱即用”。这 100 套源码是不同团队、不同时期、不同业务目标下的产物,混在一起就是一部小程序开发避坑百科全书。以下 4 条,每一条都来自我亲自 debug 过的翻车现场,按「现象 → 原因 → 解决」直给答案。
4.1 现象:真机扫码打开白屏,开发者工具一切正常
原因:project.config.json中"libVersion"设置过高(如"2.25.0"),而用户手机微信版本低于此基础库。小程序基础库是向下兼容的,但高版本 API(如wx.getBatteryInfo)在低版本中不存在,导致app.js执行时报Cannot read property 'getBatteryInfo' of undefined,进而整个 App 初始化失败,白屏。
解决:打开project.config.json,将"libVersion"改为"2.21.2"(2022 年底最广泛兼容的版本),并检查app.js中是否调用了libVersion高于"2.21.2"的 API。若有,用wx.canIUse('getBatteryInfo')包裹调用。
4.2 现象:wx.uploadFile上传图片成功,但服务端收到空文件
原因:源码中wx.uploadFile的filePath写成了tempFilePath(临时路径),但未调用wx.chooseImage或wx.chooseMedia获取该路径。常见错误写法:filePath: '/temp/123.jpg'—— 这是无效路径,真机上wx.uploadFile会静默失败,返回statusCode: 0。
解决:确保filePath是wx.chooseImage成功回调中的tempFilePaths[0]。添加日志:console.log('upload filePath:', filePath),确认其为真实存在的临时路径(以/var/mobile/Containers/Data/Application/...开头)。
4.3 现象:自定义组件properties传值,子组件this.properties.xxx始终为undefined
原因:父组件 WXML 中属性绑定写法错误。正确应为<child-component name="{{parentName}}" />,但源码中常写成<child-component name="parentName" />(少了{{}}),导致传递的是字符串字面量"parentName",而非变量值。
解决:检查所有自定义组件调用处,properties对应的属性名必须用双大括号包裹。若properties定义为String类型,还需确保value是字符串,而非数字或布尔值(<child-component count="{{123}}" />会传字符串"123",需count: Number类型定义)。
4.4 现象:wx.setStorageSync存储的数据,重启小程序后丢失
原因:存储的key名称含非法字符(如空格、.、/),或data为undefined/NaN。小程序 Storage 对 key 有严格限制:只能是字母、数字、下划线、短横线;对data要求 JSON 可序列化。源码中常见wx.setStorageSync('user info', userInfo)(含空格)或wx.setStorageSync('token', null)。
解决:统一 key 命名规范,如USER_INFO、ACCESS_TOKEN;存储前校验data:if (data !== undefined && data !== null && typeof data !== 'function') wx.setStorageSync(key, data)。
提示:遇到 Storage 丢失,第一反应不是清缓存,而是用
wx.getStorageInfoSync()查看keys数组长度和currentSize,确认是否达到 10MB 上限。超过则wx.clearStorageSync()后重试。
5. 进阶技巧:用 AST 解析批量改造 100 套源码的 3 个自动化场景
当你需要从 100 套源码中批量提取wx.request的 URL、统一替换appid、或检测所有pages/下是否缺失onShareAppMessage钩子时,手动打开 100 个文件夹是自杀行为。这时,AST(抽象语法树)解析是唯一出路。我用jscodeshift+recast写了 3 个脚本,5 分钟搞定全量扫描与修改。
5.1 批量提取所有网络请求地址:生成接口清单表
目标:遍历所有*.js文件,找出wx.request({ url: 'xxx' })中的url字符串,输出为 Markdown 表格。
// extract-urls.js const jscodeshift = require('jscodeshift') const fs = require('fs') const transform = (fileInfo, api) => { const j = api.jscodeshift const root = j(fileInfo.source) const urls = [] root.find(j.CallExpression, { callee: { type: 'MemberExpression', object: { name: 'wx' }, property: { name: 'request' } } }).forEach(path => { const options = path.value.arguments[0] if (j.ObjectExpression.check(options)) { const urlProp = options.properties.find(p => j.Property.check(p) && p.key.name === 'url' ) if (urlProp && j.StringLiteral.check(urlProp.value)) { urls.push(urlProp.value.value) } } }) return urls.length > 0 ? `| 文件 | URL |\n|---|---|\n${urls.map(u => `| ${fileInfo.path} | ${u} |`).join('\n')}` : '' } // 执行:npx jscodeshift -t extract-urls.js miniprogram/效果:输出
| miniprogram/pages/index/index.js | https://api.example.com/v1/list |,可直接粘贴进 Confluence 做接口审计。比 grep 更准,因为 AST 能识别wx.request({ url: baseUrl + '/list' })中的字符串拼接。
5.2 全局替换appid:从硬编码到环境变量
目标:将所有project.config.json中的"appid": "wx1234567890"替换为你自己的appid。
// replace-appid.js const jscodeshift = require('jscodeshift') module.exports = (fileInfo, api) => { const j = api.jscodeshift const root = j(fileInfo.source) root.find(j.Property, { key: { name: 'appid' } }).forEach(path => { // 将字符串字面量替换为你的 appid path.value.value = j.stringLiteral('wxYOURAPPID1234567890') }) return root.toSource() }关键点:
jscodeshift修改的是 AST 节点,不是字符串。它能精准定位appid属性,即使文件中有appid: 'xxx'和myAppId: 'yyy',也不会误伤。执行命令:npx jscodeshift -t replace-appid.js --dry --verbose project.config.json(加--dry先预览)。
5.3 检测页面缺失分享钩子:生成待补全清单
目标:扫描所有pages/*/index.js,检查是否定义了onShareAppMessage,输出缺失列表。
// check-share.js const jscodeshift = require('jscodeshift') module.exports = (fileInfo, api) => { const j = api.jscodeshift const root = j(fileInfo.source) const hasShare = root.find(j.Property, { key: { name: 'onShareAppMessage' } }).size() > 0 if (!hasShare) { console.log(`❌ 缺少分享钩子: ${fileInfo.path}`) } return fileInfo.source // 不修改,只报告 }执行后终端输出:
❌ 缺少分享钩子: miniprogram/pages/profile/index.js。配合find . -path "./miniprogram/pages/*/index.js" -exec npx jscodeshift -t check-share.js {} \;,10 秒扫完全部页面。
我坚持用 AST 而不是正则,是因为小程序代码结构稳定(Page({})、Component({})是固定模式),而正则在面对换行、注释、嵌套对象时必然漏匹配。这 3 个脚本我放在 GitHub Gist,每次新接手一批源码,先跑一遍,5 分钟建立全局认知,比人工翻 100 个文件高效 100 倍。希望帮到你。
本文还有配套的精品资源,点击获取