☰
织梦CMS对接微信小程序:用JSON接口快速搭建内容型小程序
2026/10/6 8:18:58 网站建设 项目流程

简介:织梦微信小程序助手2.0专为织梦CMS用户打造,旨在降低微信小程序开发门槛。通过后台傻瓜式操作即可将网站新闻、产品等内容同步生成小程序,适合不懂编程的站长快速拥抱移动端,无论是个人站点还是企业官网都能低成本搭建。压缩包共5个文件,整体仅184KB,其中txt文档提供安装与更新说明,xml文件保存接口及模板配置,html页面可作为本地预览入口,结构精简、用途明确,便于快速上手。已有390人学习下载。借助该助手,用户无需编写复杂代码,即可完成内容同步、模板定制、一键生成代码包及后台管理,还可按需扩展会员、支付等电商功能,显著缩短开发周期,让传统织梦站点更顺畅地接入微信生态。同时提供数据分析与版本管理功能,方便运营者持续优化小程序体验。

1. 织梦微信小程序助手2.0:老站点做小程序,先看这条最短的路

织梦微信小程序助手2.0,一句话概括就是:在不重建站的前提下,把织梦CMS(DedeCMS)里的栏目和文章,通过一层JSON接口喂给微信小程序端,让小程序以原生页面展示内容。很多做企业站、资讯站、地方门户的站长手里都压着织梦老站,内容不少、权重也有,就是缺一个能跑的移动端入口;这套方案的价值,就是把这部分存量内容变成小程序的骨架,避免“为了一个小程序再搭一套后台”的重复投入。

适合谁:正在用织梦维护内容、又想低成本上线小程序的站长或外包开发者。不需要动织梦的模板,不需要换数据库,只要PHP环境正常,就能在现有的站点目录里加几个接口文件,再写一个小程序前端。下文会把这套方案从数据原理一直拆到参数配置和踩坑记录,尽量让新手照着也能跑通。

2. 数据怎么从织梦流进小程序:栏目树、文章表、JSON出口

2.1 织梦不是黑匣子:先看懂它是怎么存文章的

织梦装了之后,MySQL里会多出几十张表,日常使用中真正要关心的只有三张:dede_arctype是栏目表,靠id和reid两个字段维护父子层级,reid为0的是顶级栏目;dede_archives是文档主表,存标题、缩略图、发布时间、摘要和审核状态;dede_addonarticle是文章附加表,通过aid关联主表的id,正文那一大坨HTML就存在这张表的body字段里。

小程序端不认织梦的模板标签,也不该直接连数据库。常见的做法是在织梦站点里加一层PHP接口,用织梦自带的common.inc.php完成初始化,拿到$dsql数据库对象后,直接查这三张表,拼成JSON返回给小程序。织梦微信小程序助手2.0比1.x时代更强调的是这个“接口层”的价值:1.x经常用web-view套网页,体验和加载速度都一般;2.0的思路上来就做原生页面,用API喂数据,交互顺畅很多,也更容易过微信审核。

2.2 小程序端的真实诉求:不是整站搬家,是一个可浏览的内容API

有些站长一开始就想要“小程序里跟电脑站一模一样”,这其实是误解。微信小程序对包体积有2MB限制,页面栈有限,富文本渲染靠rich-text组件,它只认节点数组或HTML字符串,且不执行任何JavaScript。换句话说,把织梦整个站搬过去既不现实也没必要,用户在小程序里要的是:看到栏目、看到文章列表、点进去能读正文。

所以助手2.0这类方案真正要解决的问题只有四个:栏目怎么分、列表怎么分页、详情怎么取、正文里的图片怎么正常显示。这四个问题全部可以在接口层解决,小程序端只是拿到JSON后渲染而已。理解这一点,就不会被“小程序开发很复杂”的舆论吓住,也不会反方向去造一个和织梦并行的内容后台——那是得不偿失的做法。

