pro*_*mer 284 javascript dom google-chrome
最近,我收到了这样的警告,这是我第一次得到它:
[Violation] Long running JavaScript task took 234ms
[Violation] Forced reflow while executing JavaScript took 45ms
Run Code Online (Sandbox Code Playgroud)
我正在开展一个小组项目,我不知道它来自哪里.这从未发生过.当参与该项目的其他人突然出现.如何找到导致此类警告的文件/功能?我一直在寻找答案,但主要是关于如何解决它的解决方案.如果我找不到问题的根源,我无法解决.
注意:在这种情况下,警告仅出现在chrome上.我试图使用Edge,没有得到任何类似的警告.我还没有在mozilla上测试它.
更新:我甚至得到错误jquery.min.js说:
[Violation] Handler took 231ms of runtime (50ms allowed) jquery.min.js:2
Run Code Online (Sandbox Code Playgroud)
vol*_*evo 248
更新:Chrome 58+默认隐藏了这些和其他调试消息.要显示它们,请单击"信息"旁边的箭头,然后选择"详细".
更新2:默认情况下,Chrome 57启用了"隐藏违规"功能.要重新打开它们,您需要启用过滤器并取消选中"隐藏违规"框.
当其他人参与该项目时突然出现
我认为你更有可能更新到Chrome 56.这个警告是一个(imo)精彩的新功能 - 如果你绝望,请关闭它,你的评估员将取消你的标记.其他浏览器存在潜在问题,但浏览器并没有告诉您存在问题.Chromium门票在这里但是没有真正有趣的讨论:https: //bugs.chromium.org/p/chromium/issues/detail?id = 662497
这些消息是警告而不是错误,因为它不会真正导致重大问题.它可能会导致帧丢失或导致不太顺畅的体验.
然而,它们值得调查和修复以提高应用程序的质量.这样做的方法是关注消息出现的情况,并进行性能测试以缩小问题发生的位置.开始性能测试的最简单方法是插入一些代码,如下所示:
function someMethodIThinkMightBeSlow() {
const startTime = performance.now();
// Do the normal stuff for this function
const duration = performance.now() - startTime;
console.log(`someMethodIThinkMightBeSlow took ${duration}ms`);
}
Run Code Online (Sandbox Code Playgroud)
如果您想获得更高级的功能,还可以使用Chrome的分析器:https: //developers.google.com/web/tools/chrome-devtools/rendering-tools/
或者使用像这样的基准测试库:https: //benchmarkjs.com/
一旦你发现一些需要很长时间的代码(50毫秒是Chrome的门槛),你有几个选择:
(1)和(2)可能是困难的或不可能的.但有时真的很容易,应该是你的第一次尝试.如果需要,应始终可以做(3).要做到这一点,你将使用类似的东西
setTimeout(functionToRunVerySoonButNotNow);
Run Code Online (Sandbox Code Playgroud)
要么
// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(functionToRunVerySoonButNotNow);
Run Code Online (Sandbox Code Playgroud)
你可以在这里阅读更多关于javascript的异步性质:
http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/
noo*_*oob 74
这些只是每个人都提到的警告.但是,如果您热衷于解决这些问题(您应该这样做),那么您需要首先确定导致警告的原因.没有任何理由可以获得强制回流警告.
有人为一些可能的选项创建了一个列表.您可以按照讨论了解更多信息.
以下是可能原因的要点 -
什么力量布局/回流
当在JavaScript中请求/调用时,以下所有属性或方法将触发浏览器同步计算样式和布局*.这也称为回流或布局颠簸,是常见的性能瓶颈.
元件
Box指标滚动的东西
elem.offsetLeft,elem.offsetTop,elem.offsetWidth,elem.offsetHeight,elem.offsetParentelem.clientLeft,elem.clientTop,elem.clientWidth,elem.clientHeightelem.getClientRects(),elem.getBoundingClientRect()焦点
elem.scrollBy(),elem.scrollTo()elem.scrollIntoView(),elem.scrollIntoViewIfNeeded()elem.scrollWidth,elem.scrollHeightelem.scrollLeft,elem.scrollTop同时,将它们设置也…
elem.focus()可以触发双重强制布局(源)
elem.computedRole,elem.computedNameelem.innerText(来源)的getComputedStyle
window.getComputedStyle()通常会强制样式重新计算(来源)
window.getComputedStyle()如果满足以下任何条件,则强制布局:
- 元素位于阴影树中
- 有媒体查询(与视口相关的查询).具体而言,执行以下操作之一:(源)*
min-width,min-height,max-width,max-height,width,height*aspect-ratio,min-aspect-ratio,max-aspect-ratio
device-pixel-ratio,resolution,orientation- 要求的财产是以下之一:( 来源)
height,width*top,right,bottom,left*margin[-top,-right,-bottom,-left,或速记 ]仅当余量是固定的.*padding[-top,-right,-bottom,-left,或速记 ]仅当填充是固定的.*transform,transform-origin,perspective-origin*translate,rotate,scale*webkit-filter,backdrop-filter*motion-path,motion-offset,motion-rotation*x,y,rx,ry窗口
window.scrollX,window.scrollYwindow.innerHeight,window.innerWidthwindow.getMatchedCSSRules()只有力量风格形式
inputElem.focus()inputElem.select(),textareaElem.select()(来源)鼠标事件
mouseEvt.layerX,mouseEvt.layerY,mouseEvt.offsetX,mouseEvt.offsetY(源)文献
doc.scrollingElement只有力量风格范围
range.getClientRects(),range.getBoundingClientRect()SVG
- 非常多; 尚未列出详尽的清单,但Tony Gentilcore的2011布局触发清单 指出了一些.
CONTENTEDITABLE
- 很多很多东西,包括将图像复制到剪贴板(来源)
在这里查看更多.
更新:
来自评论的 robocat
更新2:
有一篇关于如何最大限度地减少Google的PageSpeed Insight上的布局重排的文章.它解释了什么是浏览器重排 -
回流是Web浏览器进程的名称,用于重新计算文档中元素的位置和几何,以便重新呈现部分或全部文档.因为reflow是浏览器中的用户阻塞操作,所以对于开发人员来说,了解如何提高回流时间以及了解各种文档属性(DOM深度,CSS规则效率,不同类型的样式更改)对回流的影响是很有用的.时间.有时,回流文档中的单个元素可能需要回流其父元素以及其后的任何元素.
以及如何最小化它 -
- 减少不必要的DOM深度.DOM树中一个级别的更改可能会导致树的每个级别发生更改 - 一直到根目录,并一直向下到已修改节点的子级.这导致更多时间用于执行回流.
- 最小化CSS规则,并删除未使用的CSS规则.
- 如果您进行复杂的渲染更改(如动画),请执行此操作.使用position-absolute或position-fixed来完成此任务.
- 避免使用不必要的复杂CSS选择器 - 特别是后代选择器 - 这需要更多的CPU功率来进行选择器匹配.
the*_*kim 30
一些想法:
删除一半代码(可能通过评论).
问题还存在吗?太棒了,你已经缩小了可能性!重复.
问题不存在吗?好的,看看你注释掉的那一半!
您使用的是任何版本控制系统(例如Git)吗?如果是这样,git checkout你最近的一些提交.问题何时出现?查看提交以确定在问题首次到达时确切地更改了哪些代码.
就我而言,我发现这实际上是由扩展程序中的代码引起的(在我的案例中是Adblock).
要确定问题的根源,请运行您的应用程序,并将其记录在Chrome 中的" 性能"标签中.
在那里,您可以检查需要很长时间才能运行的各种功能.就我而言,在控制台中与警告相关联的那个是来自Adblock扩展加载的文件,但在您的情况下这可能是其他内容.
检查这些文件并尝试确定这是否是某些扩展代码或您的.
在"网络"选项卡下的Chrome控制台中查找,并查找最长时间加载的脚本.
在我的情况下,我已经包含但尚未在应用程序中使用的脚本添加了一组Angular:
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-utils/0.1.1/angular-ui-utils.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-aria.min.js"></script>
Run Code Online (Sandbox Code Playgroud)
这些是加载时间较长的JavaScript文件,而不是指定"长时间运行任务"错误的时间.
所有这些文件都在我的其他网站上运行,没有生成任何错误,但我在一个几乎没有任何功能的新网络应用程序上收到此"长时间运行任务"错误.删除后错误立即停止.
我最好的猜测是,这些Angular附加组件正在递归地看到DOM的越来越深的部分用于它们的开始标记 - 找不到,它们必须在退出之前遍历整个DOM,这比Chrome期望的要长 - 因此警告.
我在 Apache Cordova 源代码中找到了解决方案。他们是这样实现的:
var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };
Run Code Online (Sandbox Code Playgroud)
实施简单,但方式巧妙。
在 Android 4.4 以上,使用Promise. 对于较旧的浏览器,请使用setTimeout()
用法:
nextTick(function() {
// your code
});
Run Code Online (Sandbox Code Playgroud)
插入此技巧代码后,所有警告消息都消失了。
我在我的代码中找到了此消息的根,该代码搜索并隐藏或显示了节点(脱机)。这是我的代码:
search.addEventListener('keyup', function() {
for (const node of nodes)
if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
node.classList.remove('hidden');
else
node.classList.add('hidden');
});
Run Code Online (Sandbox Code Playgroud)
性能选项卡(分析器)显示事件耗时约60毫秒:

