Heim  >  Artikel  >  Web-Frontend  >  Leistungsstarke Scroll- und Seitenrendering-Optimierung

Leistungsstarke Scroll- und Seitenrendering-Optimierung

PHPz
PHPzOriginal
2017-03-12 16:41:131292Durchsuche

Kürzlich beschäftige ich mich mit den Leistungsproblemen beim Rendern von Seiten und Webanimationenund lese das Meisterwerk „CSS SECRET“.

In diesem Artikel geht es hauptsächlich um die Seitenoptimierung Scroll-Optimierung.

Der Hauptinhalt umfasst, warum das Scrollen optimiert werden muss Ereignis , die Beziehung zwischen Scrollen und Seitenrendering, Drosselung und Anti-Shake, pointer-events:none optimiert Scrollen. Da dieser Artikel viele Grundlagen abdeckt, können Sie auf die oben genannten Wissenspunkte verweisen und gezielt zur entsprechenden Stelle zum Lesen springen.

Der Ursprung der Scroll-Optimierung

Scroll-Optimierung bezieht sich eigentlich nicht nur auf Scrollen (Scroll-Ereignisse), einschließlich Ereignissen, die häufig ausgelöst werden, wie z. B. Größenänderung. Schauen Sie einfach mal rein:


var i = 0;
window.addEventListener('scroll',function(){
	console.log(i++);
},false);

Die Ausgabe ist wie folgt:

Wenn Bindungsereignisse wie Scrollen und Größenänderung auftreten, werden sie sehr häufig und in sehr kurzen Abständen ausgelöst. Wenn das Ereignis umfangreiche Positionsberechnungen, DOM-Manipulationen, Neuzeichnen von Elementen usw. umfasst und diese Aufgaben nicht abgeschlossen werden können, bevor das nächste Scroll-Ereignis ausgelöst wird, führt dies dazu, dass der Browser Frames fallen lässt. Darüber hinaus ist das Scrollen mit der Maus des Benutzers oft kontinuierlich, was weiterhin das Scroll-Ereignis auslöst, was dazu führt, dass Frame-Drops größer werden, die CPU-Auslastung des Browsers zunimmt und das Benutzererlebnis beeinträchtigt wird.

Rückrufe in Scroll-Ereignissen bindenEs gibt viele Anwendungsszenarien, wie z. B. verzögertes Laden und Verschieben von Bildernautomatisches LadenDaten, seitlichen schwebenden Navigationsleisten usw. verwendet.

Ein reibungsloses Scrollen ist ein oft übersehener, aber entscheidender Teil des Benutzererlebnisses beim Surfen im Internet. Wenn sich das Scrollen normal verhält, werden Benutzer das Gefühl haben, dass die Anwendung sehr flüssig und angenehm ist. Im Gegenteil, schweres, unnatürliches und steckengebliebenes Scrollen bereitet den Benutzern große Unannehmlichkeiten.

Die Beziehung zwischen Scrollen und Seitenrendering

Warum scrollen Muss die Veranstaltung optimiert werden? Weil es die Leistung beeinträchtigt. Welche Auswirkungen hat es also auf die Leistung? Nun... das beginnt damit, was die Probleme mit der Seitenleistung bestimmt.

Ich denke, Sie müssen nicht zu den Wurzeln der Technologie zurückkehren. Lesen Sie nicht den Artikel eines anderen, der sagt, dass Scrolling-Ereignisse Verzögerungen verursachen und über eine Reihe von Techniken zur Lösungsoptimierung sprechen Sie haben einen Schatz, was wir brauchen, ist keine Aneignung, sondern Kritik, und wir müssen öfter an die Quelle gehen.

Beginnen Sie mit dem Problem und suchen Sie Schritt für Schritt bis zum Ende. Nur so kann man sich die Lösung leicht merken.

Ich habe viel Unsinn gepredigt, einfach ignorieren. Um den Einstieg in die Optimierung zu finden Problem liegt. Für die Seitenoptimierung müssen wir das Rendering-Prinzip der Seite kennen:

Ich werde in meinem vorherigen Artikel auch ausführlich auf das Browser-Rendering-Prinzip eingehen, aber mehr Aus der Perspektive des Animationsrenderings. Apropos: [Webanimation] CSS3 3D-Planetenbewegungs- und Browser-Rendering-Prinzip.

Nachdem ich darüber nachgedacht habe, möchte ich es kurz beschreiben. Ich stelle fest, dass ich jedes Mal, wenn ich diese Wissenspunkte überprüfe, neue Erkenntnisse gewinne >chr

