简介:一份社区交流微信小程序完整源码,配套PHP后台实现,适合微信小程序开发初学者、PHP后端工程师以及想要搭建轻量社区交流平台的产品/运营人员。资源包含103个文件,压缩包仅544KB,结构紧凑清晰。其中34个php文件承载后台接口与业务逻辑,16个js、12个wxml、13个wxss、15个json共同构成小程序前端界面与交互,另有3个sql数据库脚本、nginx配置示例等,覆盖从数据建表、服务端部署到前端展示的完整链路。已有58人学习下载。通过此源码可系统观摩社区发帖/回帖、用户信息管理等模块的前后端协作方式,学习PHP与小程序的数据交互、防SQL注入/XSS等安全处理,以及基于官方框架的组件用法和页面跳转实现,适合作为毕业设计或商业项目的基础模板二次开发。
1. 社区交流小程序源码:别急着造轮子,先看看这套前后端组合
做社区类小程序最烦的不是写界面,而是搭后台。你可能花两周把前端页面画得像模像样,结果一接接口就翻车:登录态搞不定、发帖数据存不进去、列表分页越翻越乱。这套「社区交流微信小程序源码 + 后台 PHP」的资源,恰恰是把最恼人的后半段给你补齐了。它不是那种只有一个 demo 页面的玩具源码,而是带 PHP 接口、带数据库表结构、能真跑起来的一套东西。适合三类人:想快速上线社区产品的开发者、接外包需要一套底子改改就交付的,以及想搞懂小程序和 PHP 后端怎么联调的学习者。如果你正卡在前后端联调或者不知道社区功能该从哪下手,这篇笔记能把它的目录结构、部署步骤和典型坑一次说清。
2. 拆开资源看门道:前端页面与 PHP 后台的分工
拿到源码包第一件事不是急着导入开发者工具,而是先看目录。这套资源大体分两块:小程序前端是一个完整的miniprogram工程,后台是一套 PHP 接口服务,再加上一份数据库初始化 SQL。先把三者对应关系理清楚,后面部署才不会对着文件发懵。
2.1 前端目录与页面职责
小程序端常见结构是pages下按功能分目录,社区类产品至少会有首页信息流、帖子详情、发布页、个人中心四个核心模块。比如首页的index页面负责帖子列表流,detail页面负责帖子正文与评论,publish页负责发帖表单,user页负责展示当前登录用户发布的内容。每个页面下面挂着index.wxml、index.js、index.wxss和index.json四个文件,这是微信小程序的固定格式,不用改。
我一般会先打开app.json看两样东西:一个是pages数组里的页面注册顺序,另一个是tabBar配置。社区类小程序通常底部有“首页”和“我的”两个 tab,如果 tabBar 里配置的图标路径不存在,小程序编译会直接报错。这个文件里还写着window全局样式,比如导航栏背景色和标题文字,你拿到资源后改项目名,主要就是改这个文件和project.config.json里的appid配置。
接口请求这块,小程序端一般会封装一个request工具函数,放在utils/request.js里。它做的事情很简单:用wx.request发请求,统一拼接baseURL,带上登录后的 token 请求头,收到返回后统一处理错误码。社区类小程序的接口风格通常是这样:返回code=0表示成功,data里才是真正的业务数据。你替换后台地址时只需要改这个文件里的BASE_URL常量,不需要每个页面单独改。
2.2 PHP 后台的路由与控制器结构
后台 PHP 代码这块,常见做法是走轻量 MVC 风格,入口文件index.php接收所有请求,通过pathinfo参数区分路由。比如请求/index.php?r=post/list&page=1,后台就去找controller/PostController.php里的actionList方法执行。这套结构在 PHP 5.6 到 PHP 8.x 都能跑,前提是你别直接用到被 PHP 7.0 移除的mysql_*函数族。
控制器层主要负责三件事:接收参数、校验参数、调用模型层取数据。拿帖子列表接口来说,PostController的actionList方法会先接收page和pageSize参数,把page转成整数并做下限保护(最少是 1),pageSize限制最大不能超过 50,防止有人一次性拉全表数据把数据库拖垮。参数校验通过后才去调PostModel::getList($page, $pageSize)查数据库。
模型层对应最底层的 SQL 操作,用的是 PDO 预处理语句。社区项目最常见的坑就在这里:如果直接用字符串拼接 SQL,用户传个id=1 or 1=1就能把你的帖子表整个拖出来。所以拿到的源码里如果看到prepare和bindParam这两个方法,说明作者处理过注入问题;如果看到拼接 SQL,你得自己改一遍。
数据库这块,初始化 SQL 文件里至少会有这几张表:用户表、帖子表、评论表、点赞关系表。帖子表的核心字段包括id、user_id、title、content、images(图片路径,多个用逗号分隔)、create_time。点赞表用user_id + post_id联合唯一索引来防止重复点赞,这个设计是社区项目的标准做法,你在二次开发时别把这个唯一索引删掉,否则刷赞根本拦不住。
3. 本地部署三步走:从数据库到开发者工具跑通
这套资源的部署路径不复杂,但每一步都有值得注意的参数细节。我把整理好的步骤拆成三段:先建库,再起 PHP 服务,最后在微信开发者工具里把前端跑起来。按这个顺序走,能少走不少回头路。
3.1 初始化数据库与配置文件
第一步是创建数据库。打开你的 MySQL 客户端,执行source命令导入源码包里的初始化 SQL 文件:
mysql -u root -p create database community charset utf8mb4; use community; source /path/to/community.sql;导入完成后,show tables;应该能看到至少四张表。这里强调utf8mb4字符集是有原因的:社区类应用必然有用户昵称和评论内容,如果有人发了一个 emoji 表情,utf8mb3(也就是常见的 utf8)会直接报错存不进去。项目里的数据库连接文件在后台的config/database.php,你需要把用户名、密码改成自己本机的:
<?php return [ 'host' => '127.0.0.1', 'port' => 3306, 'dbname' => 'community', 'user' => 'root', 'password' => 'your_password', 'charset' => 'utf8mb4', ];改完数据库配置,还要检查 PHP 环境是否装了 PDO 扩展。PHP 7 之后官方推荐用 PDO 访问数据库,如果你用的是phpStudy或宝塔这类集成环境,通常默认已经装好。不确定的话,在后台根目录放一个临时文件执行phpinfo();,搜一下pdo_mysql有没有出现在加载列表里。
提示:
source导入时如果报错,看一下 SQL 文件头部有没有CREATE DATABASE语句。如果有,你可以先直接创建库再导入,避免重复建库冲突。
3.2 启动 PHP 内置服务器做接口联调
本地开发阶段没必要非得配 Nginx。PHP 自带的开发服务器足够应付接口联调,在后台根目录打开终端直接起:
cd /path/to/php-backend php -S localhost:8080这样你的接口地址就变成了http://localhost:8080。浏览器访问http://localhost:8080/index.php?r=post/list,能看到 JSON 返回说明后端已经通了。用内置服务器只是为了联调方便,它单线程处理并发,真实上线时还是要换 Nginx 或 Apache 加 PHP-FPM 的组合,这一条你心里有数就行。
小程序端的BASE_URL这时改成http://localhost:8080/index.php。注意路径不要写错,有的前端代码里BASE_URL已经带了/index.php,有的没带,你看一眼utils/request.js里的拼接逻辑再决定。
3.3 微信开发者工具导入与域名校验关闭
后端跑起来之后,打开微信开发者工具,选择“导入项目”,目录指向小程序前端所在的文件夹。此时project.config.json里的appid还是作者的,你需要换成自己的小程序 AppID。没有注册小程序的话,开发者工具支持测试号模式,不影响本地联调,但部分 API 功能受限。
导入后先别急着编译,在开发者工具的“详情 - 本地设置”里,把“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”这个复选框勾上。因为本地联调用的是http://localhost,不勾这个,wx.request会直接报域名不合法而拒绝发送请求。这一步几乎是所有小程序新手必踩的坑。
然后点编译,如果首页能看到帖子列表数据,说明前端到后端的链路通了。前后端联调过程中,建议把开发者工具自带的调试器 Network 面板打开,看每个请求的状态码和返回内容。请求返回 500 就看 PHP 侧的错误日志,返回 404 就检查路由参数r是否拼写正确。
4. 核心功能实现:发帖、列表加载更多与交互细节
社区交流小程序的核心不是页面多漂亮,而是围绕帖子内容的读写体验。我挑三个典型功能展开,你会发现它们的实现思路是相通的:前端负责拿数据和渲染状态,后端负责查数据和返回固定结构。
4.1 帖子列表的加载更多:page/pageSize 与 onReachBottom
“加载更多”可以说是社区类小程序最高频的交互,热搜里也有人在搜“微信小程序页面列表加载更多”的做法。列表加载更多的标准实现是前端监听onReachBottom页面触底事件,触底后把page加 1,请求下一页数据,拿到后追加到当前列表数组后面。
小程序的onReachBottom是页面级别的事件,需要在页面的index.js里定义。下面这段代码是社区项目里很典型的写法:
Page({ data: { posts: [], page: 1, pageSize: 10, hasMore: true, loading: false, }, onLoad() { this.loadPosts(true); }, onReachBottom() { if (this.data.hasMore && !this.data.loading) { this.loadPosts(false); } }, loadPosts(reset) { const page = reset ? 1 : this.data.page + 1; if (this.data.loading) return; this.setData({ loading: true }); wx.request({ url: `${getApp().globalData.baseUrl}?r=post/list`, data: { page, pageSize: this.data.pageSize }, success: (res) => { const list = res.data.data.list; const hasMore = res.data.data.hasMore; this.setData({ posts: reset ? list : this.data.posts.concat(list), page, hasMore, loading: false, }); }, fail: () => { this.setData({ loading: false }); wx.showToast({ title: '网络异常', icon: 'none' }); }, }); }, });这里的关键逻辑在于reset参数:下拉刷新时调用loadPosts(true)把列表重置成第一页,触底加载时用loadPosts(false)追加数据。loading这个布尔值是防止重复请求的闸门,如果用户疯狂滑动触底,还没有loading的拦截,每触底一次就发一个请求,后台瞬间被请求击穿,前端列表也会出现错乱。hasMore由后端返回,当某页返回的数据条数小于pageSize时,说明没有更多数据,前端就该停止请求。
对应的 PHP 后端实现如下,控制器取出page和pageSize后,用LIMIT做分页,同时多查一条来判断是否还有下一页:
public function actionList() { $page = max(1, intval($_GET['page'] ?? 1)); $pageSize = min(50, max(1, intval($_GET['pageSize'] ?? 10))); $offset = ($page - 1) * $pageSize; $total = PostModel::getTotal(); $list = PostModel::getPageList($offset, $pageSize); $hasMore = $offset + count($list) < $total; $this->json(['list' => $list, 'hasMore' => $hasMore]); }注意$offset = ($page - 1) * $pageSize这个计算。前端page从 1 开始,数据库LIMIT的偏移量从 0 开始,所以必须做一次减一操作。很多前后端出来的分页 bug 根源就在这里:前端从 0 开始传,后端也从 0 开始算,结果第一页数据永远对不上。
4.2 发帖流程:表单提交与图片上传
发帖页面的核心是一个表单加一个图片选择器。文本内容用textarea收集,图片用wx.chooseMedia选择。图片上传是社区类小程序最容易出问题的部分,因为wx.uploadFile和wx.request是两套 API,请求头格式不同,后台接收方式也不同。
提交内容的流程拆成两段:先传图再传文本。图片先通过wx.uploadFile传入后台的文件上传接口,后台保存文件后返回一个 URL 路径;文本提交时带上这些图片路径的数组,后台存成逗号分隔的字符串。这样设计的好处是,如果用户写到一半退出,已上传的图片可以在下次进入时清理,避免垃圾文件堆积。
submitPost() { const content = this.data.content.trim(); const images = this.data.images; if (!content) { wx.showToast({ title: '内容不能为空', icon: 'none' }); return; } wx.request({ url: `${getApp().globalData.baseUrl}?r=post/create`, method: 'POST', data: { content, images: images.join(','), token: wx.getStorageSync('token'), }, success: (res) => { if (res.data.code === 0) { wx.navigateBack(); } else { wx.showToast({ title: res.data.msg, icon: 'none' }); } }, }); }后台接收时有个容易被忽略的安全点:不能直接把用户传的图片路径拼进images字段存库,要校验这些路径是不是本次会话上传的文件。常见做法是在文件上传成功后把生成的路径存入 session,提交帖子时逐条比对。如果资源包里没做这一步,建议你开发时自己补上,否则别人可以构造任意路径往你的帖子里插外链图片。
4.3 Token 登录态:小程序与 PHP 的会话保持
微信小程序的登录不是自己做账号体系,而是通过wx.login拿到临时code,由后台把这个code通过 HTTP 请求发到微信接口换取openid。拿到 openid 后,后台生成一个自定义的 token 返回给前端,前端存到wx的 storage 里,后续请求都带上这个 token 作为身份凭证。
PHP 侧生成 token 的常见做法是:
$token = md5($openid . time() . uniqid());生成后把 token 和 user_id 的映射关系存到数据库或者 Redis。社区类项目数据量不大时,存数据库更省事,比如建一张user_token表,字段就是token、user_id、expire_time。前端发起需要登录的操作时,后台先查 token 是否存在且未过期,再拿user_id做后续逻辑。
这个 token 机制很值得你花点时间吃透,因为社区项目的发帖、点赞、评论、删除操作全部依赖它判断“你是谁”。资源包里如果默认写死了某个测试用户的 id,上线前必须把这块改成真实的登录校验,否则所有帖子都会显示成同一个人发的。
5. 部署与联调的五个常见坑:现象、原因、解决办法
这一章把社区类小程序联调期间最常遇到的五个问题列出来,每条都是“现象 → 原因 → 解决”三段式。你复现的时候大概率会至少撞上其中一个。
5.1 接口通了但返回中文全是乱码
数据库里存进去的是正常中文,接口返回来在开发者工具里却变成了一堆\u转义或者???。这个现象一般出在 PHP 返回 JSON 时没有设置响应头,或者数据库连接字符集不对。PHP 默认情况下json_encode会把中文转成\uXXXX形式,这是合法的 JSON,但如果你发现页面上显示的是乱码而不是正常的文字,那大概率是数据库连接没指定utf8mb4。解决方法是数据库连接配置里确保charset是utf8mb4,同时 PHP 接口输出前执行header('Content-Type: application/json; charset=utf-8');。
5.2 wx.request 报 “url not in domain list”
开发工具里点编译,控制台直接报https://example.com/api/index.php is not in domain list。这个报错是微信小程序的域名白名单机制在拦截。解决分两步走:开发调试阶段,在开发者工具详情页勾选“不校验合法域名”;准备上线时,登录微信公众平台,在“开发管理 - 开发设置 - 服务器域名”里把后台域名加到request合法域名列表。注意这里有个硬性要求:线上后台域名必须是 HTTPS,而且备案过的域名才能通过。本地联调用 HTTP 没事,但真机预览体验版时,手机上的小程序同样会做域名校验,这时候你只能在手机上开启调试模式来绕过。
5.3 列表加载更多时数据重复或跳页
页面往下滑,之前加载过的帖子又出现了一遍,或者干脆跳过了一页。这个坑的根源基本都是前后端分页的起始值不一致。前端page从 1 开始,后端LIMIT偏移量算成page * pageSize,第一页正常,第二页就把第一页的最后一条重复拉出来了。解决方法是统一约定:前端page固定从 1 开始,后端按($page - 1) * $pageSize计算偏移量。排查时用调试工具看一眼请求参数,如果发现第二页传的page还是 1,那问题出在前端的setData逻辑,page变量没有在请求成功后更新。
5.4 onReachBottom 不触发
页面内容滚不到底,onReachBottom一直不执行。这个有两种情况:一是页面内容太少,撑不满一屏,此时不存在“触底”事件,属于正常现象;二是页面被一个固定高度的scroll-view包住了,导致页面本身不能滚动,onReachBottom永远不触发。解决方法是确认页面用的是原生页面滚动而不是scroll-view组件,或者改用scroll-view的bindscrolltolower事件来替代。
5.5 PHP 7.0 以上接口直接 500
后台部署到新环境后,所有接口都返回 500,错误日志里显示Call to undefined function mysql_connect()。这是老代码迁移到 PHP 7 的经典翻车现场。PHP 7.0 移除了一切mysql_*函数,只保留mysqli和 PDO。解决方法是把数据库操作全部改成 PDO,如果你不懂怎么改,最稳妥的办法是直接用资源包内的config/database.php和模型层代码,只要作者当初写的是 PDO,就不会有这个报错。如果作者写的是mysqli,改起来也简单,把mysql_connect换成mysqli_connect,再把参数顺序调一下就行。
6. 把分页和 Token 机制改成自己的:两个进阶技巧
到了最后这一章,不再讲基础功能,而是分享两个我拆这类源码时一定会动手改的地方。它们能让这套资源从“能跑”变成“像是自己写的代码”,也方便你接到真实需求时快速调整。
第一个技巧是给列表接口加上缓存。社区类小程序的首页数据是所有用户都看的同一份内容,不需要每次请求都查数据库。我一般会在 PHP 层用内存缓存存第一页的数据,设 30 秒过期。用file_put_contents写一个简单缓存文件就够了,不一定要引入 Redis:
$cacheFile = '/tmp/post_list_page1.json'; if (file_exists($cacheFile) && time() - filemtime($cacheFile) < 30) { echo file_get_contents($cacheFile); return; } // 查数据库,生成 $json 后写入缓存 file_put_contents($cacheFile, $json);这么做的好处是首页接口的响应时间能从几十毫秒降到几毫秒,哪怕是放在了 IO 密集的虚拟主机上,效果也立竿见影。缓存时间设 30 秒,发布新帖子后最多 30 秒内可见,对社区产品来说用户完全感知不到延迟。
第二个技巧是 token 过期处理。前端在请求返回码里约定一个特殊值比如code=401表示 token 失效,前端拿到后统一跳转到登录页,而不是让用户看到一个莫名其妙的报错。实现上在utils/request.js里加一层判断:
if (res.data.code === 401) { wx.removeStorageSync('token'); wx.reLaunch({ url: '/pages/login/login' }); return; }这样用户的操作路径就是:发帖 → 后台发现没登录 → 返回 401 → 前端自动跳登录页 → 登录后返回可继续操作。整个链路不用用户手动操作任何东西,体验上比弹窗提示“请先登录”要顺滑得多。我在一次接私活的项目里被要求在现有源码上从零加登录态,当时没做这个 401 统一处理,用户发帖时直接看到白屏报错,收集了一周试用反馈全是吐槽登录体验的。从那以后我每次拆源码,都强制先查utils/request.js里的 code 处理逻辑,不合理的先改掉再动其他功能。这套资源大概率不会覆盖你所有的二次开发场景,但把分页、登录、加载更多这几个社区核心链路吃透了,你就能看清楚一件事:源码不是用来背的,是用来改的。希望帮到你。
本文还有配套的精品资源,点击获取