2.3 自建API还是装插件:2.0时代的主流选型

市面上确实有一些织梦小程序插件,后台能配置栏目映射、自动生成API文档,用起来省事。但插件的问题在于:字段是写死的,想加一个“阅读量排序”或“按关键字搜索”就得等作者更新,二次开发的自由度很低。自建API则相反,代码量不大,但每个字段、每个查询都能自己控制,遇到问题也能直接改。

我对中小型资讯站的一贯建议是自建。原因很现实:织梦本身已经停止官方更新很久了,生态里的第三方插件质量参差不齐,装一个来路不明的插件,反而可能给老站带来安全风险。自建接口只需要PHP基础,动手写一遍之后,对织梦的数据结构也熟了,后续调整栏目、加字段、接搜索都不求人。这套思路也贯穿下面所有代码。

3. 自己写一套织梦JSON接口:分类、列表、详情三步跑通

3.1 先搭接口骨架:在织梦站点目录下新建独立接口目录

常见的做法是在织梦站点根目录下建一个weapp目录,专门放小程序接口文件。这样做有两个好处:一是和织梦后台文件分离,方便排查和备份;二是在配置伪静态规则或域名白名单时,可以直接针对这个目录放行,不影响主站。

第一步就是创建目录并放一个最简单的接口文件,验证织梦环境能正常初始化:

<?php // /weapp/ping.php —— 接口连通性测试 define('DEDEINC', str_replace("\\", '/', dirname(__FILE__)) . '/include'); require_once(DEDEINC . '/common.inc.php'); header('Content-Type: application/json; charset=utf-8'); echo json_encode([ 'code' => 0, 'msg' => 'weapp api ready' ], JSON_UNESCAPED_UNICODE);

这段代码做了两件事:通过DEDEINC常量定位织梦的include目录,再引入common.inc.php完成数据库连接和全局初始化。$dsql对象就是织梦的数据库操作入口,后面所有查询都用它。在浏览器里访问https://你的域名/weapp/ping.php,如果看到{"code":0,"msg":"weapp api ready"},就说明接口骨架已经通了。

这一步卡住的人多半是路径问题。有些织梦站是装在子目录里的,DEDEINC就要写成dirname(__FILE__) . '/子目录/include';另外PHP版本过高时common.inc.php里部分写法会报警告,建议在站点入口处把错误显示关掉,接口统一只输出JSON,避免调试信息污染前端解析。

3.2 列表接口:栏目过滤、分页、缩略图一次返回

列表接口是小程序首页和栏目页的数据来源,核心SQL从dede_archives主表取数,按时间倒序,支持typeid过滤。下面这段是完整的列表接口:

<?php // /weapp/list.php —— 文章列表接口 define('DEDEINC', str_replace("\\", '/', dirname(__FILE__)) . '/include'); require_once(DEDEINC . '/common.inc.php'); header('Content-Type: application/json; charset=utf-8'); $typeid = isset($_GET['typeid']) ? intval($_GET['typeid']) : 0; $page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1; $pagesize = 12; // 每页条数,资讯站建议10-12 $offset = ($page - 1) * $pagesize; // 按栏目过滤时,只查当前栏目;typeid=0表示全站最新 if ($typeid > 0) { $sql = "SELECT id, typeid, title, litpic, pubdate, description FROM `#@__archives` WHERE typeid = $typeid AND arcrank >= 0 ORDER BY id DESC LIMIT $offset, $pagesize"; } else { $sql = "SELECT id, typeid, title, litpic, pubdate, description FROM `#@__archives` WHERE arcrank >= 0 ORDER BY id DESC LIMIT $offset, $pagesize"; } $dsql->SetQuery($sql); $dsql->Execute(); $list = []; while ($row = $dsql->GetArray()) { $row['pubdate'] = date('Y-m-d', $row['pubdate']); $row['litpic'] = str_replace('{style}', '', $row['litpic']); // 清除织梦标签残留 $list[] = $row; } echo json_encode([ 'code' => 0, 'page' => $page, 'has_more' => count($list) === $pagesize, // 按是否取满一页判断是否有下一页 'list' => $list ], JSON_UNESCAPED_UNICODE);

