news 2026/10/6 19:06:37

JavaScript性能优化实战:从度量、定位到取舍的系统工程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript性能优化实战:从度量、定位到取舍的系统工程

看到“JavaScript性能优化”这个标题,很多人的第一反应是找几个代码片段抄一抄,比如把for循环改成while,或者给事件监听加个节流。但真正在项目里趟过坑的人都知道,性能问题从来不是单点技术能解决的,它是一个从“如何度量”到“如何定位”再到“如何取舍”的系统工程。这篇文章不打算给你罗列一堆网上一搜就有的优化清单,而是想从我实际经手过的几个项目出发,聊聊我这些年做JavaScript性能优化时真正用到的思路、工具和落地方法——从分析工具怎么用、指标怎么看,到代码层面怎么改、框架层面怎么调,再到线上问题怎么排查。内容会偏实战,适合那些已经能熟练写业务代码、但遇到卡顿和慢加载时感觉无从下手的同学。看完这篇不说能让你成为性能专家,但至少再碰到性能问题,你会知道从哪里下手、每一步该做什么。

1. 性能优化的起点:先搞清楚瓶颈在哪里

很多人做性能优化的第一个动作就是“改代码”,这其实是最容易走弯路的地方。我见过不止一个项目组,花了大力气把某个纯计算函数重写了三次,结果页面依旧卡顿,最后发现罪魁祸首是某个第三方插件在持续占用主线程。所以做性能优化,第一课永远是:先量化,再动手。

1.1 别凭感觉,用Performance面板和Lighthouse建立基准线

我接手任何一个性能优化项目时,第一件事就是跑一份基线数据。浏览器自带的 Performance 面板是最直接的度量工具,它能录制一段页面从加载到交互的完整行为,然后以时间线的形式展示每个阶段发生了什么。重点看这几个关键指标:FCP(首屏内容绘制)、LCP(最大内容绘制)、CLS(布局偏移)、TBT(总阻塞时间)以及FID(首次输入延迟)。这些指标直接映射到用户能感知的“快”与“慢”,比任何代码走查都更有说服力。

Lighthouse 则更适合做一次性的整体体检,它会给页面打个分并直接告诉你哪里有问题,比如Reduce JavaScript execution time、Avoid enormous network payloads这类建议。虽然这些建议比较泛,但作为起点已经足够。

在给一个后台管理项目建立基线时,我发现它的 LCP 时间高达 4.8 秒,而 Lighthouse 给出的主要问题就是主线程上有长达 2.3 秒的脚本执行时间。顺着 Performance 面板的时间线往下钻,发现有个图表组件在初始化时做了大量同步计算,占用了将近 800ms。这个定位过程不超过 10 分钟,但如果不上工具直接看代码,可能得花好几天。

1.2 用Performance面板找到主线程上的“压倒骆驼的稻草”

Performance 面板的核心用法其实是看主线程的火焰图。火焰图里每个长条代表一个函数的执行,颜色越深、条越长,说明这个函数越值得怀疑。你要关注的是那些呈现“长条形”且在Task里反复出现的项目,那基本就是主线程卡顿的来源。

操作上有个小技巧:录制时间不需要太长,5到10秒足够,但一定要覆盖到用户实际的交互路径,比如打开弹窗、切换Tab、输入搜索关键词。很多优化点只有在你模拟真实交互时才会暴露出来。另外记得在录制时关掉浏览器扩展,否则它们的执行时间也会被算进页面脚本里,误导判断。

我习惯配合window.performance.getEntriesByType("longtask")来抓长任务,这个方法能自动收集所有超过50ms的任务。要知道浏览器的主线程超过50ms的任务就会被判定为长任务,而长任务会阻塞用户交互。把这个数据上报到监控平台,就能在用户实际环境里发现那些测试环境永远复现不了的卡顿问题。

2. 代码层面的硬核优化:从运行机制到实战写法