现在:
search.addEventListener('keyup', function() {
const nodesToHide = [];
const nodesToShow = [];
for (const node of nodes)
if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
nodesToShow.push(node);
else
nodesToHide.push(node);
nodesToHide.forEach(node => node.classList.add('hidden'));
nodesToShow.forEach(node => node.classList.remove('hidden'));
});
Run Code Online (Sandbox Code Playgroud)
现在,“性能”选项卡(分析器)显示该事件大约需要1毫秒:

而且我认为搜索现在可以更快地进行(229个节点)。
在此添加我的见解,因为该线程是有关该主题的“转到”stackoverflow 问题。
我的问题出在 Material-UI 应用程序中(早期阶段)
当我进行一些强制渲染页面的计算时(一个组件“显示结果”取决于其他组件“输入部分”中的设置)。
一切都很好,直到我更新了强制“结果组件”重新渲染的“状态”。这里的主要问题是我在同一个渲染器(App.js / return..)中有一个material-ui主题( https://material-ui.com/customization/theming/#a-note-on-performance )作为“结果组件”,SummaryAppBarPure
解决方案是将 ThemeProvider 提升一级(Index.js),并将 App 组件包装在此处,从而不强制 ThemeProvider 重新计算和绘制/布局/回流。
前
在 App.js 中:
return (
<>
<MyThemeProvider>
<Container className={classes.appMaxWidth}>
<SummaryAppBarPure
//...
Run Code Online (Sandbox Code Playgroud)
在index.js中
ReactDOM.render(
<React.StrictMode>
<App />
//...
Run Code Online (Sandbox Code Playgroud)
后
在 App.js 中:
return (
<>
{/* move theme to index. made reflow problem go away */}
{/* <MyThemeProvider> */}
<Container className={classes.appMaxWidth}>
<SummaryAppBarPure
//...
Run Code Online (Sandbox Code Playgroud)
在index.js中
ReactDOM.render(
<React.StrictMode>
<MyThemeProvider>
<App />
//...
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
299895 次 |
| 最近记录: |