这里的#@__是织梦的表前缀占位符,实际执行时会替换成配置里的前缀(常见是dede_)。arcrank >= 0是审核状态过滤:0表示正常发布,-1是待审核,-2是回收站,这个条件必须带上,否则小程序里会出现草稿和已删除内容。litpic字段偶尔会混入{style}这种模板残留值,用str_replace清理掉,避免小程序端图片链接带花括号。

分页判断用count($list) === $pagesize而不是再查一次总数,因为对小程序“上拉加载更多”的场景来说,只需要知道“还有没有下一页”,这个判断在数据量大时性能更好。如果一页刚好取满,前端就会认为可能还有下一页,再触发一次请求也不算错。

3.3 详情接口:join内容表,顺便把正文清洗干净

详情页的难点在于dede_addonarticle里的body字段是一整段HTML,里面有织梦自带的{dede:}标签、站内相对路径图片,甚至还有空行和样式残留。小程序rich-text组件可以直接渲染HTML字符串,但前提是这段HTML是干净的。详情接口在返回前必须做两件事:去掉织梦标签残留、把图片路径补成完整域名。

<?php // /weapp/detail.php —— 文章详情接口 define('DEDEINC', str_replace("\\", '/', dirname(__FILE__)) . '/include'); require_once(DEDEINC . '/common.inc.php'); header('Content-Type: application/json; charset=utf-8'); $id = isset($_GET['id']) ? intval($_GET['id']) : 0; if ($id <= 0) { http_response_code(400); echo json_encode(['code' => 1, 'msg' => 'id参数缺失']); exit; } $sql = "SELECT a.id, a.typeid, a.title, a.litpic, a.pubdate, a.description, b.body FROM `#@__addonarticle` b INNER JOIN `#@__archives` a ON a.id = b.aid WHERE a.id = $id AND a.arcrank >= 0 LIMIT 1"; $dsql->SetQuery($sql); $dsql->Execute(); $row = $dsql->GetArray(); if (!$row) { http_response_code(404); echo json_encode(['code' => 1, 'msg' => '文章不存在']); exit; } $row['pubdate'] = date('Y-m-d H:i', $row['pubdate']); // 清理织梦模板引擎残留标记 $row['body'] = preg_replace('/\{dede:.*?\}/is', '', $row['body']); // 把站内图片相对路径补全为https绝对路径,防止小程序里图片裂开 $row['body'] = preg_replace('/src="\/(uploads|images)\//i', 'src="https://你的域名/$1/', $row['body']); echo json_encode(['code' => 0, 'data' => $row], JSON_UNESCAPED_UNICODE);

这段代码里的JOIN是关键:织梦的文章内容不在主表里,只查dede_archives永远拿不到正文。b.aid = a.id是两张表的关联条件,如果站点启用了多图或独立模型,表名会变成dede_addonxxx,需要根据后台的模型名调整。preg_replace清洗正文时,正则\/\{dede:.*?\}/is覆盖了所有形如{dede:field.name/}的标签,这是织梦模板最常见的残留物。

图片路径补全这步不能省。织梦后台如果填的是/uploads/allimg/2401/xxx.jpg,小程序端拿到的就是相对路径,真机上会因无法解析而裂图。替换时注意域名前缀一定是https://,微信小程序不支持明文HTTP。如果你的站点用了七牛、OSS这类第三方存储,路径前缀又不一样,按实际情况改正则即可。

3.4 小程序端对接:request封装、下拉刷新、触底加载

接口就绪后,小程序端的对接反而是体力活。列表页的数据请求、分页累加、触底加载这三件事必须同时处理,否则就会出现“翻页后数据被清空”或“重复请求”的问题。下面是一个标准的列表页脚本:

