前阵子调一个老项目的接口时,我在CSDN上翻到了一篇正好对症的文章。那篇文章有完整的实现代码,我当时的想法很简单:先存下来,等会去工位旁边的打印机打出来慢慢看。结果Ctrl+P一按,出来的PDF简直没法看——代码块只显示一半,右下角盖着一个水印,导航栏和推荐位那些乱七八糟的东西全挤在纸面上。我当时就想,得给自己做一个小工具,把"打开文章→复制代码→手动整理→打印"这套流程缩成一键。于是有了这个书签脚本:在CSDN文章页点一下收藏夹里的按钮,自动展开全部代码、清掉水印和干扰模块、调整好打印样式,最后直接弹起打印对话框。这篇文章就是把这个小工具的完整思路、代码和踩坑过程分享出来,给同样对"保存技术文章PDF"这件事有执念的朋友参考。
1. 从一次失败的"保存网页"说起:CSDN文章到底卡在哪
1.1 三件烦心事:折叠代码、水印和乱排的布局
技术文章的打印痛点,说来说去就三个,而且这三个几乎是CSDN这类内容平台的通病。
第一个是代码折叠。CSDN 的长篇文章为了首屏阅读体验,默认会把超过一定行数的代码块折叠起来,只露出开头几行,要阅读全文得手动点一下"展开"。如果只是浏览器里看倒还好,问题是打印的时候浏览器只渲染当前DOM状态,你没点开的部分,打印出来就是不完整的。一篇文章里十来个代码块,每个都要手动点开,漏掉一个就得重新打印。
第二个是水印。CSDN 在打印或者截图时会在文章区域叠加一层水印,通常是右下角的一个半透明标志,有时候是博主ID。这个水印在屏幕上看不太显眼,但落到纸面上或者转成PDF之后,正好压在代码区上,特别影响阅读。更麻烦的是它的实现方式不一定只有一种,有些是页面里的DOM节点,有些是canvas画上去的,还有些是打印样式表里通过伪元素动态生成的。处理思路完全不同。
第三个是打印布局。CSDN 页面元素非常多:顶栏、侧边栏、博主信息卡、相关推荐、评论列表、底部工具条……直接 Ctrl+P 打印,这些模块全都会被塞进 PDF,一篇文章能打出七八页,真正有价值的正文反而被挤得支离破碎。与此同时,页面自带的 CSS 对屏幕媒体做了大量适配,但到了打印媒体上,这些适配基本失灵。
三件事叠在一起,"打印CSDN文章"这个动作就从一个简单需求变成了一个需要技术处理的问题。
1.2 用Ctrl+S存网页,为什么问题更严重
很多人打印之前会先尝试把网页保存到本地,这个思路听起来没毛病,但在CSDN上效果更差。
静态保存下来的HTML,只是把当前页面DOM的某一瞬间快照存了下来。CSDN 这类动态站点,很多代码内容是靠异步请求加载的,你 Ctrl+S 存下来的页面里可能压根没有完整代码,只有一段"加载中"的占位符。再加上现代浏览器默认会把JS执行状态和CSS一并保存,如果原页面里有用JS动态渲染的水印层,保存下来的本地副本里也一样存在。你本地打开这个HTML,再打印,还是会踩一遍上面说的那些坑。
更隐蔽的问题是异步资源的引用。保存下来的HTML文件依赖的CSS、JS、图片如果仍指向原站点域名,断网的时候打开就是一堆样式全丢的"素颜"页面,打印出来的效果比在线打印还要差。
1.3 这工具的适用场景和不适用场景
在动手写脚本之前,先把这个工具的边界想清楚。它适用的场景是这样的:你要把CSDN上一篇技术文章存成PDF,或者打印出来,且这篇文章是公开可访问的,你只是做本地存档、离线阅读或批注。它不适用的情况也有:你想批量下载别人的整个专栏,或者想在转载时去掉水印署上自己的名字,这超出了"个人工具"的合理范围,我也不建议这么干。
所以我做的这个"清洗打印"脚本,定位非常明确:它只是一个运行在浏览器里的前端页面清理工具,不碰任何接口、不做登录态相关的处理、不搞破解,纯粹解决"把网页变成一份干净文档"这个需求。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 打印前页面清洗:预览清洗脚本的核心逻辑与关键代码
2.1 为什么我用书签脚本而不是浏览器扩展
一开始我也考虑过写一个浏览器扩展,毕竟扩展有完整的API权限,可以做更多事情。但仔细一琢磨,对这个需求来说扩展是杀鸡用牛刀。
书签脚本(bookmarklet)的核心优势是零安装、零权限、跨浏览器通用。一个 javascript: 开头的短链接,收藏到书签栏,点击就在当前页面执行JS,不需要去扩展商店审核,不需要打包CRX,Edge、Chrome、Firefox通吃。对个人工具来说这是最省事的形态。
当然它也有缺点:书签脚本受同源策略限制比较死,不能跨域去乱动其他站点的内容;另外它的执行时机完全取决于用户什么时候点击,不像扩展可以挂在页面加载事件上自动触发。这个缺点我会在后面的"自动清洗"方案里用用户脚本管理器来弥补。
2.2 展开折叠代码:先看DOM,再找按钮
展开代码这个动作,细想一下其实有两个层次。
第一层次是页面里有一个明确的"展开"按钮,点了之后才会把完整代码渲染出来。这种情况最直接的处理方式就是遍历页面找到所有这类按钮,逐个点击。但CSDN页面里的按钮类名并不完全固定,不同年份的文章模板用的HTML结构还不一样,所以我选了一个更鲁棒的方案:不光找"展开"按钮,还把 aria-expanded="false" 的元素、data-toggle="collapse" 这类折叠标记一并扫描。之所以这么干,是因为代码折叠的实现方式在历史上反复变过,靠单一选择器最容易翻车。
第二层次是文章正文被限高遮罩。CSDN 有些文章开头会有一段"阅读全文"的遮罩,挡住后面的内容,点击后遮罩消失、正文展开。这也是一个需要处理的 DOM 节点,但它不是按钮而是遮罩层,处理方式不是点击而是直接移除。
下面是展开部分的代码逻辑,这个我实测下来基本能覆盖常见页面:
javascript复制// 展开折叠代码块:优先模拟点击,再兜底移除遮罩
function expandCodeBlocks() {
var selectors = [
'.hide-article-box',
'.btn-expand-code',
'.code-expand-btn',
'[class*="code-expand"]',
'[aria-expanded="false"]'
];
selectors.forEach(function (selector) {
var nodes = document.querySelectorAll(selector);
nodes.forEach(function (node) {
if (typeof node.click === 'function') {
node.click();
} else {
node.style.display = 'none';
}
});
});
// 移除文章限高遮罩
var mask = document.querySelector('.hide-article-box, .article-hide, [class*="hide-article"]');
if (mask) mask.remove();
}
这里有个细节:node.click() 是模拟真实点击,会触发页面里绑定的所有事件监听器,效果和手动点击完全一致。但有些"展开"逻辑是调整内联样式而不是绑定事件,所以还得有一个兜底方案——把元素直接隐藏掉或者把它的高度限制去掉。我在脚本里用 style.display = 'none' 和移除遮罩两种方式做了双保险。
2.3 去水印:水印有三种,处理方式各不相同
水印是这几个问题里最需要技术判断的一个,因为它的实现路径足足有三种。
第一种是普通DOM节点水印。页面上直接挂了一个 div,里面是版权文字或者标记,可能是半透明浮层,也可能是固定在某个角落的块级元素。这种最好办,直接查找到节点然后移除。查找时我用了通配匹配方式,[class*="watermark"]、[id*="watermark"]、[class*="water-mark"] 都扫一遍,防止类名微小差异导致漏网。
第二种是canvas绘制水印。页面里插入一个全屏的canvas元素,水印内容由JS绘制上去。移除DOM节点没用,因为canvas本身就是一个已经画好的图层。处理方式是把canvas元素找出来,根据尺寸判断它是不是水印层,然后移除。判断依据是全屏水印的canvas宽度通常是100%视口宽度,而正常代码里用的canvas(比如图表、流程图)往往有特定尺寸。当然这个判断不完美,所以我加了一个保护逻辑:只移除位置固定且尺寸超过视口宽度一半的canvas,避免误伤文章里的图表。
第三种最阴险,是打印样式表里的伪元素水印。页面正常显示的时候看不到水印,但一旦触发打印,CSS 的 @media print 规则里通过 body::after 或类似的伪元素动态生成水印。这种情况下,即使你把DOM里的水印节点删干净,打印预览里还是会冒出来。处理方案是额外注入一段覆盖样式,把伪元素直接禁用。
2.4 打印样式重置与自动触发print
展开代码、清完水印之后,还差最后一步:把打印样式调好。
CSDN 页面的布局决定了直接打印会把大量无关模块塞进来,所以脚本里要注入一条自定义的 @media print 规则,把这些模块全部隐藏,同时把正文容器的宽度拉到100%。这一步的价值在于:它彻底改变了"打印出来是什么样",否则前面做了再多功夫,打印结果还是乱糟糟的。
自动触发 window.print() 也不能太急。折叠代码展开后,页面需要一点时间重新渲染,如果立刻调起打印对话框,可能拍到的是"正在展开但还没展开完"的中间态。我在脚本里加了一个延迟,800毫秒到1000毫秒比较稳妥。
2.5 核心代码实现(附完整脚本)
把上面几部分拼起来,就是一个最小可用的清洗打印脚本。我贴的版本是可读版本,方便你看逻辑;实际做书签脚本的时候需要把换行和注释都去掉,压缩成一行再塞进收藏夹。
javascript复制(function () {
var wait = function (ms) {
return new Promise(function (resolve) { setTimeout(resolve, ms); });
};
// 1. 展开折叠代码块
var expandSelectors = [
'.hide-article-box',
'.btn-expand-code',
'.code-expand-btn',
'[class*="code-expand"]',
'[aria-expanded="false"]'
];
expandSelectors.forEach(function (selector) {
document.querySelectorAll(selector).forEach(function (node) {
if (typeof node.click === 'function') { node.click(); }
else { node.style.display = 'none'; }
});
});
var mask = document.querySelector('.hide-article-box, .article-hide, [class*="hide-article"]');
if (mask) mask.remove();
// 2. 清除DOM水印节点
var watermarkSelectors = [
'[class*="watermark"]',
'[class*="water-mark"]',
'[id*="watermark"]',
'[id*="water-mark"]'
];
watermarkSelectors.forEach(function (selector) {
document.querySelectorAll(selector).forEach(function (node) {
node.remove();
});
});
// 3. 清理canvas水印层(只处理全屏水印,保护正常图表)
document.querySelectorAll('canvas').forEach(function (canvas) {
var rect = canvas.getBoundingClientRect();
if (rect.width >= window.innerWidth * 0.5 && rect.height >= window.innerHeight * 0.5) {
canvas.remove();
}
});
// 4. 注入打印样式
var style = document.createElement('style');
style.textContent = [
'@media print {',
' .toolbar, .navbar, .sidebar, .aside, .recommend-box,',
' .comment-box, .article-banner, .login-mark, .csdn-side-toolbar {',
' display: none !important;',
' }',
' #mainBox, .article_content, .main, .blog-content-box {',
' width: 100% !important;',
' margin: 0 !important;',
' padding: 0 !important;',
' }',
' body::before, body::after, #app::before, #app::after {',
' display: none !important;',
' content: none !important;',
' }',
' pre, code {',
' white-space: pre-wrap !important;',
' word-break: break-all !important;',
' }',
'}'
].join('\n');
document.head.appendChild(style);
// 5. 等待页面重排后触发打印
wait(800).then(function () {
window.print();
});
})();
需要说明的是,这个脚本在选择器层面依赖CSDN当前通用的HTML结构,如果页面改版导致选择器失效,需要配合控制台重新确认类名。这一点在后面的实测部分我会详细讲排查方法。
3. 手把手实操:从零开始把"清洗打印"书签用起来
3.1 在浏览器里把脚本变成书签
把上面的脚本变成可用的书签,只需要三步。
第一步,把代码压缩成一行。我实际操作的时候是先把注释和多余换行去掉,再把整个文件内容复制到输入框里,按Ctrl+A、Ctrl+J把多行合并成一行。压缩完成的代码会非常长,但这没关系,书签的URL长度在主流浏览器里是足够容纳这么长一串JS的。
第二步,在浏览器收藏夹里新建一个书签,名称填"CSDN清洗打印",URL栏填 javascript: 后面紧跟着压缩后的完整脚本。注意 javascript: 后面不要有换行,不要有空格,直接拼接脚本内容。
第三步,把这个书签拖到书签栏。不同浏览器的收藏夹管理略有差异,但核心操作都一样:新建书签、粘贴代码、拖到工具栏。
这里有一个非常容易踩的坑:很多浏览器在粘贴 javascript: 开头的地址时会自动帮你改成 https:// 或者拦截掉,这是安全策略在作怪。解决办法是先把 javascript: 拼好,如果粘贴后被吞,就把书签的URL再编辑一次,手动补上 javascript: 前缀。Chrome和Edge我都遇到过这种被自动清理的情况,多编辑一次就稳定了。
3.2 实测流程:从CSDN打开文章到PDF落地
书签准备好之后,完整的实际操作流程是这样的:
- 打开一篇CSDN技术文章,等待页面加载完成。
- 点击书签栏里的"CSDN清洗打印"。
- 页面会轻微跳动一下,脚本依次执行展开代码、移除水印、注入打印样式。
- 大约1秒后,浏览器的打印对话框弹出。
- 在打印对话框里,目标打印机选"另存为PDF"。
- 如果正文里包含深色背景的代码块,建议勾选"背景图形"选项,否则代码块的深色背景会丢失,变成白底黑字。
- 点击保存,得到一个干净的PDF文件。
我测试过一次典型的操作,一篇文章包含14个代码块、4张图和3个水印节点,脚本跑完后PDF从原来的17页缩减到9页,代码完整、水印消失、正文排版清爽。这个效果和之前手动操作相比,差距是肉眼可见的。
3.3 打印对话框的几个关键设置
很多人卡在"脚本执行了但PDF不理想",其实不是脚本的问题,是打印对话框里的设置不对。我有几个常用的设置组合,直接列在这里供参考。
纸张大小选A4,这是默认值,不用改。边距建议选"默认"或者"无",如果你希望内容充分利用页宽,选"无"效果最好,但要注意代码块可能会挤得很满。最关键的设置是"更多设置"里的"背景图形"选项,这个选项在Chrome系浏览器里默认是不勾选的,如果不打开,代码块里用CSS背景渲染出来的高亮色块、文章里的深色分隔线全部会消失。还有一个容易被忽略的是"双面打印"选项,如果只是存PDF,不影响;如果要打纸质版,建议关闭双面打印,因为技术文章经常要对照代码看,单面更方便做批注。
3.4 在Edge和Chrome里的差异小记
这个脚本我在Edge和Chrome上都跑过,核心功能完全一致,但有两个小差异值得提一下。
第一个是打印对话框的触发速度。Edge在调用 window.print() 时,打印预览打开得比Chrome稍慢,看起来像是"卡了一下",其实只是预览界面在渲染。不用慌张,等一下就好。
第二个差异是背景图形的默认设置。Chrome打印对话框打开后默认不勾选"背景图形",Edge的默认行为在不同版本里不太一样,有的版本会继承上一轮的设置。所以如果你在Edge里发现上次勾选过,这次打开可能还是勾选状态,这不算bug,是浏览器记忆了你的偏好。
4. 实测翻车记录:代码折叠失效与水印残留的完整排查链路
4.1 翻车1:脚本点了没反应,代码还是折叠的
第一次写完脚本,我满心欢喜地打开一篇长文章测试,结果一点书签,代码还是折叠状态,脚本仿佛没执行。当时的第一反应是"选择器写错了",于是打开开发者工具检查CSDN页面的DOM结构,发现"展开"按钮的类名确实是 hide-article-box,但页面里这个元素是存在于一个更低层级的容器里的,而且它绑定的事件不是原生click,而是框架里的合成事件。
问题出在 node.click() 上。原生 click() 方法只能触发原生事件,对某些框架的合成事件监听器无效。但奇怪的是,过了一版之后我再测试,原来的写法又生效了。这说明CSDN不同文章模板的处理方式不一样,有的模板可以靠原生click解决,有的不行。
我的应对方案很简单粗暴:把展开逻辑从"模拟点击"改成"直接操作样式"。既然代码块完全展开后,元素的 style.maxHeight 会从固定像素值变成 none,那我干脆就把所有可疑元素的内联 maxHeight 直接清掉,同时把 overflow 改成 visible。这样绕开了框架事件绑定,从DOM层面的效果上说和点开按钮完全一致。最终版的脚本里我保留了 click() 作为一个优化选项,但更依赖样式的强制清除。
4.2 翻车2:预览干净了,打印稿的水印又回来了
这个坑最折磨人。第一次运行脚本,一切正常,水印在打印预览里也消失了。我开心地保存了PDF,结果打开PDF一看,水印又出现在右下角。当时我的第一反应是"脚本是不是没跑完就触发了打印",于是把延迟从800毫秒加到了1500毫秒,重测,还是不行。
后来我才意识到,CSDN 的水印实现方式比我想象的复杂。它的页面里有一层canvas水印,但同时它的打印样式表里还有一段 @media print 规则,通过 body::after 生成另一层水印。我清掉了canvas水印,但伪元素水印是每当你进入打印流程时才会渲染的,预览界面里甚至看不到,只有真正输出到PDF或者打印机的瞬间才出现。
这个问题让我意识到,去水印不能只靠"删节点"这一招,还得主动覆盖掉打印样式表里可能存在的伪元素。于是我在脚本里加了一条覆盖规则,把 body::before、body::after 以及整个页面里常见的 #app::before、#app::after 全部禁用。这里有一个更全面的思路:可以扫描页面里所有样式表,找出 @media print 规则中设置 content 的伪元素并禁用,但这样会打开一个复杂的"对抗CSS"的坑。实测下来,直接覆盖几个常见的伪元素选择器已经足够应对当前CSDN的版本。
4.3 翻车3:代码长行被切断,打印出来没法看
水印问题解决之后,我开始检查打印出来的代码质量,发现一个没有处理过的新问题:有些代码行太长,在PDF里被硬生生切断,后半行直接丢到页面边缘,完全没法阅读。
这个问题的根因是代码块容器在打印模式下默认 white-space: pre,浏览器为了保证代码不换行,把超出页宽的部分直接裁掉了。在屏幕上看滚动条还能看到后面的内容,但打印到纸上,看不到了就是看不到了。
解决办法是在打印样式里给 pre 和 code 设置 white-space: pre-wrap 和 word-break: break-all。前者让长行在必要的空格处换行,后者在实在没法拆的时候按字符折行。这样处理后的代码虽然会失去原来的"一行到底"的排版,但至少所有内容都能完整出现在纸面上,比被裁掉强得多。不过要注意,加了 word-break: break-all 之后,字符串类型的标识符可能被拦腰拆开,这是折中方案,阅读体验略受影响但信息不丢失。
4.4 排查工具与通用排查链路
经过这几次踩坑,我总结了一套页面清洗脚本的通用排查链路,遇到"脚本没生效"或者"效果不对"的情况,按这个顺序排查基本能定位到问题。
第一步,打开开发者工具,切换到Elements面板,确认目标元素是否存在以及类名是否和脚本里写的一致。很多脚本失效的根本原因是站点改版或模板更新,类名变了,脚本自然失效。
第二步,在Console里手动执行 document.querySelectorAll('选择器'),看返回的节点数。如果是0,说明选择器没匹配到任何元素,需要换选择器;如果大于0,说明节点存在,问题出在脚本后续逻辑上。
第三步,用"检查元素→事件监听器"面板看目标元素绑定了哪些类型的事件。如果发现面板里显示的不是原生click而是合成事件,就要考虑脚本里的 click() 是否真的被页面逻辑响应了。
第四步,临时在脚本里加一些 console.log 输出,观察每一步的执行结果。这种方法最笨但最有效,尤其是在处理那些只在打印时才出现的伪元素水印时,可以输出 getComputedStyle(document.body, '::after').content 确认水印内容是否还存在。
我把这个排查链路整理成表格,方便对照:
| 症状 | 可能原因 | 排查方法 | 解决方向 |
|---|---|---|---|
| 代码未展开 | 选择器失效或事件绑定方式特殊 | 控制台检查元素是否存在、监听器类型 | 修改选择器,或直接操作样式属性 |
| 预览干净但PDF有水印 | 打印样式表用伪元素生成水印 | 检查 @media print 规则里的 ::after |
注入覆盖样式禁用伪元素 |
| 代码长行被截断 | 代码块 white-space: pre 导致溢出裁剪 |
打印预览里放大检查 | 设置 pre-wrap 和 word-break |
| 打印多了多余页面 | 侧边栏、推荐位等模块未隐藏 | 打印预览里看多出的空白页 | 完善隐藏模块的CSS选择器 |
5. 更进一步:从手动一键到自动清洗的进阶思路
5.1 接入用户脚本管理器:打开文章自动清洗
书签脚本用了一段时间之后,新的问题冒出来了:每次打开文章都得手动点一下书签,次数多了还是觉得烦。于是我想把"点击才触发"升级成"打开文章就自动触发",这就需要一个能挂在页面加载时机上的工具。
用户脚本管理器(国内用得比较多的是Tampermonkey)正好解决这个问题。把清洗逻辑包装成一段用户脚本,设置匹配规则为 https://blog.csdn.net/*,这样每次打开CSDN文章页,脚本会在页面加载完成后自动执行。相比书签脚本,用户脚本的好处是不用每次手动触发,而且可以通过 @run-at document-idle 控制执行时机,保证在页面基本渲染完成之后再动手。
不过自动触发也会引入新的问题:如果你只是想在浏览器里正常阅读,脚本已经自动把代码展开了、水印去掉了,虽然无伤大雅,但偶尔会导致页面样式和原版有差异。我的折中方案是:用户脚本只做"展开代码"和"隐藏水印"这两步,不自动调起打印窗口;真正需要打印时,再手动点书签脚本。这样既不影响日常阅读,又能在需要时有完整的打印能力。
5.2 多篇文章批量存档的尝试与效果
如果你有批量存档的需求,比如想把某个专题下的几篇文章一次性转成PDF,书签脚本逐篇点击显然太低效。
我试过两种批量方案。第一种是利用打印对话框的"保存"功能配合浏览器自动操作,但实测效果一般,因为打印对话框是浏览器原生弹窗,脚本没法直接控制它。第二种方案更靠谱一点:先把多篇文章的正文抓取合并成一个本地HTML文件,再用浏览器打开这个合并文件,用同样的清洗打印逻辑一次性导出PDF。这个方案理论上可行,但合并多篇文章的样式和资源比较折腾,我目前只是手动处理过两三篇的合并。
如果只是想要"没有水印且排版干净"的多篇文章存档,其实更稳妥的路线是用截图工具整页截图,或者配合一些浏览器扩展做整页截长图。截图方案不需要处理打印CSS,也不需要调打印对话框,生成的图片存档作为速览索引也够用了。
5.3 和导出PDF、转Markdown工具组合使用
做了这么多清洗工作,最后得说一句大实话:CSDN上真正值得长期保存的文章,最优存档格式不一定是PDF,可能是Markdown。
PDF适合作为"文章原貌"的存档版本,保留图片、代码高亮、页面排版。但我个人在长期使用过程中的体会是,PDF更适合阅读、批注,不适合二次编辑。如果这篇文章你要反复摘录代码、改注释、重组笔记,导出成Markdown再放到本地笔记软件里,效率会高得多。
现在市面上有不少把CSDN文章转成Markdown的在线工具和浏览器扩展,它们大多直接抓取渲染后的HTML,转换成Markdown语法。这类工具和我的清洗打印脚本组合使用效果很好:先用转MD工具拿到干净源码,再用清洗打印脚本把原版PDF留档。两条路线服务于不同的场景,互不冲突。
5.4 几个值得继续完善的方向
这个脚本目前已经能满足我90%的需求,但有几处还可以继续打磨。
一个是选择器维护。CSDN改版频繁,这类依赖类名的脚本迟早会失效,除非能在代码里用更鲁棒的方式定位目标元素。比如通过文本内容匹配"展开"按钮,而不是依赖类名。另一个方向是扩展到其他技术社区,比如博客园的"代码折叠+打印排版"问题也和CSDN类似,可以复用这套思路。
还有一个方向是水印处理的完整方案。我现在的方案是"清DOM节点+禁伪元素+删canvas",但如果目标站点用更高级的方式动态加水印,目前这个方案不一定都覆盖得到。好在CSDN目前的用法我已经全部踩过一遍,短期内这个脚本的稳定性是有保障的。
如果你也对这套思路感兴趣,完全可以基于我上面贴的完整脚本改成你自己的版本。技术上没有太多高深的东西,核心就是对DOM结构和打印CSS的理解,再加上一套"先排查、再动手、最后验证"的调试流程。动手做一次,这套前端"页面清洗"的思路就算彻底吃透了。