有了度量工具和数据支撑,接下来才是真正动手改代码的环节。JavaScript 性能优化的代码层功夫,本质上是对两个运行机制的理解:事件循环(Event Loop)和V8 引擎的隐藏类与优化编译。理解了这两个机制,很多“为什么这样写更快”的问题就迎刃而解。

2.1 不懂事件循环,你的setTimeout优化可能都是错的

今时今日的前端开发,99% 的性能瓶颈都跟主线程被长时间占用有关,单独把一段同步代码改快 0.1ms 的意义并不大。事件循环机制的核心是:主线程上一个时间只能执行一个任务,所有耗时的操作如果都以同步方式执行,就会阻塞后面的渲染和交互。

真正有效的做法是利用事件循环的机制来重新编排任务优先级和执行时间:

  • 把非关键任务拆碎执行:一个大的同步任务可以拆成多个小任务,用setTimeout或requestIdleCallback分散到不同的宏任务里执行。这里的用意不是缩短总执行时间,而是让主线程能有间隔去处理用户的点击和渲染请求。
  • 用requestAnimationFrame做动画:动画相关的 DOM 变更尽量放在requestAnimationFrame回调里,浏览器会在下一帧渲染之前统一处理,避免因为强制同步布局导致的抖动。
  • 用微任务处理异步结果:Promise.then里的回调属于微任务,会在当前宏任务结束后立刻执行,适合用来处理对时间敏感的异步结果,比如状态更新。

我见过最典型的反例是在点击事件里直接做了几万条数据的遍历和字符串拼接,然后一次性插入 DOM。这本来应该拆成几十个小片段,每段用requestIdleCallback在空闲时间处理,用户感知就会完全不一样。这不是什么高深技巧,只是理解了事件循环后的自然选择。

2.2 对象访问模式、闭包与隐藏类:V8引擎的“潜规则”

V8 引擎为了加速属性访问,会为对象创建隐藏类(Hidden Class),并且在运行时尝试将函数优化编译成机器码。在这套机制下,代码的运行速度可能相差数倍:

  • 保持对象结构稳定:不要在实例化后再随意增删属性。V8 会根据属性顺序生成隐藏类,如果每次创建的属性顺序不一致,隐藏类就会不同,导致“去优化”甚至重新走解释执行。简单说,所有实例都该用同一个构造顺序,相同属性名。
  • 慎用delete操作符:delete obj.attr会让对象退出快速模式,V8 会把它降级成字典模式,访问速度会明显下降。如果你只是想把属性置空,直接赋值为null或undefined通常更划算。
  • 避免闭包带来的变量查找开销:闭包确实好用,但内部函数每访问一个外层变量,都要经过作用域链查找。高频调用的函数里,尽量把会反复使用的外层变量赋给局部变量,再在函数体内使用,能有效减少查找开销。

我记得刚入行时做过一个地图打点需求,几千个标记点每帧都要更新坐标。一开始直接在更新函数里遍历this.points[i].x,后来我改成把坐标数组单独拆出来,一次性赋值给局部变量再计算,帧率从 30 提到接近 60。这种优化靠“背代码技巧”是记不全面的,核心还是理解引擎的执行逻辑。

2.3 判断数据类型的正确姿势:一个被低估的性能细节

看到热搜词里有“javascript 判断数据类型”,我突然想多说两句。别小看这个日常操作,它在很多性能敏感场景下真的会拖后腿。

常见的判断方式无非typeof、instanceof、Object.prototype.toString.call()三种。很多人会告诉你typeof最快、Object.prototype.toString最准。但实际项目里,最慢的往往不是你选错了方法,而是你每个地方都独立写了一大段判断逻辑,导致这段代码在 V8 里无法被优化,因为函数太“多态”。

我的实践建议是:如果要高频判断数据类型,尽量写一个统一的isType函数,内部用Object.prototype.toString做一次判断后缓存结果。尤其像图表库、编辑器这类需要频繁处理输入数据的场景,把类型判断统一收口之后,不仅代码更简洁,执行路径也更稳定,V8 优化起来更友好。当然,typeof的适用场景(判断undefined、function、原始类型)天然很快,没必要杀鸡用牛刀。