// pages/list/list.js Page({ data: { typeid: 0, page: 1, articles: [], hasMore: true, loading: false }, onLoad(options) { // 栏目页传入typeid,首页默认全站 if (options.typeid) { this.setData({ typeid: Number(options.typeid) }); } this.loadArticles(true); }, loadArticles(reset = false) { if (this.data.loading) return; // 防止重复触发 const page = reset ? 1 : this.data.page + 1; this.setData({ loading: true }); wx.request({ url: 'https://你的域名/weapp/list.php', data: { typeid: this.data.typeid, page }, success: (res) => { const data = res.data; if (data.code === 0) { const articles = reset ? data.list : this.data.articles.concat(data.list); this.setData({ articles, page, hasMore: data.has_more, loading: false }); } }, fail: () => { this.setData({ loading: false }); wx.showToast({ title: '加载失败', icon: 'none' }); } }); }, onReachBottom() { if (this.data.hasMore) { this.loadArticles(false); } }, onPullDownRefresh() { this.loadArticles(true).then(() => wx.stopPullDownRefresh()); } });

这段代码的逻辑是:reset为true时从第一页重新拉数据,用于下拉刷新;触底时用当前页+1去拉下一页,成功后再concat追加。注意loading标志位必须加,否则用户快速滚动时会同时发出多个请求,列表顺序会错乱。

列表页的WXML渲染建议用wx:for配合wx:key="id",图片用mode="aspectFill"裁切,摘要字段直接展示description,它本身是从正文里截取的一段纯文本,适合做列表摘要。下面是列表项和详情页的渲染片段:

<!-- pages/list/list.wxml 列表项核心结构 --> <view class="article-item" wx:for="{{articles}}" wx:key="id" bindtap="goDetail">// 递归获取当前栏目及所有子栏目ID function get_child_typeids($dsql, $typeid) { $ids = [$typeid]; $dsql->SetQuery("SELECT id FROM `#@__arctype` WHERE reid = $typeid"); $dsql->Execute(); while ($row = $dsql->GetArray()) { $ids = array_merge($ids, get_child_typeids($dsql, $row['id'])); } return $ids; } // 使用时把SQL里的typeid条件替换为: $typeid_list = get_child_typeids($dsql, $typeid); $typeid_sql = implode(',', $typeid_list); // WHERE typeid IN ($typeid_sql) AND arcrank >= 0

递归层数要限制,我一般控制在三层以内,防止站点栏目结构太深导致查询变慢。如果栏目只有两级,直接用一条reid IN子查询也能替代,但递归写法对任意层级都通用,值得保留。

4.3 开启伪静态后,接口文件被规则吞掉

现象:织梦后台开启了伪静态(Apache的mod_rewrite或Nginx的try_files),小程序接口在电脑上打开正常,一到小程序里就报“request:fail url not in domain list”或直接404。

原因:伪静态规则通常会把所有不带真实文件名的请求全部rewrite到index.php,如果规则写得比较宽,/weapp/list.php这种以.php结尾的真实路径也会被拦截,导致接口返回织梦的404页面。更隐蔽的情况是Nginx的location ~ \.php$规则里没放行/weapp/目录,PHP请求被扔给了静态文件处理逻辑。

解决:在伪静态规则里把/weapp/目录单独放行。Apache的.htaccess在RewriteRule前加一条:

RewriteCond %{REQUEST_URI} !^/weapp/

Nginx则在server块里的PHP location前加入:

