LOADING

加载过慢请开启缓存 浏览器默认开启

修复加密文章解密后目录消失的问题

记录一个给 ParticleX 主题修复加密文章解密后目录(TOC)消失的 bug,顺带修了一个隐藏已久的 setter 错误。

问题现象

加密文章输入正确密码解密后,右侧目录导航不显示任何内容,一片空白。

根本原因

有两个 bug 叠在一起。

Bug 1:TOC 初始化时机问题

toc.js 里的 TocComponentonMounted 时扫描 #main-content 容器内的标题节点:

Vue.onMounted(() => {
  const element = document.getElementById("main-content");
  // 遍历子节点,找 H1~H6 标签...
});

但加密文章的 #main-content 初始是空的,内容在解密后才通过 innerHTML 动态填充。Vue 组件挂载时标题根本不存在,所以目录为空。

Bug 2:Topic 类的 level setter 写错了

Topic 类里有一个 setter:

set level(value) {
  this.className = TOC_CLASSNAME_PREFIX + value;  // ❌ 错误
  this._level = value;
}

className 只有 getter 没有 setter,直接赋值会抛 Cannot set property className of #<Topic> which has only a getter。正确写法应该是 this._className

这个 bug 原本在普通文章里也会触发(minLevel > 1 时会执行 value.level -= gap),只是之前没被发现。

修复方案

修复 Bug 2:先把 setter 改对

themes/particlex/source/js/lib/toc.js

set level(value) {
  this._className = TOC_CLASSNAME_PREFIX + value;  // ✅ 改为 _className
  this._level = value;
}

修复 Bug 1:解密后重新扫描 TOC

思路是把扫描逻辑抽成独立函数 scanToc,然后监听一个自定义事件 crypto-decrypted,解密成功后触发它。

**themes/particlex/source/js/lib/toc.js**,把 onMounted 里的扫描逻辑抽出来:

function scanToc() {
  const element = document.getElementById(SCAN_FROM_ELEMENT_ID);
  if (!element) return;
  const toc = [];
  let minLevel = 1000;
  element.childNodes.forEach((value) => {
    let level;
    if (!(value.nodeName.length === 2 && value.nodeName[0] === "H" &&
          !Number.isNaN((level = Number.parseInt(value.nodeName[1]))))) return;
    toc.push(new Topic(value, level));
    minLevel = Math.min(minLevel, level);
  });
  if (minLevel > 1 && toc.length > 0) {
    const gap = minLevel - 1;
    toc.forEach((value) => { value.level -= gap; });
  }
  topics.value = toc;
  adjustTocLabelPos();
}

Vue.onMounted(() => {
  scanToc();
  window.addEventListener("scroll", scrollListener);
  window.addEventListener("resize", resizeListener);
  window.addEventListener("crypto-decrypted", scanToc);  // 监听解密事件
});

Vue.onUnmounted(() => {
  window.removeEventListener("scroll", scrollListener);
  window.removeEventListener("resize", resizeListener);
  window.removeEventListener("crypto-decrypted", scanToc);  // 清理
});

**themes/particlex/source/js/lib/crypto.js**,解密成功后派发事件:

if (CryptoJS.SHA256(decrypted).toString() === shasum) {
  this.cryptoStatus = "success";
  content.innerHTML = decrypted;
  this.render();
  window.dispatchEvent(new CustomEvent("crypto-decrypted"));  // 通知 TOC 重新扫描
}

效果

解密成功后,crypto-decrypted 事件触发,TOC 重新扫描此时已经填充好内容的 #main-content,目录正常渲染出来。普通文章不受影响,onMounted 时直接扫描即可。