这是一篇水文, 可是为了解决 mpMath 与微信公众号新版编辑器不兼容的原因, 我浪费了我美丽的周日下午, 不把这个经历水出来我实在意难平啊!意难平!!
mpMath 的一键公式转换一直是我做出来的东西中我第二引以为豪的! 每次点击 “公式转换” 之后 mpMath 将文档中一大坨公式一一绘制成 svg 我都感到了多巴胺爆棚. 而且 mpMath 也帮我认识好多数学科普 up 主. 可是! 最近! 她不能工作了!
P.S. 第一自豪的当时是我的 hidva/as2cfg 啦, 将一大坨汇编代码绘制成一个井然有序的 Control Flow Graph 啧啧那感觉爽爆了!
现场很奇怪, 点了 “公式转换” 之后页面便被清空了. 一脸懵逼, 我对前端可是一窍不通啊, 当初一键公式转换就是在 Qwen, ChatGPT 一大帮子 AI 帮助下才整出来的, 而且我真不想花时间在这上面. 可是没有办法, 硬着头皮给编辑器主页面 div 元素下了断点, 准备看看是谁修改了这个 div.
// https://res.wx.qq.com/mpres/zh_CN/htmledition/js/default~media/appmsg_edit_v2_gray~media/msg_modify_fe.9ffea297.js
this.observer = window.MutationObserver && new window.MutationObserver(u => {
for (let y = 0; y < u.length; y++)
this.queue.push(u[y]);
if (U && Z <= 11 && u.some(y => y.type == "childList" && y.removedNodes.length || y.type == "characterData" && y.oldValue.length > y.target.nodeValue.length))
this.flushSoon();
else
this.flush()
});
如上所示, 微信通过 MutationObserver 监听到变更页面具有 mjx-container, mathml 等 tag 之后会自动移除这些 node, 我实在不理解为啥要移除掉这些人畜无害的 node. 所以解决方法也很简单, hook window.MutationObserver! 一开始是使用 Tampermonkey 直接让 window.MutationObserver
为 None:
// ==UserScript==
// @name Disable MutationObserver
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 禁用 MutationObserver
// @match *://*/* // 用于定义在哪些页面上应用此脚本,你可以更改为特定网页
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
window.MutationObserver = undefined;
window.WebKitMutationObserver = undefined; // 兼容性
})();
P.S. 注意这个 run-at document-start
不可少, 不然 msg_modify_fe 先加载之行的话 hook 就没有效果了. 但这会影响到 appmsg_edit_v2_gray_fe.50b9e746.js 一些功能, 这个文件会直接构造 MutationObserver 对象:
MutationObserver(function() {
Z.disconnect();
if (V.textContent && !V.hasAttribute("data-styled")) {
Ut(V, M.name, K, M.url, V.__MICRO_APP_LINK_PATH__)
}
});
所以换个思路, 仅当 msg_modify_fe 文件构造 MutationObserver 时才使用 hook 后的 MutationObserver.
// ==UserScript==
// @name wechat-hooked-MutationObserver
// @namespace http://tampermonkey.net/
// @description hook MutationObserver 以便让微信公众号允许 mjx-container 等 Tag.
// @author hidva.com
// @match *://*/*
// @match https://mp.weixin.qq.com/cgi-bin/appmsg*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 保存原始 MutationObserver
const OriginalMutationObserver = window.MutationObserver;
// 创建一个包装的 MutationObserver
window.MutationObserver = function(callback) {
// 根据调用栈判断如果是 a.js 调用,就拒绝创建观察者
const error = new Error();
if (error.stack && error.stack.includes('msg_modify_fe')) {
console.warn('MutationObserver blocked in msg_modify_fe');
// 返回一个伪造的对象以避免错误
return {
observe: function(a,b) {},
disconnect: function() {},
takeRecords: function() { return []; }
};
} else {
// 否则继续使用原始 MutationObserver
return new OriginalMutationObserver(callback);
}
};
// 保留原型链,以兼容 instanceof 操作
window.MutationObserver.prototype = OriginalMutationObserver.prototype;
})();
还是不行, 保存时还是会被去掉所有 mjx-container. P.S. 如上这些代码全是 Qwen 生成的.
唉! 静了下心, 看来速战速决的法子行不通, 需要踏实踏实分析一下根因了. 看上去根本原因应该还是微信公众号编辑器换到 ProseMirror 了, 原有基于 ueditor 的方式便不再兼容了.
等等我注意到文件名中的 “gray” 这意味着 ProseMirror 新编辑器还在灰度中, 而我不幸地被选中灰度了. 那么意味着一定有个地方可以控制我是否参与灰度, 我希望这些东西不要是放在服务端决策的啊. 简单捋了下被混淆后的代码, __createEditor()
看起来像是 create editor 的入口, window.__MpEditor
, useEditorV
若为 true, 则使用新版编辑器. 然后幸运地找到了:
var Ut = (Dt || location.href.indexOf("mpeditor=1") !== -1) && location.href.indexOf("mpeditor=0") === -1
这里 Ut 也是用于控制是否启用 new editor. 而且确实在链接中追加 mpeditor=0
便不会使用新编辑器了!