location ^~ /weapp/ { include fastcgi_params; fastcgi_pass unix:/tmp/php-cgi.sock; # 根据实际环境改 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }

判断是不是这个原因的方法很直接:用浏览器访问接口文件,加上一个不存在的参数,如果返回的是织梦的HTML错误页而不是JSON,说明请求根本没进PHP接口。此时直接在服务器上curl一下接口地址,看返回体就能确认。

4.4 接口时通时不通,原来是被session和登录态拖住

现象:小程序端调用列表接口,有时候几秒就返回,有时候卡到超时;偶尔还会出现不同用户看到不同数据的情况。

原因:织梦的common.inc.php初始化时会启动会话机制并加载会员状态,如果站点启用了会员功能,接口每次执行都可能触发session_start()和用户表查询。小程序端在并发请求时,同一时间多个页面请求接口,PHP的session文件锁会让请求排队,表现就是时通时不通。另外如果接口文件放在织梦后台能访问的目录下,还可能被后台的登录校验逻辑拦截。

解决:这套方案里接口只做内容读取,不需要用户登录态,所以我在接口文件里在引入common.inc.php之前先声明不启动session,同时把接口放到独立子域名,比如api.你的域名.com,只允许通过微信小程序访问。如果不想上子域名,也可以在Nginx层面对/weapp/目录单独配置一个不带session的PHP-FPM池,但这样成本略高。实际中最快的缓解手段是接口文件顶部加上:

if (session_status() === PHP_SESSION_ACTIVE) { session_write_close(); // 释放会话锁 }

这个改动对性能提升立竿见影,微信小程序请求默认不带Cookie,这些接口本身就“不认识”用户,干脆跳过所有会员态才是正确的姿态。血泪经验是:不要为了省事把织梦后台的管理员session校验逻辑复制到接口里,否则小程序审核时容易被判定为“强制登录才能使用”。

4.5 微信审核被拒:先检查这三样,别急着申诉

现象:小程序提交审核后,被微信以“页面内容为空”或“功能不完整”为由驳回;有些资讯站还被提示“涉及信息发布但无相关资质”。

原因:审核被拒的原因绝大多数不在代码,而在内容。列表页如果默认typeid=0且站点尚没有文章,审核员打开就是空页面;详情页如果rich-text渲染失败,也会被当成“功能不完整”。更麻烦的是,织梦老站经常有一些分类叫“行业资讯”“合作伙伴”,审核员点进去看到一堆空白或重复内容,就会被判定为低质。

解决:提交审核之前,用游客视角把小程序完整走一遍:从首页列表点进详情,正文图片必须显示完整,底部不能有“联系客服加微信”这类诱导性按钮;栏目页必须有至少五条真实内容;有搜索功能的,搜索空结果要展示友好提示。如果站点内容偏少,建议先只在后台发布十篇以上高质量文章再提审。审核被拒后不要急着申诉,先按驳回理由把空页面补上内容,再重新提审,成功率更高。

5. 织梦助手2.0必调参数:分页、图片、缓存与超时的推荐值

5.1 织梦端:SQL查询、栏目递归、图片尺寸

接口性能的瓶颈几乎都在织梦端的SQL查询上。列表接口的分页数不建议超过15条,移动端屏幕一次展示不了太多,拉太长反而增加小程序内存压力。详情接口的JOIN查询很轻,但要注意LIMIT 1必须写,防止同一个ID存在多条附加记录时查出重复数据。栏目递归层数上面提过,控制在三层以内,如果站点很深,改成在织梦后台把栏目结构压平,对维护也是好事。

图片尺寸这块,织梦后台的缩略图默认是按原图尺寸生成的,很多老站直接上传了3MB的大图,小程序列表页加载起来非常慢。常见做法是在列表接口里对litpic做处理,或者在上传时就在织梦后台的“系统基本参数→图片设置”里把缩略图宽度设成750px。750px正好是iPhone的物理像素宽度,列表页用2倍屏显示也不会模糊,高度按4:3裁切,UI上比较协调。

5.2 微信公众平台:request合法域名、业务域名、HTTPS

这一步参数配错,小程序开发者工具里调试没问题,真机上却会被挡住。在微信公众平台后台,“开发管理→开发设置→服务器域名”里要配置request合法域名,填入接口所在的域名,必须是HTTPS且已备案。如果你的接口域名和网页里用到的域名不一致,还要在“业务域名”里一并加上,否则rich-text里偶尔出现的网页跳转会被拦截。

HTTPS证书别贪便宜,小程序对证书链的要求很严,有些免费证书在Android低版本机型上会握手失败。我一般直接用云厂商的一年期证书,配合Nginx配置HTTP/2,接口请求的耗时能显著下降。域名数量也是坑:request合法域名最多能配20个,如果你既有主站的图片域名、又有API子域名、还有CDN域名,数一数很容易超,提前规划好。

5.3 小程序端:请求超时、缓存策略、token时效

小程序端的wx.request默认超时是60秒,对移动网络来说太长。推荐设置成10秒,配合失败重试一次的策略,用户体验最稳。列表数据建议缓存到本地缓存Storage,我在书籍类、资讯类小程序里一般用wx.setStorageSync做一个“先展示缓存、再静默拉新”的策略,用户第二次打开首页时不用白屏等接口。

token时效这个参数只在你后续接入评论、点赞这类需要用户身份的功能时才需要。微信小程序没有传统意义上的session,常见做法是用wx.login拿code,在后端换成openid,再签发一个有效期2小时的token存在Storage里,过期后静默重新登录即可。不建议为了省事把token有效期设成30天,一旦泄露就有刷接口的风险。

参数项推荐值说明
列表分页数量10-12条/页移动端一屏半高度,加载体验最好
列表缓存TTL600秒织梦端文件缓存或Redis,缓解MySQL压力
接口请求超时10秒wx.request的timeout设置
token有效期2小时需要用户态时使用,过期静默刷新
详情正文缓存24小时文章不常改,详情接口可做页面级缓存
缩略图宽度750px适配2倍屏,列表页不乱码

缓存策略上,织梦端我一般用文件缓存,不用Redis,因为老站大多是虚拟主机,装不了Redis插件。一个简单做法是在/weapp/cache/目录下写JSON文件,接口运行时先检查缓存文件是否在TTL内,在就直接读文件,不在才查数据库。注意缓存目录要加权限保护,避免被Web访问到。

6. 进阶用法:把资讯站做成“书架式”小程序,离线也能读

如果你维护的是类似“织梦图书馆”这种以书目、文档、文章摘录为主的内容站,列表页做成简单的feed流有点浪费。书架式布局更适合这类站点:每个栏目像一格书架,书架里摆着文档封面和标题,用户点进去直接读全文。实现起来不复杂,列表接口已经返回了litpic和title,小程序端只需要把数据重新分组:

// pages/shelf/shelf.js const groupByType = (articles) => { const map = {}; articles.forEach(item => { const key = item.typeid; if (!map[key]) map[key] = []; map[key].push(item); }); return Object.keys(map).map(key => ({ typeid: Number(key), list: map[key] })); };

离线阅读是这类内容站最能提升好评的功能。织梦的文章更新频率不高,完全可以把已读过的详情页正文缓存到本地,下次无网络时直接读取。做法是在详情页请求成功后写入缓存,onLoad时先读缓存再发请求:

const detail = wx.getStorageSync('article_' + id); if (detail) { this.setData({ body: detail.body }); } wx.request({ /* 请求详情接口,成功后更新缓存 */ });

我第一版做小程序时没做缓存,用户每次打开详情都要转圈等接口,在弱网环境几乎不可用;后来花了半天补上这层,用户反馈明显改善。现在我对所有织梦类小程序都默认要求“列表可缓存、详情可离线”,这个习惯直接提升了留存率。书架式的视觉配合离线缓存,用户甚至会以为你专门做了一个App——其实后端还是那套织梦,接口还是那两个PHP文件。希望这个方案能帮你把手里的织梦老站盘活,少走几步弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询