写在前面: 本文首发于 Asahi Blog ,可能会出现 用词不当、不分主次、逻辑不明 等各种 错误 ,以及 入机式发言 ,希望各位佬友批评指正。 升级完 OpenList 的那一刻,我以为只是常规的 bugfix 更新,结果打开主页一看——评论区消失了。 CSS 好端端的,Waline 的样式文件请求返回 200,但 <div id="waline"> 里空空如也,高度是一个令人绝望的 908.812×0 。 折腾了一段时间之后,把问题锁定在了 v4.2.2 的一个 Breaking Change 上。这篇文章就把整个来龙去脉记录下来,顺带把修复方案整合一下,希望踩过同样坑的朋友少花点时间。 问题复现 根据 Waline快速上手中的方案 ,在旧版本(≤ 4.1.10)中仅需在主页的 元数据 README 里写一段 HTML,CSS 和 JS 全部内联: <!-- Waline 容器 --> <link rel="stylesheet" href="https://unpkg.com/@waline/client@v3/dist/waline.css" /> <div id="waline-comment" style="margin: 20px auto; max-width: 960px;"> <h2 style="text-align:center">- 评论 Comments -</h2> <div id="waline"></div> </div> <!-- Waline JS 初始化 --> <script type="module"> import { init } from 'https://unpkg.com/@waline/client@v3/dist/waline.js'; init({ el: '#waline', serverURL: 'https://comments.example.com', emoji: false, comment: true, search: false, path: window.location.pathname, }); </script> 升级到 4.2.2 之后,这段代码的表现变成: <link> 的 CSS 请求正常发出并返回 <script type="module"> 中的 JS 完全没有执行 #waline 容器尺寸为 908.812×0 (宽度撑开了,但高度为 0,Waline 从未初始化) 根本原因:PR #2346 把过滤逻辑从后端搬到了前端 v4.2.2 的 Release Notes 里有两条 Breaking Changes,其中一条就是: settings : Move FilterReadMeScripts to frontend - by @xrgzs in #2346 这条改动对应 commit a5ba6a0 ,看一下实际 diff 就很清楚了。 旧版后端做了什么 旧版的 server/handles/down.go 在代理 .md 文件时,如果 FilterReadMeScripts 开关为 true (默认开启),会在 后端 走一套完整的处理流水线: 用 goldmark 把 Markdown 源文件渲染成 HTML 用 bluemonday 的 UGC Policy 对 HTML 进行 sanitize(消毒) 把清洗过的 HTML 以 text/html 形式返回给前端 bluemonday 的 UGC Policy 会 剥除 <script> 、 <link> 等危险标签 ,这是它的设计初衷。但这套流水线只在 直接代理 .md 文件 时(走 /d/ 路径)才触发,主页展示 README 走的是 /api/fs/get API 接口,后端直接原样返回 Markdown 内容,不经过 bluemonday。 所以旧版真正起作用的机制在前端:前端把 README 内容渲染为 HTML 注入到页面后,会对其中的 <script> 标签做一次 克隆重新 append 的处理,让它们实际执行。 <script type="module"> 就是通过这个机制被触发的。 新版改动:32 行代码的删除 commit a5ba6a0 从 down.go 里 整体删除 了那 31 行后端清洗逻辑,同时移除了对 goldmark 和 bluemonday 两个依赖库的引用,并在 setting.go 里给 FilterReadMeScripts 的配置加上了注释 // frontend ,意思是:这个开关的实际执行逻辑已经全部交给前端负责了。 这是一次架构上的合理重构——把视图层的过滤放在前端做,后端减负,逻辑更清晰。 但问题在于 :前端新版本对 README 内容里 <script> 标签的处理比之前更严格, type="module" 形式的脚本会被 直接过滤掉,不再执行 。 这就解释了所有现象: 现象 原因 CSS 正常加载 <link> 标签通过了前端的过滤,或走了不同处理路径 JS 完全不执行 <script type="module"> 被前端新过滤器丢弃 #waline 高度为 0 容器 div 存在,但 Waline 从未初始化,内容为空 顺便一提, <script type="module"> 在被 innerHTML 动态插入 DOM 时,浏览器本身也不会自动执行——这是浏览器的安全规范,ES Module 的 import 语义只在顶层脚本上下文有效。所以即使前端没过滤,不经过特殊处理的 module script 也跑不起来。旧版前端的克隆 append 机制恰好绕过了这个限制,新版则没有。 完整修复方案 修复思路是把代码分散到三个不同的注入点,各司其职,完全绕开 README 的过滤器: 第一步:自定义头部(Custom Head)—— 放 CSS 进入 设置 → 全局 → 自定义头部 ,填入: <link rel="stylesheet" href="https://unpkg.com/@waline/client@v3/dist/waline.css"> <head> 里的 <link> 走标准 HTML 解析路径,完全不经过任何 README 过滤器,稳定可靠。 第二步:自定义 Body(Custom Body)—— 放 JS 初始化逻辑 进入 设置 → 全局 → 自定义 Body ,填入以下完整代码: <!-- 引入 UMD 版本的 Waline(挂载到 window.Waline 全局变量) --> <script src="https://unpkg.com/@waline/client@v3/dist/waline.umd.js"></script> <script> (function() { let walineInstance = null; let initTimer = null; function doInitWaline() { const walineEl = document.querySelector('#waline'); if (!walineEl || typeof Waline === 'undefined') return; // 容器内已有内容,说明 Waline 正常挂载着,无需重复初始化 if (walineEl.children.length > 0) return; // 有旧实例先销毁,防止内存泄漏 if (walineInstance && typeof walineInstance.destroy === 'function') { try { walineInstance.destroy(); } catch(e) {} walineInstance = null; } walineInstance = Waline.init({ el: '#waline', serverURL: 'https://comments.example.xyz', // 替换为你的 Waline 服务地址 emoji: false, comment: true, search: false, path: window.location.pathname, }); } // 防抖:SPA 路由切换时 DOM 频繁变动,延迟 300ms 等待稳定 function debouncedInit() { clearTimeout(initTimer); initTimer = setTimeout(doInitWaline, 300); } // 1. 页面首次加载 doInitWaline(); // 2. 监听 DOM 变化(SPA 框架动态重建 DOM 时触发) const observer = new MutationObserver(debouncedInit); observer.observe(document.body, { childList: true, subtree: true }); // 3. 拦截 History API(前进/后退/框架内部路由跳转) const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function() { originalPushState.apply(this, arguments); debouncedInit(); }; history.replaceState = function() { originalReplaceState.apply(this, arguments); debouncedInit(); }; window.addEventListener('popstate', debouncedInit); })(); </script> 为什么改用 UMD 而不是 ESM? 原来的 import { init } from '...waline.js' 是 ES Module 写法,有两个问题:一是动态插入的 <script type="module"> 浏览器不执行;二是前端过滤器会丢弃它。改用 waline.umd.js 后,脚本加载后直接把 Waline 挂到 window 上,后续代码用 Waline.init() 调用,完全是普通的全局变量方式,没有任何模块化限制。 为什么需要 MutationObserver 和 History API 拦截? OpenList 是基于 SolidJS 的 SPA(单页应用)。当你从别的目录切换回主页时,整个 DOM 树会被 SolidJS 销毁并重建,之前初始化的 Waline 实例也随之消失。如果只在页面加载时初始化一次,路由切换回来后评论区就空了。 MutationObserver 监听 document.body 的子树变化,每次 DOM 重建后都会触发重新初始化; history.pushState/replaceState 的拦截则覆盖了框架内部路由跳转的场景;300ms 防抖避免 DOM 频繁变动时重复触发。 第三步:主页元数据 README —— 只保留容器 div 回到主页元数据,把 <script> 和 <link> 全部删掉,只保留 HTML 容器: <div id="waline-comment" style="margin: 20px auto; max-width: 960px;"> <h2 style="text-align:center">- 评论 Comments -</h2> <div id="waline"></div> </div> 纯粹的 <div> 不含任何可执行内容,不会被任何过滤器动到。JS 找到这个 #waline 容器后负责挂载,CSS 从 <head> 里来,三方各归其位。 三种注入点的本质区别 理解这三个位置的差异,以后遇到类似问题就不会懵了: 注入点 处理机制 能跑 <script> ? 能跑 <link> ? 主页元数据 README 前端 Markdown 渲染 + 新版过滤器 (被过滤) 看情况,不稳定 自定义 Body 直接注入 <body> ,不走 README 过滤器 自定义 Head 注入 <head> ,标准 HTML 解析 README 的设计初衷是内容展示,过滤脚本是合理的安全考量。需要执行 JS 的逻辑就应该放到 Custom Body/Head 这两个专门为此设计的位置。 1 个帖子 - 1 位参与者 阅读完整话题
tag机器人有点怪() 3 个帖子 - 3 位参与者 阅读完整话题
让gpt-5.5修复了半天 方法:将~/.claude.json的cachedGrowthBookFeatures.tengu_workflows_enabled从false改为true 1 个帖子 - 1 位参与者 阅读完整话题
Linux 之父 Linus 发飙:别用 AI 提交大量重复 Bug 报告,直接提修复方案,OK? - IT之家 14 个帖子 - 9 位参与者 阅读完整话题
IT之家 5 月 18 日消息,Linus Torvalds 日前发布了 Linux 7.1 的第四个候选版本,他在 5 月 17 日表示 AI 很棒,但前提是它真的有帮助。Linus 抱怨大量重复的 bug 报告会带来不必要的工作。 需要明确的是,Linus 并不是说开发者在 Linux 开发时不应该使用 AI 工具。他的意思是,如果你用 AI 工具发现了 Linux 中的漏洞,那么很可能其他人也发现了,因此造成 AI 提交 Bug 报告内容的大量重复。 Linus 表示, 他不需要那些随便发报告、没有真正理解问题的随意提交者 。简而言之,他希望开发者们使用 AI 工具,对发现的问题提交实际修复方案。 一些文档更新值得一提:持续涌入的 AI 报告基本上让安全列表几乎完全无法管理,因为不同的人用同样的工具发现了同样的东西,导致大量重复。人们花了所有时间把事情转发给合适的人,或者说“那个问题一周 / 一个月前就解决了”,并指向公开讨论。 Linus Torvalds 曾在 2025 年 11 月的韩国首尔 Linux Foundation 开源峰会上表示,过去差不多 20 年里,他其实已经不是程序员了,更多是系统的技术负责人和维护者,日常工作就是坐在电脑前看邮件。 据IT之家此前报道,在谈到 AI 替代程序员时, Linus 表示 AI 只是另一种工具 ,就像编译器让人们从手写汇编代码中解放出来,但并没有让程序员消失。
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
我记得以前有一个复方角菜酸酯乳栓,但是现在停产了,只能用马应龙了么? 大家日常怎么维护菊花有什么小妙招?
36氪获悉,丰原药业公告,近日,公司收到国家药监局核准签发的复方聚乙二醇(3350)电解质散药品注册证书。该药品用于治疗1岁至11岁儿童慢性便秘;用于治疗5岁至11岁儿童粪便嵌���,即顽固性便秘伴直肠和/或结肠粪块堆积。