WWW.YOUINFO.SITE
标签聚合 复方

/tag/复方

LinuxDo 最新话题 · 2026-06-08 16:51:18+08:00 · tech

写在前面: 本文首发于 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 位参与者 阅读完整话题

IT之家 · 2026-05-18 10:53:50+08:00 · tech

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 只是另一种工具 ,就像编译器让人们从手写汇编代码中解放出来,但并没有让程序员消失。