资讯详情

帝国CMS微信分享卡片修复:JS-SDK签名、缓存与模板兜底

📅 2026/11/5 6:03:24 | 华诺云谱 👁 阅读
帝国CMS微信分享卡片修复:JS-SDK签名、缓存与模板兜底
简介面向帝国CMS 7.5 UTF8站点运营者与开发者的微信分享增强源码包重点解决文章转发到朋友圈、发送给好友时缺少缩略图与简介的问题适用于已认证订阅号或服务号、域名完成备案且PHP空间支持HTTPS抓取的建站场景。包内共6个文件约31KB以2个PHP接口文件、2个TXT说明与参数备注、1个JPG配置示意图和1个微信JS-SDK脚本组成覆盖公众号凭证配置、分享接口调用及HTTPS环境下的脚本本地化等环节。已有1040人学习。其价值在于把认证公众号的appid、appsecret、JS接口安全域名和IP白名单等关键配置集中到可直接部署的目录中并附access_token获取检测思路便于快速判断配置是否生效对需要排查分享卡片不显示、缩略图缺失或HTTPS抓取异常的中级建站人员较有参考意义GBK版本需自行转码。1. 分享卡片只有一个灰方块这份源码补的是哪块短板把帝国CMS 的文章链接丢进微信对话框卡片上只有个灰底方块标题后面挂着栏目名简介那一栏空着——这个场景我见过太多次。站点里 titlepic 明明传了封面正文开头也写了摘要页面 head 里 og:image、description 一个不落微信就是不认。原因不复杂微信分享走的是 JS-SDK页面标签它压根不读它要的是当前页 URL 的一份签名再用 updateShareData 把缩略图、简介、链接塞进去。这份源码做的就是把这根线接通帝国CMS 侧加一个签名接口和两级缓存内容页模板把 titlepic、smalltext 取出来输出成一段 JSON前端统一挂 wx.config。它适合还在维护帝国CMS 老站、后台模型和模板都不方便大改、只想让转发卡片正常显示缩略图和简介的人。签名怎么算、缓存怎么存、模板里字段怎么兜底这套思路换到别的 CMS 上也一样能用。2. 链路拆解一次转发要过模板、接口、签名三道手微信分享这条链路其实很短短到很多人第一次做会以为「加个 meta 就完事」结果折腾一下午。完整走一遍是用户打开文章页 → 模板渲染时把标题、简介、封面图写进一段全局变量 → 前端 JS 拿当前页 URL 去请求自己的签名接口 → 接口返回 appId、timestamp、nonceStr、signature → wx.config 校验通过 → wx.ready 里调 updateShareData 塞数据。任何一环错位最后都是「卡片没图」这一个现象所以排查时不能只盯着模板。下面按源码实际落位顺序拆先是文件放哪再是后端怎么算签名最后是前端什么时机调。2.1 源码落位为什么放 /e/extend 而不是模板目录帝国CMS 的目录结构里/e/是程序主体/e/class/放公共函数和类/e/data/放缓存和临时文件/e/extend/是官方留出来的扩展目录模板则存在后台数据库或者/e/template/下。这份源码我建议整个丢进/e/extend/wxshare/理由有三条升级帝国CMS 时/e/extend/不会被覆盖能直接require到/e/class/connect.php里的函数比如截字用的esub()接口 URL 固定成/e/extend/wxshare/sign.php前端写死也好、做域名白名单也好都好处理。/e/extend/wxshare/ ├── config.php # appId、appSecret、允许签名的域名白名单 ├── WxJsSdk.php # 签名类含 token / ticket 两级缓存 ├── sign.php # 对外的签名接口返回 JSON ├── share.js # 前端注入脚本wx.config updateShareData └── cache/ # 缓存目录必须可写建议加 .htaccess 禁直接访问cache/这个目录是重点很多人图省事直接放/e/data/tmp/那个目录帝国CMS 自己也在读写清理缓存时容易连你的 token 一起删掉第二天早上第一批访问的用户就吃到一次接口报错。单独建目录权限给 755、属主跟 PHP 进程一致config.php里只留配置不写逻辑这样后面换公众号只改一个文件。模板侧不用大动内容页模板一般是showtemplate那套里加一段 PHP 和一个script把文章数据吐给前端就行具体写法放到第 3 章。这里先记住一个原则模板只负责「给数据」不负责「算签名」。签名必须放服务端appSecret 绝对不能出现在 HTML 里这是血泪经验泄露一次就得去后台重置。2.2 签名接口access_token 和 jsapi_ticket 的两级缓存签名公式本身一行就够把jsapi_ticket、noncestr、timestamp、url按字段名字典序拼成 query string做一次 sha1。真正麻烦的是jsapi_ticket从哪来——它得用access_token去换而access_token每天有调用次数上限多进程并发下还容易互相把对方刷失效。所以合格的做法是两级缓存token 缓一份ticket 缓一份都带过期时间。?php // /e/extend/wxshare/WxJsSdk.php class WxJsSdk { private $appId; private $appSecret; private $cacheDir; public function __construct($appId, $appSecret, $cacheDir) { $this-appId $appId; $this-appSecret $appSecret; $this-cacheDir rtrim($cacheDir, /) . /; } // 第一级access_token有效期 7200 秒提前 300 秒判过期 private function getAccessToken() { $file $this-cacheDir . token_ . md5($this-appId) . .php; $data $this-readCache($file); if ($data $data[expire] time() 300) { return $data[value]; } $url https://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credential . appid . $this-appId . secret . $this-appSecret; $res json_decode($this-httpGet($url), true); if (empty($res[access_token])) { return false; // 上层记录日志不要把错误吞掉 } $this-writeCache($file, [ value $res[access_token], expire time() $res[expires_in], ]); return $res[access_token]; } // 第二级jsapi_ticket同样 7200 秒 private function getJsapiTicket() { $file $this-cacheDir . ticket_ . md5($this-appId) . .php; $data $this-readCache($file); if ($data $data[expire] time() 300) { return $data[value]; } $token $this-getAccessToken(); if (!$token) { return false; } $url https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token . $token . typejsapi; $res json_decode($this-httpGet($url), true); if (empty($res[ticket])) { return false; } $this-writeCache($file, [ value $res[ticket], expire time() $res[expires_in], ]); return $res[ticket]; } // 对外只暴露这一个方法返回 wx.config 需要的四个参数 public function buildSignPackage($url) { $ticket $this-getJsapiTicket(); if (!$ticket) { return false; } $nonceStr substr(md5(uniqid(mt_rand(), true)), 0, 16); $timestamp time(); // 字段名字典序拼接url 用原始未编码的完整地址不要 urlencode $raw jsapi_ticket . $ticket . noncestr . $nonceStr . timestamp . $timestamp . url . $url; return [ appId $this-appId, timestamp $timestamp, nonceStr $nonceStr, signature sha1($raw), ]; } private function readCache($file) { return is_file($file) ? include $file : false; } private function writeCache($file, $payload) { // 先写临时文件再 rename避免并发读到写了一半的内容 $tmp $file . . . getmypid() . .tmp; file_put_contents($tmp, ?php return . var_export($payload, true) . ;); rename($tmp, $file); } private function httpGet($url) { $ch curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER true, CURLOPT_TIMEOUT 5, CURLOPT_SSL_VERIFYPEER true, ]); $out curl_exec($ch); curl_close($ch); return $out; } }几个参数值得单独说清楚。expire time() 300里的 300 秒是提前量——微信的 token 是「拿到那一刻起算 7200 秒」但网络延迟、服务器时钟偏差都会让边界判断飘不留提前量就会出现「刚判断没过期、拿去请求就报 40001」。CURLOPT_TIMEOUT 5是必须的微信那边偶发慢响应不设超时会把 PHP-FPM 进程挂住文章页跟着变慢。writeCache里先写.tmp再rename这一步容易被忽略。rename在同一文件系统内是原子操作读者要么看到旧文件要么看到新文件不会读到半截 PHP 语法错误的白屏。我在并发压测时踩过这个坑几十个请求同时进来都发现缓存过期一起写同一个文件其中几个读到空文件直接 fatal error。拼签名时url一定不要urlencode。字典序拼接阶段用的是原始字符串编码是最后传给前端做 query 参数时才做的事两处搞混是 invalid signature 的高发原因。2.3 前端时序wx.config 必须在分享数据之前后端算完签名接口文件把结果吐成 JSON。这里加一层域名白名单避免这个接口被人当成「任意 URL 签名器」拿去乱用?php // /e/extend/wxshare/sign.php require __DIR__ . /WxJsSdk.php; require __DIR__ . /config.php; // 里面定义 $appId / $appSecret / $allowHosts header(Content-Type: application/json; charsetutf-8); $url isset($_GET[url]) ? trim($_GET[url]) : ; $host parse_url($url, PHP_URL_HOST); if (!$url || !in_array($host, $allowHosts, true)) { echo json_encode([code 1, msg bad url]); exit; } // 微信签名不吃 # 后面的部分先切掉 $url strtok($url, #); $sdk new WxJsSdk($appId, $appSecret, __DIR__ . /cache); $pkg $sdk-buildSignPackage($url); if (!$pkg) { echo json_encode([code 1, msg sign fail]); exit; } echo json_encode($pkg, JSON_UNESCAPED_UNICODE);前端拿到这四个参数后顺序不能乱先wx.config成功回调wx.ready里再设置分享内容。而且jsApiList要把用到的接口都列进去漏一个就是调用无反应、控制台还不报错。// /e/extend/wxshare/share.js (function () { if (!window.__SHARE__) return; var s window.__SHARE__; var signUrl location.href.split(#)[0]; // 与后端保持同一份 URL fetch(/e/extend/wxshare/sign.php?url encodeURIComponent(signUrl)) .then(function (r) { return r.json(); }) .then(function (cfg) { if (cfg.code 1) { console.warn([wx] sign fail, cfg.msg); return; } wx.config({ debug: false, appId: cfg.appId, timestamp: cfg.timestamp, nonceStr: cfg.nonceStr, signature: cfg.signature, jsApiList: [ updateAppendShareData, updateShareData, onMenuShareTimeline, onMenuShareAppMessage ] }); wx.ready(function () { var data { title: s.title, desc: s.desc, link: s.link, imgUrl: s.img }; // 新版 JSSDK 走这个 if (wx.updateShareData) { wx.updateShareData(data); } // 老接口兜底部分客户端仍认 if (wx.onMenuShareTimeline) { wx.onMenuShareTimeline({ title: data.title, link: data.link, imgUrl: data.imgUrl }); } if (wx.onMenuShareAppMessage) { wx.onMenuShareAppMessage({ title: data.title, desc: data.desc, link: data.link, imgUrl: data.imgUrl }); } }); wx.error(function (res) { console.warn([wx] config error, res); }); }); })();signUrl必须和用户地址栏里看到的完全一致——带不带www、末尾有没有斜杠、query 参数顺序全都算在内差一个字符就是 invalid signature。老站点经常同时存在http://和https://两套入口这种要在服务器层面 301 收敛到一个不然签名永远对不上。如果站点是纯静态缓存页帝国CMS 开了静态生成location.href取的是当前真实地址反而是好事不用额外处理。3. 正文页改造把 titlepic 和 smalltext 真正喂进分享卡片后端接口通了接下来是数据源。帝国CMS 内容页模板里能拿到当前文章的$navinfor数组title、titlepic标题图、smalltext简介、newstext正文都在里面。理论上直接取就行实际上有一半的文章 titlepic 是空的简介字段编辑嫌麻烦没填封面图存的是相对路径正文里的图还带防盗链。这一章就是把这些脏数据磨平。3.1 模板取字段titlepic 空了拿什么兜底先在后台把「模板支持程序代码」打开否则模板里的?php ?会被原样输出。然后内容页模板里加这么一段放在/head之前或者 body 末尾都行注意要在引入 share.js 之前。?php // 内容页模板内$navinfor 为当前文章数组 $shareImg trim($navinfor[titlepic]); $shareDesc trim(strip_tags($navinfor[smalltext])); // 兜底一没有标题图从正文抓第一张 img if ($shareImg preg_match(/img[^]src[\]([^\])[\]/i, $navinfor[newstext], $m)) { $shareImg $m[1]; } // 兜底二还是没有用栏目配置的默认图 if ($shareImg ) { $shareImg /e/extend/wxshare/default.jpg; } // 兜底三简介为空截正文前 60 字 if ($shareDesc ) { $shareDesc esub(strip_tags($navinfor[newstext]), 60); } // 相对路径补成绝对地址 if (strpos($shareImg, http) ! 0) { $shareImg https:// . $_SERVER[HTTP_HOST] . / . ltrim($shareImg, /); } ? script window.__SHARE__ { title: ? json_encode($navinfor[title], JSON_UNESCAPED_UNICODE) ?, desc : ? json_encode($shareDesc, JSON_UNESCAPED_UNICODE) ?, img : ? json_encode($shareImg, JSON_UNESCAPED_UNICODE) ?, link : ? json_encode(https:// . $_SERVER[HTTP_HOST] . $_SERVER[REQUEST_URI], JSON_UNESCAPED_UNICODE) ? }; /script script src/e/extend/wxshare/share.js/scriptesub()是帝国CMS 自带的截字函数第二个参数是字符数中文按字符算不用自己写 mb_substr。json_encode后面加JSON_UNESCAPED_UNICODE是为了中文不被转成\uXXXX虽然不影响功能但调试时看输出会舒服很多。三个兜底顺序是有讲究的正文首图优先于默认图因为首图跟内容相关点开率明显高默认图只在前两者都没有时出场别小看它——一篇文章分享出去是灰方块用户基本不会点有个栏目统一配图至少卡片是完整的。这里踩过一次坑正文首图抓的是img srcdata:image/...懒加载占位图分享出去显示一个 1×1 透明像素看起来跟没图一样。加一条strpos($shareImg, data:) 0就跳过。另外$navinfor[title]里如果带了站点名后缀很多模板会拼分享标题就会变成「文章标题 - 站点名 - 站点名」。在模板里取原始$navinfor[title]而不是$navinfor[title]经过处理的版本或者干脆在 share.js 里做一次截断控制在 30 字以内微信卡片标题太长会被省略号截掉反而不好看。3.2 图片地址归一化相对路径、附件域名和混合内容帝国CMS 的附件路径取决于后台「附件地址」设置。有的站是相对路径/d/file/xxx.jpg有的是独立附件域名http://img.example.com/...这里只作格式示意还有的开了 https 但数据库里存的历史图片还是 http。微信客户端对分享缩略图的要求经验值是这样的正方形、300×300 以上、文件控制在几十 KB 以内最稳非正方形会被裁切太大或格式冷门比如 webp 在某些旧版本上可能直接不显示。归一化这一步建议单独写个函数别塞模板里?php // /e/extend/wxshare/helper.php function wx_abs_url($path, $scheme https) { $path trim($path); if ($path ) return ; if (strpos($path, data:) 0) return ; // 懒加载占位图 if (stripos($path, http://) 0) { // 站点全站 https 时把 http 图片也升上去避免混合内容被拦 return $scheme . substr($path, 4); } if (stripos($path, https://) 0) return $path; // 协议相对地址 //img.xxx.com/a.jpg if (strpos($path, //) 0) return $scheme . : . $path; return $scheme . :// . $_SERVER[HTTP_HOST] . / . ltrim($path, /); }参数说明$path是数据库里存的原始值$scheme默认 https是因为绝大多数公众号要求 JS 接口安全域名走 https。这个函数只做地址层面的改写不判断图片是否存在——判断存在性要发 HTTP 请求放在模板渲染里会拖慢页面。真要做好是在文章发布时用钩子异步检测一次把不合格的图记进日志让编辑去换。防盗链是另一个高频问题。图片站常开 Referer 白名单微信客户端拉图时 Referer 为空或者不是你的域名直接 403卡片就成了灰块。解决方式要么给缩略图走一个不带防盗链的独立域名要么在 Nginx 里对/d/file/下特定尺寸的缩略图放开空 Referer。这条别偷懒测试时一定要用真手机发一次浏览器里看是正常的微信里不一定。3.3 分享链接与回流统计把 from 参数接回自己的计数分享出去的链接默认就是当前页 URL好处是签名对得上坏处是你不知道流量从哪来。常见做法是在link里追加一个标记参数比如?fx1然后在文章页入口处判断这个参数往自己的统计表里写一条。注意link里的 URL 和签名用的 URL 是两回事前者可以带参数后者必须是当前真实地址别把两者搞混。?php // 文章页顶部统计分享回流 if (isset($_GET[fx])) { $aid intval($navinfor[id]); $src $_GET[fx] 2 ? timeline : friend; // 简单写一张自建统计表或用帝国CMS 的点击数表扩展 $empire-query(INSERT INTO {$dbtbpre}ecms_share_log(aid, src, ip, addtime) VALUES ($aid, $src, . egetip() . , . time() . )); }$empire是帝国CMS 的数据库操作对象$dbtbpre是表前缀egetip()取真实 IP会读X-Forwarded-For前面有 CDN 时记得配置可信代理。这张表不用建索引太多量大了按天分表或者只留最近 30 天不然文章表没垮日志表先撑爆。想让统计更准可以在 share.js 里给朋友圈和好友分别传不同的linkwx.updateShareData是统一设置onMenuShareTimeline和onMenuShareAppMessage可以各传各的正好用来区分两个入口。4. 配置与缓存让签名接口在流量峰值下不打爆配额签名接口是每打开一篇文章就要调一次的遇上热点文章被大号转发QPS 能翻好几倍。这个接口本身很轻读缓存 sha1真正怕的是缓存失效瞬间的大量回源——几百个请求同时发现 ticket 过期一起去找微信换新的轻则触发频率限制重则把刚拿到的 token 刷失效进入「一直失败一直重试」的死循环。4.1 文件缓存还是数据表并发下的两种写法小站用文件缓存足够成本低、不用动数据库。多机部署就得换成共享存储常见选择是 Redis 或者数据库表。用帝国CMS 自带的数据库操作最省事建一张两列的表就行CREATE TABLE IF NOT EXISTS phome_wx_cache ( ckey varchar(64) NOT NULL COMMENT 缓存键如 ticket_abc123, cvalue text NOT NULL COMMENT access_token 或 jsapi_ticket, expire int(10) unsigned NOT NULL DEFAULT 0 COMMENT 绝对过期时间戳, PRIMARY KEY (ckey) ) ENGINEMyISAM DEFAULT CHARSETutf8;phome_是帝国CMS 默认表前缀装站时改过就换成自己的。ckey用token_/ticket_加 appId 的 md5一个库里挂多个公众号也不会串。expire存绝对时间戳而不是剩余秒数避免不同机器时钟不一致算出不同结果。换成数据库后并发问题从「同时写文件」变成「同时回源」。加一把锁最直接更新前先UPDATE ... SET expire 过期时间 WHERE ckey x AND expire 当前时间影响行数为 1 的进程才去调微信接口其他进程直接读旧值或者短暂重试。这招在文件缓存上也能用——用flock()排他锁包住回源那一段代价是请求会短暂排队但比一起打微信接口强。提示缓存目录和缓存表都要有清理策略。token 泄露或者换了 appSecret 之后旧缓存不清会一直报 40001很多人第一反应是代码写错了其实清一下缓存就好。4.2 后台配置项与公众号后台的对应关系签名失败有一半不是代码问题是配置对不上。这张表是我每次接新站都要对着核一遍的配置项写在哪对应公众号后台位置常见错误appIdconfig.php设置与开发 - 基本配置复制时带了空格appSecretconfig.php同上重置后旧值立即失效重置后忘了改文件JS 接口安全域名公众号后台设置与开发 - 公众号设置 - 功能设置带了https://或端口域名校验文件网站根目录同上下载 MP_verify_xxx.txt放错目录或返回 404服务器出口 IP公众号后台基本配置 - IP 白名单只填了入口 IP漏了出口几个细节安全域名不能带协议头和端口填www.example.com这种裸域名这里仅示意格式校验文件必须能从根域名直接访问到帝国CMS 有些站的根目录被伪静态规则拦了.txt得单独加一条location ~* \.txt$ { try_files $uri 404; }之类的放行。IP 白名单是出口 IP不是域名解析的 IP——如果服务器走 NAT 出去得问运维要真实出口地址填错了报 40164。多域名站点PC 站 移动站要在安全域名里把所有用到的域名都加上最多三个。mobile 站如果是独立域名模板里取$_SERVER[HTTP_HOST]生成link是对的但签名接口那边一定要按请求的实际 host 来签别写死一个。5. 避坑排查上线后最常撞上的五类转发异常调这块最难受的地方是失败现象高度相似——九成都是「卡片没图」或者「简介不对」但原因可能在南辕北辙的五个地方。下面这五条按我遇到的频率排序每条都按现象、原因、解决三步写照着对一遍基本能定位。5.1 图有了简介变成正文前几十个字现象分享卡片缩略图正常但简介显示的是正文开头那段而不是后台填的摘要。原因模板取smalltext时这个字段是空的走了兜底逻辑截正文。帝国CMS 的smalltext需要在后台「录入项」里勾选并填写很多老站从别的系统迁移过来这个字段压根没数据还有一种情况是模板里取成了$navinfor[smalltext]的 HTML 版本里面带了p标签微信把标签当文本截断了。解决先确认字段有没有值没有就推动编辑补别长期靠截正文兜底。有值但带标签的用strip_tags()过一遍再trim()连续空格用preg_replace(/\s/u, , $str)压成一个。截断长度控制在 40 到 60 字之间微信朋友圈卡片一行大概能放 14 个字两行刚好。5.2 iOS 上正常安卓上分享出去还是老样子现象iPhone 转发出去是新卡片安卓手机转发还是旧的标题和灰图清缓存也不行。原因安卓微信客户端对分享数据的读取时机和 iOS 不一样wx.config没走完就点了右上角菜单读到的还是默认值另一种情况是站点开了页面缓存比如 CDN 缓存了 HTMLwindow.__SHARE__里还是上一版数据。解决把wx.ready里的设置逻辑提前到页面加载完成就执行别等到用户点菜单才设。同时给签名接口的响应加Cache-Control: no-storeHTML 页面如果走 CDN对?fx这类参数做不缓存规则。测试时不要只在开发者工具里看必须真机iOS 和安卓各测一台。5.3 控制台报 invalid signature现象wx.config回调wx.error提示签名错误卡片完全不生效。原因按出现频率排一是 URL 不一致多了www、少了末尾斜杠、http和https混用二是签名前对 url 做了urlencode三是jsapi_ticket缓存串了别的公众号的值四是服务器时间不准timestamp和微信侧差太多。解决在sign.php里把参与签名的$url原样error_log下来跟手机浏览器地址栏逐个字符比对。写个临时调试页把sha1的原文打印出来用微信官方提供的校验工具对一遍。时间问题用ntpdate同步一下或者直接在服务器上date看偏差。5.4 图片 403浏览器能打开微信打不开现象缩略图地址直接在浏览器里访问正常微信里显示灰块抓包看是 403。原因图片服务器开了防盗链微信客户端拉图时 Referer 为空或不是白名单域名也可能是图片走的是 http页面是 https混合内容被客户端拦了。解决给缩略图单独放行空 Referer或者在 Nginx 里对图片来源做一个不受防盗链限制的路径。http 图片统一在wx_abs_url()里升到 https前提是图片服务器支持 https不支持就得换图床或者加证书。5.5 定时任务跑完全天分享都失效现象每天凌晨过后一段时间所有文章的分享卡片全部没图过一阵又自己好了。原因有人写了个定时任务「每天清一次 access_token 缓存」本意是防止过期结果清完之后大量请求同时回源微信侧把新 token 刷掉旧 token 又已经失效中间这段时间全是失败。解决删掉这种「定时清缓存」的任务靠expire自然过期加提前量就够了。要预热就在低峰期跑一个只读任务先请求一次sign.php把缓存填上不要去删。真的需要强制刷新加一个带密钥的手动接口人工触发。6. 二次开发从单篇文章扩到全站再让卡片带上栏目和作者单篇改造做完接下来八成会遇到「全站都要生效」的需求。最省事的做法不是每套模板都贴一遍代码而是把数据组装抽成一个函数放在/e/extend/wxshare/helper.php里模板只留一行调用?php // helper.php 里补充统一组装分享数据 function wx_share_data($navinfor, $defaultImg /e/extend/wxshare/default.jpg) { $title trim($navinfor[title]); // 标题带栏目名方便订阅号列表里区分来源 if (!empty($navinfor[classname])) { $title $title . | . $navinfor[classname]; } $img wx_abs_url($navinfor[titlepic]); if ($img preg_match(/img[^]src[\]([^\])[\]/i, $navinfor[newstext], $m)) { $img wx_abs_url($m[1]); } if ($img ) $img $defaultImg; $desc trim(strip_tags($navinfor[smalltext])); if ($desc ) $desc esub(strip_tags($navinfor[newstext]), 50); return [ title esub($title, 30), desc $desc, img $img, link https:// . $_SERVER[HTTP_HOST] . $_SERVER[REQUEST_URI], ]; }模板里就变成window.__SHARE__ ? json_encode(wx_share_data($navinfor), JSON_UNESCAPED_UNICODE) ?;栏目页、专题页也能复用传进去的数组凑齐title、titlepic、smalltext、newstext四个键就行。再往前一步是让不同入口的卡片有差异。列表页分享和详情页分享标题策略不一样详情页用文章标题列表页用「栏目名 最新更新」用户点进来预期不同。用updateAppendShareData可以在已有的分享数据上追加字段部分版本支持但更稳的还是按页面类型渲染不同的__SHARE__前端只做一次读取。改造范围改动点工作量适合场景单篇模板模板加 PHP 引 share.js半小时只想修好内容页全站函数化抽 helper 各模板调用半天栏目页、专题页也要加统计回流link 带 fx 参数 日志表一天要看分享带来的量多公众号配置按域名分组一天一站多号运营验证方法别偷懒改完之后用微信开发者工具的「公众号网页调试」先看wx.config是否 ok再用两台真机分别发一次好友和朋友圈确认标题、简介、图三样都对。有条件的话在分享链接上带一个明显的短参数看服务端日志里有没有收到能同时验证回流统计通没通。说个我自己的教训。早期做这类改造我习惯在浏览器里看着卡片正常就交付结果上线第二天运营反馈安卓端全是灰图。从那以后我每次动分享相关的代码都强制走一遍「iOS 真机发好友 → 安卓真机发朋友圈 → 换一个没登录过的微信再发一次」这三步一步不落。缓存、域名、客户端差异这三样少测一个都可能翻车。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