ome ist ein Beispiel für die Anzeige einer Webseite

  • JavaScript: Im Allgemeinen werden wir JavaScript verwenden, um einige visuelle Änderungen zu erreichen. Erstellen Sie beispielsweise eine Animation oder fügen Sie der Seite einige DOM-Elemente usw. hinzu.

  • Stil: Stil berechnen: Dieser Prozess dient dazu, den entsprechenden CSS-Stil für jedes DOM-Element basierend auf dem CSS-Selektor anzupassen. Nachdem dieser Schritt abgeschlossen ist, wird festgelegt, welche CSS-Stilregeln auf jedes DOM-Element angewendet werden sollen.

  • Layout: Layout Im vorherigen Schritt werden die Stilregeln für jedes DOM-Element ermittelt jedes DOM-Element Die Größe und Position der Anzeige auf dem Bildschirm. Das Layout der Elemente auf einer Webseite ist relativ, d. h. Änderungen am Layout eines Elements führen zu Änderungen im Layout anderer Elemente. Beispielsweise wirken sich Änderungen in der Breite des 6c04bd5ca3fcae76e30b72ad730ca86d-Elements auf die Breite seiner untergeordneten Elemente aus, und Änderungen in der Breite seiner untergeordneten Elemente wirken sich auch weiterhin auf seine untergeordneten Elemente aus. Daher kommt es bei Browsern häufig zum Layoutvorgang.

  • Malen: Zeichnen ist im Wesentlichen der Prozess des Füllens von Pixeln. Einschließlich Zeichnungstext, Farbe, Bild, Rahmen und Schatten usw., also aller visuellen Effekte eines DOM-Elements. Im Allgemeinen erfolgt dieser Zeichenvorgang auf mehreren Ebenen.

  • Zusammengesetzt: Wie aus dem vorherigen Schritt hervorgeht, ist die Zeichnung der DOM-Elemente auf der Seite auf mehreren Ebenen durchgeführt. Nachdem der Zeichenvorgang für jede Ebene abgeschlossen ist, führt der Browser alle Ebenen in einer angemessenen Reihenfolge zu einer einzigen Ebene zusammen und zeigt sie dann auf dem Bildschirm an. Dieser Vorgang ist besonders wichtig für Seiten mit überlappenden Elementen, da die Elemente nicht ordnungsgemäß angezeigt werden, sobald die Ebenen in der falschen Reihenfolge zusammengeführt werden.

Hier geht es wieder um das Konzept der Ebene (GraphicsLayer), die als Textur (Textur) auf die GPU hochgeladen wird, was mittlerweile häufig verwendet wird Sie sehen, dass die GPU-Hardwarebeschleunigung eng mit dem sogenannten Layer-Konzept zusammenhängt. Für die Scroll-Optimierung dieses Artikels hat es jedoch wenig Relevanz. Wer mehr erfahren möchte, kann selbst mehr googeln.

Um es einfach auszudrücken: Wenn eine Webseite generiert wird, wird sie mindestens einmal gerendert (Layout + Farbe). Während des Benutzerbesuchs wird es weiterhin umgeflossen und neu gestrichen.

Unter anderem führt das Scrollen und Ändern der Größe des Benutzers

Verhalten (d. h. das Verschieben der Seite und das Ändern der Fenstergröße) dazu, dass die Seite kontinuierlich neu gerendert wird.

Während Sie auf der Seite scrollen, muss der Browser möglicherweise einige der Pixel in diesen Ebenen zeichnen (manchmal auch Compositing-Ebenen genannt). Durch das Gruppieren von

mit Elementen müssen wir, wenn sich der Inhalt einer bestimmten Ebene ändert, nur die Struktur der Ebene aktualisieren und nur den geänderten Teil der Rendering-Ebenenstruktur neu zeichnen und rastern . , ohne komplett neu zu zeichnen. Wenn sich so etwas wie eine Parallaxen-Website (schau es dir an) beim Scrollen bewegt, ist es natürlich möglich, dass sich die Größe großer Inhaltsbereiche über mehrere Ebenen hinweg ändert, was zu viel Zeichenarbeit führt.

Entprellen und Drosseln Das

Scroll-Event selbst wird Gleichzeitig wird der Handler des Scroll-Ereignisses häufig ausgelöst. Daher sollte es keine komplexen Vorgänge im Ereignishandler geben, wie z. B. DOM-Vorgänge sollte nicht in der