3. 真实项目的性能优化实践:功能、工具、框架三管齐下

代码层的优化更多是“内功”,但真正让用户感知到性能变化的,往往是功能实现方式和工具选型上的调整。这一节我会拿几个具体场景说事,包括第三方库怎么选、事件和渲染怎么权衡,这些才是实战中占比最大的部分。

3.1 从FullCalendar到自研排期组件:第三方框架的性能边界

热搜词里的fullcalendar让我特别有感触。之前接过一个培训系统的排课功能,起初图省事直接用了 FullCalendar。功能确实强大,但一渲染 3 个月的课程表,浏览器就直接卡死。打开 Performance 面板一看,FullCalendar 初始化时要把所有事件转成内部模型,还要做大量 DOM 操作,两千多个事件在低端机器上根本扛不住。

后来我换了思路:不再依赖 FullCalendar 的完整渲染,而是只读取它的数据层,自己写一个轻量的网格组件。核心改动是这么几个点:

  • 只渲染可视区域:用虚拟滚动,只生成用户当前能看到的那几周 DOM 节点,上下滚动时动态替换。
  • 把事件数据处理提前:在拿到原始排课数据后,先做一次预处理,把所有冲突检测、时间格式化全部算完,渲染时只做纯展示。
  • 用DocumentFragment批量更新:哪怕是可视区域内的 DOM 更新,也先拼接好再整体插入,避免每次.appendChild都触发一次重排。

改完之后,同样 3 个月的数据,初始化时间从 4 秒降到 500ms 以内,滚动时的帧率也稳定在 55fps 以上。这个案例想说的是:性能优化很多时候不是把现有代码改快,而是砍掉那些你其实不需要的功能。全功能日历看着厉害,但如果你只需要一个排课表格,自研往往才是性能最优解。

3.2 事件处理的高级姿势:节流、防抖与事件委托的配合

事件性能问题是移动端 H5 最常见的卡顿原因。热搜词里的“javascript 事件”是一个很宽泛的词,但实战中真正需要下功夫的无非三件事:节流(throttle)、防抖(debounce)、事件委托。

先说节流和防抖的区别。滚动事件、窗口缩放这种高频触发场景用节流,保证一段时间内只执行一次,比如每 100ms 触发一次;输入框搜索这种需要等用户停下来的场景用防抖,比如用户停止输入 300ms 后才发起请求。有一个很容易被忽略的点:节流函数最好用requestAnimationFrame来写,而不是setTimeout。因为requestAnimationFrame天然跟帧率对齐,最多一帧执行一次,不会出现明明还有空闲却白等的情况。

事件委托则是另一个容易被忽略但收益极高的优化。如果一个列表有上千个可点击项,给每一项都绑定click事件,光内存开销就够喝一壶的。正确做法是给父容器统一绑一个监听器,用e.target判断实际点击的元素。我自己做长列表时还会配合一个WeakMap缓存事件处理结果,把同一个数据对象的计算结果直接复用,省掉重复的查询和计算。

我的习惯是给事件绑定写一个统一的管理工具,提供throttle、debounce、delegate三个方法,所有业务逻辑都走这个工具的出口。这样不只性能有保障,后续排查问题也容易,因为你只需要守着一份源码。

3.3 Canvas性能排查实录:不要一上来就考虑离屏渲染

H5 游戏化运营页面里,Canvas 是最容易吃性能的地方,热搜里的“javascript canvas”“javascript素材 云彩”我都会经常用到。很多人一谈 Canvas 优化就想到离屏渲染(OffscreenCanvas)、分层 Canvas,这些确实有用,但我踩过的坑是:很多项目的性能瓶颈根本不在绘制本身,而是drawImage的图片资源和状态管理。

先说资源。如果一张背景图是5000x3000像素的 PNG,你在 Canvas 上每帧都去绘制它,哪怕有 GPU 加速也会很吃力。正确做法是把素材提前按显示尺寸缩放好,直接在资源阶段就压缩到位,而不是让 Canvas 每帧去做缩放。我之前做过一个满天云彩飘动的 H5 素材,原图是摄影作品转的 PNG,首帧绘制直接卡了 800ms;后来把素材压缩成 2 倍屏尺寸的 WebP,首帧降到 100ms,画面观感几乎无差别。

