当你知道的越多就越发现 DOM 这个看起来简单的东西其实是整个前端世界的地基。很多人在初学阶段把 DOM 当作“用 JS 改网页内容的工具”等面试被问到“虚拟 DOM 和 diff 算法”“事件冒泡、事件捕获、事件委托”时才发现自己连 DOM 的边界都没划清。这篇文章我准备一次性讲透把 DOM 从是什么、怎么构建到怎么渲染、怎么操作再到虚拟 DOM 和事件机制这些高频考点全部串起来适合刚入门的前端也适合准备面试但想系统复盘一遍的同学。先交代一句DOM 全称叫 Document Object Model中文是“文档对象模型”。它不是一门语言也不是浏览器的某个具体功能模块而是浏览器暴露给脚本的一棵可操作的内存结构树。你写的 HTML 只是文本真正让 JavaScript 能“看见”页面元素并修改它们靠的全是这棵 DOM 树。1. 拆开缩写的三个词文档、对象、模型凭什么组成一门技术1.1 文档、对象、模型三个词到底是什么意思很多人只记住了“DOM 是操作页面的接口”但这三个词其实精准描述了它存在的方式。“文档”指的是 HTML 文档本身也就是你在编辑器里写的那一串标签文本。浏览器拿到这串文本之后会把它解析成结构化的数据。注意这里的“解析”很关键因为文本本身没有任何结构化能力div和span谁是父子关系必须要通过语法分析才能确定。“对象”指的是内存中的一个个节点实例。每个 HTML 标签、文本、注释、属性在 DOM 里都是一个“对象”这些对象有自己的属性和方法。比如一个div标签在 DOM 中是HTMLDivElement的实例它继承了HTMLElement、Element、Node这些原型链上的能力所以你才能调用appendChild()、querySelector()这类方法。“模型”指的是整个文档的结构被抽象成了一种“树模型”也就是节点之间的关系。树的根是document往下依次是html、head、body再往下一层层展开。DOM 提供了一整套对这个树模型进行读取、插入、删除、替换的 API浏览器会根据这棵树的实时状态决定要不要重新渲染。一句话总结DOM 是 HTML 文档在内存里的一种树形对象表示它既是数据的结构也是操作的入口。没有 DOMJavaScript 就是一门和网页毫无关系的语言。1.2 浏览器是怎么把 HTML 变成 DOM 树的这个过程值得仔细讲因为它决定了你对“网页渲染”的很多判断。浏览器拿到 HTML 字节流之后第一步是字节解码把网络传输过来的字节按编码规则转成字符比如 UTF-8 解码成可读的文本。第二步是词法分析也叫 tokenization把字符串拆成一个一个的 token比如开始标签div、属性classbox、文本内容、结束标签/div这些 token 都有类型标记着它们在文档中的角色。第三步是语法分析也就是构建节点树浏览器按照 HTML 规范里定义的规则把 token 组装成嵌套的节点。这里有个很有意思的细节HTML 解析并不是“洗干净之后一次性建树”而是一个边读边建的过程。浏览器维护着一个“解析器状态机”读到开始标签就创建一个元素节点并压入栈读到文本就追加到当前节点的文本内容里读到结束标签就出栈。碰到标签嵌套错误比如pdiv/p/div解析器不会直接报错而是按照规范里的“错误处理规则”去修正自动补全或者重组结构。原因在于 HTML 是一个“宽容”的语言。浏览器厂商设计解析器时的共识是网页不能被语法错误彻底卡死能修就修保证用户永远都能看到内容。所以你在浏览器里看到的 DOM 结构未必和你编辑器里写的源码一字不差它已经是“修正优化”过的一版。1.3 DOM 和 HTML、CSSOM 的关系别再混为一谈初学者最容易搞混的一件事是把“HTML 文本”直接当成“DOM”。其实二者是源数据与结构化表达的关系。HTML 是静态的刷新之后它会重新解析一遍DOM 是动态的JS 改了它页面上的内容立刻变化但这个变化并不会回写到 HTML 源文件里刷新页面之后又恢复原样。还需要分清 DOM 不是仅有的“对象模型”浏览器里还有一个和它并列的结构叫 CSSOM也就是 CSS 对象模型。CSSOM 是浏览器解析 CSS 文本生成的样式规则树它负责回答“这个元素最终该长什么样”。渲染时浏览器会把 DOM 和 CSSOM 合并成渲染树只有渲染树上能够影响视觉布局的节点才会被真正绘制。display: none的节点会从渲染树里被剔除但它依然在 DOM 树里。这就是为什么getElementById依然能找到那个元素只是因为样式隐藏所以看不见而已。我把 DOM、CSSOM、渲染树三者的关系用一张表对给你结构来源职责是否直接决定视觉DOMHTML 解析描述页面结构和内容间接CSSOMCSS 解析描述样式规则和层叠关系间接Render TreeDOM 与 CSSOM 合并提供布局与绘制的节点集合直接理解了它们之间的差别后面的渲染性能问题就很好解释了。2. 浏览器渲染流程与“从上到下”顺序渲染的真相2.1 从 HTML 到界面浏览器到底做了几次工渲染流程可以概括为五个大阶段解析、样式计算、布局、绘制、合成。解析阶段生成了 DOM 和 CSSOM这一点前面已经说过。样式计算阶段浏览器会根据 CSSOM 的规则为 DOM 树里的每个可见节点计算出最终的样式值这个阶段会处理继承、层叠、单位换算比如把em、vh等相对单位换算成像素。布局阶段也叫 reflow浏览器会计算每个元素在页面上的几何位置和尺寸包括它相对于父容器和兄弟节点的偏移量。绘制阶段把每个节点的背景、边框、文字、阴影等视觉属性绘制到多个图层上。合成阶段浏览器把各个图层按正确的层级顺序交给 GPU 合成最终显示在屏幕上。这里有一个重要判断上面说的是“一次完整渲染”的流程但真实页面的首次加载往往不是一次性的。HTML 是边解析边构建 DOM 的只要解析器先读完了head里的内容并且 CSS 也没有阻塞浏览器就可能先绘制出第一屏的内容然后继续解析主体部分。这种机制叫做渐进式渲染是浏览器特意为了“最早时间看到内容”而设计的。2.2 “DOM 从上到下顺序渲染是不是更快”答案没那么简单很多开发者问DOM 从上到下按顺序渲染是不是就更快这个说法有一定道理但远没有“顺序决定一切”那么绝对。说它有道理是因为浏览器的 HTML 解析器确实是按照字节流的顺序从上到下扫描、解析、构建节点的。按理说HTML 顶部的内容总是会先被解析到理论上也会先出现在渲染结果里。但真实情况受两个关键因素影响CSS 和 JavaScript。CSS 会阻塞渲染。浏览器只有在解析完样式表之后才能进行样式计算所以你如果把style或者外部样式表放在页面底部浏览器会先解析出一部分 DOM却发现没有样式规则可以做计算最终在布局之前停下来等待。等到 CSS 到了它再重新计算。这反而拖慢了首屏速度。因此业界惯例是把 CSS 放在head里让样式尽早就位。JavaScript 会阻塞解析。当解析器碰到script标签时按照早期 HTML 规范它必须停下来等脚本执行完因为脚本里可能调用了document.write()来修改文档流。阻塞期间DOM 构建暂停渲染也就停住。为此我们才把普通脚本放在/body前或者给脚本加defer和async。回到“从上到下顺序渲染是不是更快”这个问题比较准确的回答是从上到下顺序解析确实让渐进式渲染成为可能看起来是“边读边画”但实际速度取决于有多少渲染阻塞资源。顺序本身不是性能瓶颈瓶颈在于你的 CSS 和脚本放置策略是否让解析器“空转等待”。2.3 回流与重绘为什么操作 DOM 慢就慢在这里前端圈子里有一句老话操作 DOM 是最贵的操作。贵就贵在当你修改 DOM 时浏览器可能被迫重新执行“布局”甚至“绘制”。回流Reflow指的是布局阶段被重新触发。当你改变元素的尺寸、位置、内容、字体大小或者增删节点时浏览器需要重新计算受影响区域里所有元素的几何属性。最糟糕的情况是修改了根节点附近的结构整个页面都重新布局。比如改body的宽度所有子元素的排列都要重新算一遍。重绘Repaint指的是绘制阶段被重新触发。当你只修改颜色、背景、阴影这些不影响布局的属性时浏览器不需要重新算几何只需要把像素重新画一遍。重绘的代价虽然比回流小但也不是免费的因为浏览器还是要遍历图层、计算绘制指令。在实际开发里常见的“慢”是回流和重绘交叠出现。比如你用一个循环去逐个插入列表项每插入一项都可能触发一次回流几十上百次循环下来性能直接崩。解决思路很清晰把 DOM 操作合并减少触发次数我推荐以下几种做法批量读取再批量写入不要在读写之间交替触发布局。使用DocumentFragment先构建子节点再一次性插入。用display: none先隐藏元素操作完再显示只触发两次回流而不是 N 次。动画里优先用transform和opacity因为它们能走合成层不触发布局和绘制。2.4 一套可以直接落地的渲染性能优化清单这部分是我在实际项目中整理出来的一般项目照着做都不会差把 CSS 放在head保证样式尽早参与渲染。把 JS 放在/body前或使用defer/async避免阻塞。减少 DOM 嵌套层级特别是深层无序的嵌套会拖慢样式计算速度。用classList切换多个样式类不要一条一条地改style属性。避免频繁读取offsetWidth、offsetHeight、getComputedStyle等布局属性它们在读取时会强制刷新布局队列。利用事件委托减少事件监听器的数量。数据列表用虚拟滚动只渲染可见区域控制 DOM 节点总量。这些优化背后其实都是同一个原则让浏览器少干活。DOM 操作本身不慢慢的是你可能不小心让浏览器重复干了很多不必要的活。3. 日常开发里高频用到的 DOM 操作技法3.1 一张表看懂查、增、改、删四类操作DOM 提供的 API 非常多但日常开发真正高频用到的不外乎“查、增、改、删”四类。我把它们整理成表顺手标注了每个方法的特点和使用场景。类型常用 API说明注意点查询document.getElementById()按 id 找单个元素老牌 API性能极佳查询document.querySelector()按 CSS 选择器找单个元素方便但比 getElementById 慢查询document.querySelectorAll()按选择器找一组元素返回静态 NodeList查询element.closest()从自身向上找符合选择器的祖先事件委托里的神器新增document.createElement()创建元素节点需配合 append 使用新增element.insertAdjacentHTML()在指定位置插入 HTML慎用涉及 XSS 风险新增element.append()/prepend()尾部/头部添加子节点支持多节点和字符串修改element.textContent设置或读取文本内容设置时自动转义标签修改element.setAttribute()设置属性可直接赋值部分属性修改element.classList操作 class比操作 className 更方便删除element.remove()直接移除元素兼容性没问题删除element.replaceWith()把当前元素替换成另一个节点一步到位避免手动删再插一个值得强调的点是textContent和innerHTML的区别。textContent会把内容当作纯文本插入标签会被自动转义成普通文本innerHTML则会把字符串当作 HTML 解析。前者安全后者在数据可控的情况下才建议用。3.2 批量插入的正确姿势DocumentFragment 与一次性重绘如果你需要在页面里添加一个包含 100 个列表项的ul最糟糕的写法是循环 100 次每次都创建节点并appendChild这样触发的回流次数可能上百次。正确的姿势是利用DocumentFragment做一个“离线容器”。const ul document.querySelector(#list); const fragment document.createDocumentFragment(); for (let i 0; i 100; i) { const li document.createElement(li); li.textContent 第 ${i 1} 项; fragment.appendChild(li); } ul.appendChild(fragment);DocumentFragment是一种特殊的节点它可以临时存储子节点但不会成为真实 DOM 的一部分因此你对它的所有修改都不会触发回流。当你把它一次性塞进ul时浏览器只执行一次插入操作DOM 更新也就只触发一次回流。实测下来这种写法在渲染上百条数据时性能提升非常明显。这里要住的细节是fragment.appendChild(li)之后li会暂时从 fragment 里“脱离”但当你把fragment插入ul时fragment里的所有子节点会自动转移到ul下fragment自己则变回空状态。这是大部分前端都不太注意面试里偶尔会被问到的一个小考点。3.3 动画场景里绕开“布局抖动”的实战技巧我自己在写复杂动画时踩过好几次坑最典型的是“布局抖动”引发的掉帧。布局抖动指的是你在同一个函数里既读取了布局属性又修改了 DOM 属性导致浏览器被迫反复执行同步布局。举个例子// 问题代码读写交替触发多次回流 for (let i 0; i items.length; i) { const width items[i].offsetWidth; // 读取 items[i].style.width width 10 px; // 写入 }正确做法是把读取和写入分成两个阶段。先读所有需要的布局数据存到数组里再统一写入。// 优化代码先读后写 const widths []; for (let i 0; i items.length; i) { widths.push(items[i].offsetWidth); } for (let i 0; i items.length; i) { items[i].style.width widths[i] 10 px; }动画循环里也是一样的道理。如果你在requestAnimationFrame回调里既读取又写入浏览器没办法把多个帧的布局工作合并起来动画就卡顿。更推荐的做法是用transform来实现位移和缩放动画因为 transform 只触发合成层的更新走 GPU 加速完全绕开布局和绘制阶段。4. 事件模型冒泡、捕获、委托一网打尽4.1 DOM 事件流的三个阶段别再只背一个“捕获”几乎每个前端面试都会问到事件流。完整的事件流分为三个阶段捕获阶段、目标阶段、冒泡阶段。你点击页面上一个按钮时事件的完整传播路径是这样的从window出发经过document再到html然后是body一路向下到目标元素的外层容器最终到达目标元素本身。这个“从根到目标”的下行过程就是捕获阶段。命中目标元素的那一刻事件处于目标阶段事件监听器开始在目标上执行。紧接着事件沿原路返回——从目标元素一层层向上传播——这个过程就是冒泡阶段。给你看一个直观的例子。假设结构是body div button你点了button事件传播顺序是window - document - html - body - div - button目标阶段 - div - body - html - document - windowaddEventListener的第三个参数控制监听器在哪个阶段触发。默认是false表示监听器在冒泡阶段触发传true表示在捕获阶段触发。const div document.querySelector(div); // 冒泡阶段触发 div.addEventListener(click, () { console.log(冒泡阶段); }, false); // 捕获阶段触发 div.addEventListener(click, () { console.log(捕获阶段); }, true);面试的时候能够完整说出这两个阶段的区别并且清楚第三个参数的默认值就比大部分只会说“事件冒泡”的候选人强很多。4.2 冒泡与捕获的实际差异从代码出发理解写页面的时候为什么我们常常对“冒泡”讨论得更多因为实际开发里绝大多数监听器都挂在冒泡阶段它的行为更符合直觉事件从目标往上走父容器能统一收到所有子元素的事件。捕获阶段用得少但它在某些场景下很关键。比如你想在所有子元素之前拦截某个事件就应该在捕获阶段监听。一个常见需求页面里有一个列表你希望点击任何区域的空白处都收起菜单但不希望点击菜单本身时也触发收起。你可以在document的捕获阶段判断目标是不是菜单内部的元素提前决定要不要阻止它。document.addEventListener(click, (e) { if (!menu.contains(e.target)) { menu.hide(); } }, true);这里第二个参数true必不可少。因为你如果写在冒泡阶段事件里那些子元素上的监听器可能已经把数据修改掉了或者已经执行了某些业务逻辑。捕获阶段监听能让你的“兜底逻辑”最早介入。顺带提一下两个容易混的方法stopPropagation()阻止事件继续传播冒泡阶段被调用后父元素不会再收到该事件。stopImmediatePropagation()不仅阻止传播还阻止当前元素上其他监听器的执行。preventDefault()阻止浏览器默认行为比如阻止跳转链接但它不会阻止事件传播。三者的边界一定要分清楚面试时会被追问。4.3 事件委托最经典也最常被问到的性能技巧事件委托的核心思路其实很简单因为事件有冒泡机制子元素的点击事件会冒泡到父元素所以我们可以只在父元素上绑定一个监听器通过判断event.target来处理任意一个子元素的事件。经典场景是动态列表列表项由接口返回用户也能随时新增项如果给每一项都绑定监听器新增时还得重新绑定非常麻烦。事件委托只需要在容器上绑定一次document.querySelector(#list).addEventListener(click, (e) { const item e.target.closest(.list-item); if (!item) return; item.classList.toggle(selected); });这段代码里有几个关键点。第一e.target是真正被点击的元素可能是.list-item里的某个子元素所以要用closest()从它自己向上找到.list-item这个祖先。第二closest()找不到时返回null所以要提前return掉。第三事件委托天然覆盖“未来新增的节点”这是它最值钱的地方。面试时如果被问到“事件委托的优点”你可以这样答减少内存占用因为监听器数量少提高绑定效率一次性绑定代替大量循环支持动态节点新增元素不需要重复绑定。如果被追问“缺点”也值得说清楚事件委托依赖事件冒泡focus、blur这类不冒泡的事件没法直接委托但可以用focusin、focusout代替。5. 虚拟 DOM 与 diff 算法面试高频考点拆解5.1 虚拟 DOM 到底是虚拟了什么很多面试者把虚拟 DOM 背成“用 JS 对象模拟 DOM”但为什么需要这样一个模拟结构真的理解的人不多。核心原因是直接操作真实 DOM 代价高。真实 DOM 节点包含大量的属性一个普通的div节点上可能有几十上百个内置属性和方法频繁创建、比较、更新它们是很重的。而虚拟 DOM 用一个极简的 JS 对象描述节点信息它只记录标签名、属性、子节点等关键数据相比真实节点轻得多。// 一个最简单的虚拟 DOM 节点结构 const vnode { tag: div, props: { id: app, class: container }, children: [ { tag: h1, props: {}, children: [标题] } ] };渲染流程是这样的组件运行 render 函数生成新的虚拟 DOM 树框架把新树和旧树作 diff 比较计算出差异集合框架只把差异同步到真实 DOM 上而不是直接重建整棵树。这样一来即使页面发生大量状态变化真实 DOM 的操作次数也被压到了最小。虚拟 DOM 最大的价值其实不是“比手写 DOM 操作更快”而是它把“描述页面状态”和“更新真实 DOM”这两个过程解耦了。开发者只需要描述页面“应该长什么样”至于怎么高效地更新交给框架处理。这种抽象让跨平台成为可能因为你可以在不同平台上用同一套描述逻辑渲染到 DOM、Canvas、甚至原生组件。5.2 Diff 算法核心用“最小差异”换可接受性能diff 算法要解决的问题是给定两棵虚拟 DOM 树如何找到它们之间最小的更新差异理论上两棵树的最小差异比较是一个非常复杂的算法时间复杂度会高到不可接受。所以 Vue 和 React 都采取了一个“牺牲理论最优、换取实际可用”的策略只在同一层级做比较不跨层级移动节点。这基于一个实践中几乎总是成立的假设DOM 节点不会无端地从一处被移到另一处跨层级的操作是极少数。在同层比较时框架会逐个对比新旧节点的tag和props。如果节点类型相同就继续深入地比较子节点如果节点类型不同直接把旧节点整个替换掉不再花时间比较它的子树。这种“同层比较 类型不同即替换”的规则把 diff 的时间复杂度压到了接近 O(n)。为了让重复节点能被复用框架引入了key。key是节点在列表中的唯一标识当新旧列表结构相似但顺序发生变化时diff 算法可以通过key判断出哪些节点是“同一个”然后执行移动而不是删除重建。这就是为什么在 Vue 的v-for列表里官方一直要求你绑定一个稳定唯一的key而不要用数组索引。5.3 React 和 Vue 的 diff 到底差在哪这个问题经常在面试中出现。从宏观上讲两者的思路一致都是同层比较 key 优化但微观细节有明显差异。Vue 2 的 diff 采用的是“双端比较”策略。它会同时从新旧两个数组的两端开始尝试用头部、尾部节点做比较优先处理“头头相同、尾尾相同、头尾相同”这几种常见情况把能复用的节点尽量找出来。这种策略在列表只是末尾新增项、头部新增项等场景里有明显优势能够用最少的移动次数完成更新。React 从较新的版本开始diff 的实现往“Fiber 架构”演化。它允许 diff 过程被拆分成多个小任务按优先级分片执行而不是一次性同步完成这样浏览器可以在 diff 过程中穿插处理用户输入和动画不至于因为长时间占用主线程而卡顿。如果你在面试时能说出这种层次差异就能给面试官留下“真读过源码级内容”的印象。但要记住面试官最关心的不是你能背多少细节而是你有没有理解虚拟 DOM 解决的问题通过最小化真实 DOM 操作让开发者的心智负担和框架的性能表现达成平衡。6. 真实的 DOM 世界安全、歧义与面试清单6.1 DOM 型 XSS不是网上随便说说那么简单“DOM 型 XSS”是 Web 安全领域的一个老话题和 DOM 有直接关系。它的原理是前端代码将不可信的外部数据直接插入到 DOM 中导致恶意脚本在页面上下文里被解析和执行。最常见的场景是使用innerHTMLconst name new URLSearchParams(window.location.search).get(name); document.querySelector(#welcome).innerHTML 欢迎回来${name};如果攻击者构造一个这样的链接https://example.com/?nameimg srcx onerroralert(document.cookie)那么innerHTML会把这个字符串当作 HTML 解析img标签被创建onerror里的脚本自动执行alert(document.cookie)弹出。这只是一个演示真正的攻击者可以窃取 Cookie、修改页面内容、伪装成登录框骗取密码。防御的核心原则是不要用 HTML 解析器处理不可信数据。登一条入textContent或innerText代替innerHTML数据就会被当作纯文本插入标签失去效果。此外服务端对输入做严格的校验和编码响应头配置正确的 Content-Security-Policy都能有效降低 DOM 型 XSS 的风险。面试时被问到 XSS能结合 DOM 操作讲清楚攻击链路并且主动说出textContent和innerHTML的本质区别就已经能体现你对 Web 安全的认知深度。6.2 别被同名坑了ArcMap、OSGB 里的“DOM”是另一回事这里我必须插个题外话因为在测绘和地理信息行业里“DOM”是一个被我很多朋友问过太多遍的同名缩写。在这个领域中DOM 指 Digital Orthophoto Model即数字正射影像模型它和前端文档对象模型完全不是一个东西。比如有人在用 ArcMap 10.8 给影像数据做切片、或者做本地切片时会搜索“DOM 切片”得到的自然是地理信息领域的结果。那里的 DOM 是指经过几何校正后、带有真实地理坐标的航空或卫星影像用来在地图软件里做底图或叠加分析和浏览器、JavaScript、HTML 没任何关系。还有一个常见混淆是 OSGB 和 DOM 的关系。OSGB 是 OpenSceneGraph Binary 的缩写是倾斜摄影测量中生成的三维模型格式带有完整的几何结构、纹理和层级细节而测绘领域的 DOM 是二维正射影像两者是不同类型的数据。在三维测图的工程流程里通常会把倾斜摄影生成的 OSGB 模型和正射影像 DOM 配合使用模型负责立体真实的城市面貌影像负责平面精度和纹理参考。它们是互补关系不是同一个东西。作为一个前端开发者碰到这种同名缩写最好先确认语境再动手查资料否则很容易被搜索引擎带偏。6.3 我总结的 DOM 面试复盘清单最后给准备面试的同学整理一份可以直接对着自测的清单。你能把下面这些问题不看资料回答出来DOM 这块基本就过关了说一说 DOM 全称以及文档、对象、模型分别代表什么浏览器是如何从 HTML 字节流构建 DOM 树的DOM 和 CSSOM、Render Tree 之间是什么关系CSS 为什么放在headJS 为什么放在/body前回流和重绘有什么区别哪些属性会触发回流为什么说频繁操作 DOM 很慢你有哪些减少回流的经验事件流分哪三个阶段addEventListener的第三个参数有什么作用事件委托的原理是什么为什么支持动态节点虚拟 DOM 解决了什么问题它的结构长什么样diff 算法为什么只比较同层key的作用是什么Vue 双端比较和 React Fiber 架构有什么本质区别什么是 DOM 型 XSS如何防御这些问题大多数都能在这篇文章里找到答案。如果还有含糊的地方建议把对应小节重读一遍然后亲手上手写几个 demo 验证一下。最后再说一点我的个人体会。DOM 这东西你可以用 jQuery 或者 Vue、React 很多年都用不到深层原理但它始终是面试的试金石也是排查页面性能问题时最终定位的底层逻辑。我见过太多候选人把“虚拟 DOM 比真实 DOM 快”这句话挂在嘴边但一问到回流重绘、事件流阶段就立刻露出破绽。别做那种“背结论、不会推导”的人。真正理解了浏览器解析、渲染、事件传播这条链路之后你会发现所有前端框架的设计都是围绕着 DOM 的高昂代价展开的。这份理解值得你收藏起来反复看。
企业数字化 ERP 产品动态
相关推荐
自动控制理论课后题结构化学习与验证方法 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 5:10:55
Highcharts Treemap自定义布局算法:从入门到实战 做数据可视化这些年,矩形树图(Treemap)是我觉得最容易被低估的一种图表。它用矩形面积表达数据权重,用嵌套关系表达层级结构,一眼就能看出“谁是大头、谁是小头”,尤其适合磁盘占用分析、销售构成拆解、预算… · 2026/9/25 5:10:55
Atlas 300V推理卡部署YOLO全流程:从模型转换到性能调优 1. 先搞清楚:Atlas 300V 24G到底算什么硬件1.1 它是不是运算加速卡?我直接给结论最近团队接了个边缘AI项目,要把YOLOv5目标检测模型从GPU服务器迁到华为Atlas平台上。群里有人直接甩了一句:Atlas 300V 24G是运算加速卡吗ÿ… · 2026/9/25 5:10:55
红蓝对抗演练实战指南:从红队攻击链路到蓝队防御闭环 简介:《红蓝对抗演练指南:企业攻防实战全流程拆解》是一份面向企业安全工程师、运维人员及信息安全学习者的实战型PDF,系统拆解红蓝对抗演练从前期筹备、实战攻防到总结复盘的完整流程。整个资源包仅包含1个PDF文件,体积约4.28MB&… · 2026/9/25 5:35:01
天融信TopRules网闸实战:物理隔离与数据摆渡详解 简介:天融信网络卫士TopRules技术培训PPT,由应用交付产品部张凌云于2012年7月主讲,面向网络安全工程师、网闸实施与运维人员,旨在帮助读者系统掌握安全隔离与信息交换技术。内容从隔离技术起源、协议隔离与防火墙区别讲起… · 2026/9/25 5:35:01
从URL签名到Secure Boot:常见signature错误排查全指南 1. 从一条带 signature 参数的外链说起:签名到底在保护什么前几天在整理从某个工业资料站拉下来的文档时,看到一个很有意思的条目:文件名是Технология и оборудование для производства и ремон… · 2026/9/25 5:35:01
Atlas 300V 24G实战:从驱动到YOLO推理的完整踩坑指南 Atlas 300V 24G这块卡最近讨论度是真的高。我后台收到最多的两个问题,一个是“Atlas部署YOLO到底怎么搞”,另一个更直接——“atlas 300v 24g 是运算加速卡吗”。这块卡我实际用了三周,从最开始连驱动都装不上,到后面把YOLOv5和YO… · 2026/9/25 5:34:55
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37