Ereignisbehandlung platziert werden. Für solche hochfrequent ausgelösten Ereignisprobleme (wie Seitenscrollen, Bildschirmgrößenänderung, Überwachung von Benutzereingaben usw.) werden im Folgenden zwei häufig verwendete Lösungen vorgestellt

: Anti-Shake und Drosselung.

防抖(Debouncing)

防抖技术即是可以把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

通俗一点来说,看看下面这个简化的例子:


// 简单的防抖动函数
function debounce(func, wait, immediate) {
	// 定时器变量
	var timeout;
	return function() {
		// 每次触发 scroll handler 时先清除定时器
		clearTimeout(timeout);
		// 指定 xx ms 后触发真正想进行的操作 handler
		timeout = setTimeout(func, wait);
	};
};

// 实际想绑定在 scroll 事件上的 handler
function realFunc(){
	console.log("Success");
}

// 采用了防抖动
window.addEventListener('scroll',debounce(realFunc,500));
// 没采用防抖动
window.addEventListener('scroll',realFunc);

上面简单的防抖的例子可以拿到浏览器下试一下,大概功能就是如果 500ms 内没有连续触发两次 scroll 事件,那么才会触发我们真正想在 scroll 事件中触发的函数。

上面的示例可以更好的封装一下:


// 防抖动函数
function debounce(func, wait, immediate) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		var later = function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		};
		var callNow = immediate && !timeout;
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);
	};
};

var myEfficientFn = debounce(function() {
	// 滚动中的真正的操作
}, 250);

// 绑定监听
window.addEventListener('resize', myEfficientFn);

节流(Throttling)

防抖函数确实不错,但是也存在问题,譬如图片的懒加载,我希望在下滑过程中图片不断的被加载出来,而不是只有当我停止下滑时候,图片才被加载出来。又或者下滑时候的数据的 ajax 请求加载也是同理。

这个时候,我们希望即使页面在不断被滚动,但是滚动 handler 也可以以一定的频率被触发(譬如 250ms 触发一次),这类场景,就要用到另一种技巧,称为节流函数(throttling)。

节流函数,只允许一个函数在 X 毫秒内执行一次。

与防抖相比,节流函数最主要的不同在于它保证在 X 毫秒内至少执行一次我们希望触发的事件 handler。

与防抖相比,节流函数多了一个 mustRun 属性,代表 mustRun 毫秒内,必然会触发一次 handler ,同样是利用定时器,看看简单的示例:


// 简单的节流函数
function throttle(func, wait, mustRun) {
	var timeout,
		startTime = new Date();

	return function() {
		var context = this,
			args = arguments,
			curTime = new Date();

		clearTimeout(timeout);
		// 如果达到了规定的触发时间间隔,触发 handler
		if(curTime - startTime >= mustRun){
			func.apply(context,args);
			startTime = curTime;
		// 没达到触发间隔,重新设定定时器
		}else{
			timeout = setTimeout(func, wait);
		}
	};
};
// 实际想绑定在 scroll 事件上的 handler
function realFunc(){
	console.log("Success");
}
// 采用了节流函数
window.addEventListener('scroll',throttle(realFunc,500,1000));

上面简单的节流函数的例子可以拿到浏览器下试一下,大概功能就是如果在一段时间内 scroll 触发的间隔一直短于 500ms ,那么能保证事件我们希望调用的 handler 至少在 1000ms 内会触发一次。

 

   使用 rAF(requestAnimationFrame)触发滚动事件

上面介绍的抖动与节流实现的方式都是借助了定时器 setTimeout ,但是如果页面只需要兼容高版本浏览器或应用在移动端,又或者页面需要追求高精度的效果,那么可以使用浏览器的原生方法 rAF(requestAnimationFrame)。

requestAnimationFrame

window.requestAnimationFrame() 这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数。这个方法接受一个函数为参,该函数会在重绘前调用。

rAF 常用于 web 动画的制作,用于准确控制页面的帧刷新渲染,让动画效果更加流畅,当然它的作用不仅仅局限于动画制作,我们可以利用它的特性将它视为一个定时器。(当然它不是定时器)