再说状态管理。Canvas 的save()和restore()很贵,尤其在小游戏或复杂动画里,每次调用都会保存/恢复一整套绘图状态。我后来把公共状态(比如全局旋转变换)抽出来,只在确实需要隔离时再做状态进出。再加上用requestAnimationFrame统一驱动所有动画而不是各自设置setInterval,整体性能提升非常显著。

3.4 移动端性能优化的几个关键细节

移动端性能优化,和桌面端最大的区别在于设备的硬件限制和网络的不稳定性。热搜里的“移动端性能优化”和“oc和javascript互相调用”其实都指向同一个核心:在受限环境里做取舍。

  • JS执行时间预算更紧张:主流中低端安卓机的 CPU 性能可能只有旗舰机的一半不到,同样的脚本执行时间在旗舰机上不卡,在低端机上就可能掉帧。我在项目里给自己定的预算是所有启动期脚本不超过 500ms,交互期单次任务不超过 50ms。
  • 网络请求要优先保证首屏:移动端建议把与首屏无关的 JS 脚本全部加上defer或async,并把业务代码拆成按需加载的 chunk,避免一个几 MB 的 bundle 阻塞首屏渲染。
  • 与原生交互的内存管理:在 iOS 的 WKWebView 或安卓的 WebView 场景下,JS 和原生互相调用时,如果每次调用都创建大量临时对象,很容易导致内存告警。我的办法是尽量批量传递数据,比如把处理结果合成一个 JSON 字符串整体传回原生,而不是一条一条地多次调用桥接方法。

我自己做移动端页面时,还会刻意用 Chrome DevTools 的“CPU 降速 6 倍”和网络节流来模拟低端机。很多在电脑上运行流畅的页面,模拟一遍低端机之后你会发现自己写的代码简直是“重量级选手”。

4. 代码逻辑中常被忽略的“隐形性能杀手”

如果说上面讲的是策略和框架层面的大局,这一节要聊的就是真正写代码时容易埋雷的单点细节。这些问题单看都不大,但组合在一起,足以把一个页面拖垮。

4.1 保留两位小数:toFixed、Math.round与字符串处理的性能陷阱

热搜词里有一条“javascript 保留两位小数”,看起来是个入门级问题,但它在图表和表格类项目里会高频出现。如果你在渲染上万行的表格时对每个数字都做一次toFixed(2),也谈不上什么灾难。但如果你是在一个每帧都会触发重绘的 Canvas 图表里这么做,那积少成多也是肉眼可见的卡。

处理金额和数值展示,我的习惯是:

  • 如果是固定小数位的格式化,直接用Intl.NumberFormat,它在 V8 里是原生实现的,内部做了大量优化,比手写正则快很多。
  • 如果只是单纯保留两位小数而不需要考虑千分位,直接Math.round(num * 100) / 100其实是最快的。
  • 尽量避免用parseFloat(num.toFixed(2))这种链条式写法,中间会生成好几个临时字符串和数字对象。

一个真实项目里,我把某个统计报表中所有toFixed相关的逻辑都改成Intl.NumberFormat实例复用,在数据量 5 万行的渲染场景下,单次格式化耗时从 120ms 降到了 15ms。这就是“细节决定成败”的典型案例。

4.2 运行时错误对性能的影响:隐藏在try-catch里的代价

热搜词叫“javascript运行时报错”,得说一个反直觉的事实:运行时报错本身的性能影响不大,真正的大坑是错误处理机制干扰了 V8 的优化编译。在 V8 里,try-catch会创建一个执行上下文,函数一旦包含try-catch,V8 就默认进入了不适合激进优化的状态。所以高频函数里如果没有必要,尽量别用try-catch,把错误边界统一放到最外层或调用入口。

此外还有一类“静默错误”,比如监听器里抛了异常但没被正确处理,上报机制里面又带着new Error去堆栈采样,频率一高,性能直接崩。我的检查清单里有一条:线上监控平台里如果发现window.onerror上报过于频繁,第一件事不是去查业务逻辑,而是查死循环里的异常抛掷。

还有一个小坑是Promise里的错误忘记catch。如果未捕获的 Promise 异常在 Node 端会直接进程崩溃,在浏览器端则会变成无头错误,既不影响功能又特别难排查。这种问题会消耗开发者的注意力和调试时间,间接拖慢整个项目的迭代质量,也算是一种“开发性能”问题。

4.3 不要把“工具函数”写成性能黑洞

很多人喜欢用 Lodash 这类工具库写函数,方便是方便,但也要知道它的代价。Lodash 的_.debounce、_.cloneDeep在大多场景下都没问题,但如果你是写依赖包或公共 SDK 给外部系统调用的,建议先看看里面的实现再决定要不要引。

比如_.cloneDeep这种通用深拷贝,它在处理复杂对象时会递归遍历所有属性,对于树形结构特别深的数据,反而比手写一个针对性强、知道要拷哪些字段的版本慢得多。我在优化一个配置系统时,把一次提交里所有_.cloneDeep改成只复制必要字段的自定义函数,单次提交从 80ms 降到了 20ms。

这不是让你“不要用函数库”,而是每个工具函数的使用都应该建立在对它实现大致了解的基础上。用了却不明白它的复杂度,等于给项目埋了颗不知道什么时候会炸的雷。

5. 不可不知的运行时内存、加载策略与工具选型

性能优化到了后期,比拼的不再是谁的技巧更多,而是谁的体系更完整。除了代码执行速度,页面启动的加载速度、运行期间的内存占用、以及你手上工具链的选择,都会决定整体表现。这一块其实最贴近“性能优化实战”里“实战”二字。

5.1 内存管理与内存泄漏工具链

JavaScript 是带垃圾回收的语言,但这不代表你就不用管内存。最典型的泄漏场景是:全局变量挂载了对象、事件监听器没销毁、定时器没清、闭包持有大对象引用。这类泄漏很难靠眼睛看代码发现,得借助工具。

Heap Snapshot(堆快照)工具是排查内存泄漏的第一选择。操作上就是拍两次堆快照:一次在页面操作前,一次在操作后,然后对比“Detached DOM”节点和“Closures”引用,这两个区域通常是泄漏的高发区。我之前在一个数据大屏项目里用这招,发现了 30 多个从地图实例身上“detach”掉的 DOM 节点,每操作一次地图就涨 10MB 内存,最后排查出是某个第三方的setInterval回调里默默保存了旧实例。

在“julia性能优化与内存管理”这类热搜的启发下,我也想说一句:任何一门语言的性能优化都逃不开“时间”和“空间”的权衡。JS 里很多数组方法(map、filter)虽然写起来优雅,但每一次都会创建新数组对象。如果是在一个循环体里反复调用,内存分配的压力会成倍增长。遇到这种情况,我会改用传统的for循环加复用数组的方式,内存和耗时双双受益。这不算推翻“函数式编程”的价值,只是性能敏感代码里需要有意识地进行权衡。

5.2 加载策略:屏蔽高负载JavaScript的科学姿势

热搜词里有“屏蔽高负载 javascript”,这说法挺有意思。在实际项目里,我们的确会遇到某些第三方脚本加载后严重拖慢页面,比如一堆营销监测脚本、客服系统脚本、埋点 SDK。要“屏蔽”它们,不是简单的不引用了,而是要有组织地把它们分层管理。

我的核心思路是给脚本划分优先级:

  • 关键路径脚本:影响首屏渲染的必须同步或立即加载,比如页面框架代码、首屏组件的样式与逻辑。
  • 延迟加载脚本:这类脚本不参与首屏渲染,比如弹窗插件、近底部的列表组件、图片懒加载逻辑,全部加上async或defer。
  • 空闲时间加载脚本:监控、埋点、客服这类长期运行的脚本,建议用requestIdleCallback在空闲时动态注入。

具体到“屏蔽”的动作,很多广告或埋点脚本可以用加载后劫持或移除高危事件监听的方式来降低影响,但千万别盲目移除核心依赖。我在一个项目里的做法是:先用 Performance 面板统计每个第三方脚本的耗时占比,再和业务方确认哪些真的需要立即加载、哪些能降级为异步加载。改造之后,某个客服脚本从首屏的 1.2 秒降到完全不影响体验的空闲期加载,首屏白屏时间直接降了 30%。

5.3 工具与框架选型:好的选择比用力优化更省力

做 JS 性能和项目架构选型时,我越来越认同热词里那句话:“javascript 框架或库是一组能轻松生成跨浏览器兼容的 javascript 代码的工具和函数”,框架的意义不只是帮你加快开发,更是在帮你规避大量重复的性能陷阱。比如 React 的虚拟 DOM、Vue 的依赖追踪,都是为了让开发者不用手写 DOM 操作而自然获得不错的性能表现。

但这也带来了一个反向问题:很多人不知道框架的边界在哪里,把一切性能问题都归咎于框架。我用过的经验是,React 项目里的性能问题 80% 出在组件切分不合理和状态设计上,Vue 项目里则多是响应式依赖过深。优化 React 组件时,我会把每个列表项单独拆成React.memo包裹的组件,并严格控制 props 引用不变化,这样才能让 memo 真正生效。Vue 3 里则优先使用shallowRef和markRaw,避免对复杂对象做深度响应式代理,这个举措在渲染大型表格时非常有用。

工具选型也是一个隐形性能点。如果你还在用手动拼接字符串模板的方式渲染动态内容,换成任意一个现代框架都会是“降维打击”。同样,如果项目只需要部分交互,完全可以考虑用原生 JS 配合Web Component来实现,省去整整一个框架的运行体积。关键是意识到框架的性能优势建立在它的运行机制能匹配你的业务模型之上,而不是选了框架就万事大吉。

6. 常见问题与排查技巧实录:一份来自现场的经验清单

最后这部分,我把实际项目中反复遇到的高频问题和对应排查思路整理成一张速查表。它不是什么官方规范,但都是我在踩坑中验证过有效的办法,写出来供大家参考。

6.1 高频问题速查表

症状可能原因排查技巧
首屏加载时间过长主 JS bundle 过大,加载路径长看 Network 面板有没有体积惊人的 JS;用 webpack-bundle-analyzer 看占比
页面交互卡顿,掉帧主线程执行了长任务Performance 面板找长任务;getEntriesByType('longtask')
滚动时图片或动画闪烁强制同步布局(Forced Reflow)检查滚动事件里有没有读 offsetHeight 后立即写样式
内存持续上涨,最终页面崩溃事件监听泄漏、定时器未清对比两次 Heap Snapshot,查 Detached DOM
列表渲染成千上万条后操作卡死没有虚拟滚动,DOM 节点太多用虚拟列表方案,只渲染可视区
AJAX 数据返回后渲染白屏同步遍历和字符串拼接阻塞渲染拆任务或改用DocumentFragment批量更新
低端机表现远差于高端机没做移动端专项优化用 DevTools CPU 降速体验一下
上线后监控平台JavaScript报错陡增边界条件没处理好看报错堆栈和用户操作路径,收敛 try-catch 范围

我每次给团队做培训,都会让大家把这张表贴在工位旁边。排查问题最忌讳的就是瞎猜,有一个从“现象到原因”的对照表能节省大量时间。

6.2 排查脚本性能问题的一个小工具思路

除了浏览器自带的工具,我还习惯在公共代码里埋一个小工具:

// 这段代码用来在控制台快速查长任务 window.addEventListener('longtask', (e) => { const task = e.detail; console.warn(`耗时${task.duration}ms的长任务`, task.attribution || ''); });
注意:longtask 事件目前需要 PerformanceObserver 配合。实际使用的版本如下:
const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { console.warn(`长任务耗时:${entry.duration}ms`); } }); observer.observe({ entryTypes: ['longtask'] });

这段代码只需要本地调试时打开,能帮你快速定位是哪个交互触发了长任务。配合 Performance 面板的录像,基本 90% 的卡顿问题都能定位到具体的函数或第三方脚本。

6.3 实用避坑技巧:优化上线前必做的小事

优化工作最容易在“自我感觉良好”中翻车。这里分享三个我吃过亏之后的习惯:

  • 优化一个指标,盯住另外两个指标。比如你为了减少 JS 执行时间做了代码拆分,结果发现 FCP 变快了,但 LCP 因为某个 chunk 加载延迟反而变大了。优化一定是全局视角下的取舍,不是单点冲刺。
  • 用真机或低频设备验收。我在电脑上测过各种流畅的页面,拿到安卓中端机上就是另一回事。至少准备一台 1500 元价位的安卓机作为性能验收机,很多问题不模拟不会出现。
  • 给性能指标建立监控。这不只是上线前做一次 Lighthouse 那么简单,最好把 FCP、LCP、长任务次数上报到监控平台,设置阈值告警。性能跟业务一样,是会回归的,没有监控的优化等于没有优化。

写到最后的一些体感

前阵子帮一个朋友的项目做性能会诊,我打开 Performance 面板跑了一遍,发现整个页面的 JS 执行时间有 60% 来自一个“自动保存”功能——它每 5 秒就会把整个编辑器的内容深度克隆一遍再发到后台。这种问题跟你的代码技巧没有任何关系,纯粹是设计层面少了一个“脏标记”判断。很多所谓的性能优化实战,最后优化掉的都不是某项技术瓶颈,而是一个不合理的设计决策。所以我一直觉得,性能优化的第一步永远是重新审视你的业务逻辑,而不是急着秀操作。希望这篇文章里的方法能帮你少踩一些坑,也欢迎你把自己实战中遇到的奇葩性能问题拿出来交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 19:03:52

CSS分页控制:page-break-inside解决表格卡片跨页断裂的实战指南

打印预览里表格被拦腰截断、卡片从中间裂开、列表项跨页断行——这些几乎每个做过打印需求的前端都踩过。你翻遍整个样式表,最后往往发现解决问题的关键,就藏在一个叫 page-break-inside 的 CSS 属性里。 这个属性属于 CSS 分页控制体系的一员&#x…

作者头像 李华
网站建设 2026/10/6 19:01:27

GitHub新手入门全指南:仓库、提交、推送与Pages部署

第一次接触GitHub的人,通常不是在学它,而是在“被它卡住”:想找个开源工具,看到一堆英文按钮不知道点什么;照着网上的命令敲了半天,结果本地文件没推上去,还冒出一堆红色报错。这篇GitHub入门笔…

作者头像 李华
网站建设 2026/10/6 18:58:48

神经网络结合遗传算法:中国象棋AI评估与搜索实战解析

简介:一份融合神经网络与遗传算法的中国象棋AI程序,源自毕业设计与算法课程项目,适合学习人工智能与博弈算法的学生、开发者和游戏编程爱好者参考。压缩包内文件数量很多,共六百九十三个文件,整体大小约十五兆字节&…

作者头像 李华
网站建设 2026/10/6 18:54:53

第083篇 作用域函数五兄弟:let、run、with、apply、also

let、run、with、apply、also 这五个作用域函数,语法上都是"把一段代码包起来",但它们的组合有 22 两种维度共四种,加上 with 构成五兄弟。答这题靠背表格没用——从定义出发(参数式 vs 接收者式、返回旧对象 vs 返回新值)就能自己推出这五个的行为,不需要背。…

作者头像 李华
网站建设 2026/10/6 18:50:57

Java类与继承

Java 面向对象:类的继承(extends)与重写 前言 这是学习Java类与继承的笔记,用动物作为父类,狗,猫作为子类进行一个示范。本篇记录继承相关的基础知识。 环境 JDK 17IntelliJ IDEA纯 Java 项目(未…

作者头像 李华