通常来说,rAF 被调用的频率是每秒 60 次,也就是 1000/60 ,触发频率大概是 16.7ms 。(当执行复杂操作时,当它发现无法维持 60fps 的频率时,它会把频率降低到 30fps 来保持帧数的稳定。

简单而言,使用 requestAnimationFrame 来触发滚动事件,相当于上面的:


throttle(func, xx, 1000/60) //xx 代表 xx ms内不会重复触发事件 handler

简单的示例如下:


var ticking = false; // rAF 触发锁

function onScroll(){
  if(!ticking) {
    requestAnimationFrame(realFunc);
    ticking = true;
  }
}

function realFunc(){
	// do something...
	console.log("Success");
	ticking = false;
}
// 滚动事件监听
window.addEventListener('scroll', onScroll, false);

上面简单的使用 rAF 的例子可以拿到浏览器下试一下,大概功能就是在滚动的过程中,保持以 16.7ms 的频率触发事件 handler。

使用 requestAnimationFrame 优缺点并存,首先我们不得不考虑它的兼容问题,其次因为它只能实现以 16.7ms 的频率来触发,代表它的可调节性十分差。但是相比 throttle(func, xx, 16.7) ,用于更复杂的场景时,rAF 可能效果更佳,性能更好。

总结一下 

  • 防抖动:防抖技术即是可以把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

  • 节流函数:只允许一个函数在 X 毫秒内执行一次,只有当上一次函数执行后过了你规定的时间间隔,才能进行下一次该函数的调用。

  • rAF:16.7ms 触发一次 handler,降低了可控性,但是提升了性能和精确度。

 

   简化 scroll 内的操作

上面介绍的方法都是如何去优化 scroll 事件的触发,避免 scroll 事件过度消耗资源的。

但是从本质上而言,我们应该尽量去精简 scroll 事件的 handler ,将一些变量的初始化、不依赖于滚动位置变化的计算等都应当在 scroll 事件外提前就绪。

建议如下:

避免在scroll 事件中修改样式属性 / 将样式操作从 scroll 事件中剥离

 

输入事件处理函数,比如 scroll / touch 事件的处理,都会在 requestAnimationFrame 之前被调用执行。

因此,如果你在 scroll 事件的处理函数中做了修改样式属性的操作,那么这些操作会被浏览器暂存起来。然后在调用 requestAnimationFrame 的时候,如果你在一开始做了读取样式属性的操作,那么这将会导致触发浏览器的强制同步布局。

 

   滑动过程中尝试使用 pointer-events: none 禁止鼠标事件

大部分人可能都不认识这个属性,嗯,那么它是干什么用的呢?

pointer-events 是一个 CSS 属性,可以有多个不同的值,属性的一部分值仅仅与 SVG 有关联,这里我们只关注 pointer-events: none 的情况,大概的意思就是禁止鼠标行为,应用了该属性后,譬如鼠标点击,hover 等功能都将失效,即是元素不会成为鼠标事件的 target。

可以就近 F12 打开开发者工具面板,给 6c04bd5ca3fcae76e30b72ad730ca86d 标签添加上 pointer-events: none 样式,然后在页面上感受下效果,发现所有鼠标事件都被禁止了。

那么它有什么用呢?

pointer-events: none 可用来提高滚动时的帧频。的确,当滚动时,鼠标悬停在某些元素上,则触发其上的 hover 效果,然而这些影响通常不被用户注意,并多半导致滚动出现问题。对 body 元素应用 pointer-events: none ,禁用了包括 hover 在内的鼠标事件,从而提高滚动性能。


.disable-hover {
    pointer-events: none;
}

大概的做法就是在页面滚动的时候, 给 6c04bd5ca3fcae76e30b72ad730ca86d 添加上 .disable-hover 样式,那么在滚动停止之前, 所有鼠标事件都将被禁止。当滚动结束之后,再移除该属性。

可以查看这个 demo 页面。

上面说 pointer-events: none 可用来提高滚动时的帧频 的这段话摘自 pointer-events-MDN ,还专门有文章讲解过这个技术:

使用pointer-events:none实现60fps滚动 。

这就完了吗?没有,张鑫旭有一篇专门的文章,用来探讨 pointer-events: none 是否真的能够加速滚动性能,并提出了自己的质疑:

pointer-events:none提高页面滚动时候的绘制性能?

结论见仁见智,使用 pointer-events: none 的场合要依据业务本身来定夺,拒绝拿来主义,多去源头看看,动手实践一番再做定夺。

其他参考文献(都是好文章,值得一读):

  • 实例解析防抖动(Debouncing)和节流阀(Throttling)

  • 无线性能优化:Composite

  • Javascript高性能动画与页面渲染

  • Google Developers--渲染性能

  • Web高性能动画

 

Das obige ist der detaillierte Inhalt vonLeistungsstarke Scroll- und Seitenrendering-Optimierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn