Performance.now
Performance是一個做前端效能監控離不開的API,最好在頁面完全載入完成之後再使用,因為很多值必須在頁面完全載入之後才能得到。最簡單的方法是在window.onload事件中讀取各種資料。
performance.now()方法傳回一個精確到毫秒的 DOMHighResTimeStamp 。
根據 MDN :
這個時間戳記其實不是高精度的。為了降低像Spectre這樣的安全威脅,各類瀏覽器對此類型的值做了不同程度的四捨五入處理。 (Firefox從Firefox 59開始四捨五入到2毫秒精度)一些瀏覽器也可能對這個值作稍微的隨機化處理。這個值的精確度在未來的版本中可能會再次改善;瀏覽器開發者也正在調查這些時間測定攻擊和如何更好的緩解這些攻擊。
因為,要計算一個函數的執行時間,分別比較函數執行前和執行後的兩次performance.now()的值即可,如下所示:
const t0 = performance.now(); for (let i = 0; i這裡可以觀察到Firefox 和Chrome 所呈現的結果完全不同。這是因為從版本60開始,Firefox 將performance API的精確度降到2ms。
performance API 不當當只有回傳時間戳這個功能,還有很多實用方法,大家可以依照需要到 MDN 查詢相關的文件。
然而,對於我們的用例,我們只想計算單一函數的效能,因此時間戳就足夠了。
performance.now() 和Date.now一樣嗎?
#你可能會想,嘿,我也可以用Date.now來做?
是的,你可以,但這有缺點。
Date.now返回自Unix紀元(1970-01-01T00:00:00Z)以來經過的時間(以毫秒為單位),並取決於系統時鐘。這不僅意味著它不夠精確,而且不總是遞增。在此,WebKit工程師Tony Gentilcore做出了解釋:
使用系統時間作為日期可能不是最好的選擇,也不適用於使用者監控。大多數系統運行一個守護程序,該程序會定期同步時間。通常每15至20分鐘將時鐘調整幾毫秒。以該速率,大約10秒間隔的1%將是不準確的。
Performance.mark 和 Performance.measure
#除了Performance.now函數外,還有一些函數可以讓我們度量程式碼不同部分的時間,並將它們當作效能測試工具(如Webpagetest)中的自訂度量。
Performance.mark
##先來看看MDN中關於mark方法的定義:
performance.mark('name');
Performance.measure
#同樣先來看看MDN 上關於measure 的定義: 這段定義和上面mark 的定義有些類似,其最核心的不同點在於這句話between two specified marks。所以measure是指定兩個mark點之間的時間戳記。如果說mark可以理解為**"打點"的話,measure就可以理解為"連線"**。performance.measure(name, startMark, endMark);
performance.measure('measure name');
performance.measure('measure name', undefined, 'mark-2');
performance.measure('measure name', 'mark-1', 'mark-2');
從performance entry buffer 取得資料
#
在上面的函数中,总是提到结果存储在performance entry buffer,但是如何访问其中的内容呢?
performance API有3个函数可以用来访问该数据:
performance.getEntries()
获取一组当前页面已经加载的资源PerformanceEntry对象。接收一个可选的参数options进行过滤,options支持的属性有name,entryType,initiatorType。
let entries = window.performance.getEntries();
performance.getEntriesByName
根据参数name,type获取一组当前页面已经加载的资源数据。资源数据中的"name"字段对应于"name"的取值,资源数据中的"entryType"字段对应于"type"的取值。
let entries = window.performance.getEntriesByName(name, type);
performance.getEntriesByType
根据参数type获取一组当前页面已经加载的资源数据。type取值对应到资源数据中的entryType字段。
var entries = window.performance.getEntriesByType(type);
结合事例:
performance.mark('mark-1'); // some code performance.mark('mark-2') performance.measure('test', 'mark-1', 'mark-2') console.log(performance.getEntriesByName('test')[0].duration);
Console.time
这个 API确实易于使用。当需要统计一段代码的执行时间时,可以使用console.time方法与console.timeEnd方法,其中console.time方法用于标记开始时间,console.timeEnd方法用于标记结束时间,并且将结束时间与开始时间之间经过的毫秒数在控制台中输出。这两个方法的使用方法如下所示。
console.time('test'); for (let i = 0; i输出的结果与Performance API非常相似。
console.time的优点是易于使用,因为它不需要手动计算两个时间戳之间的差。
减少时间精度
如果在不同的浏览器中使用上面提到的 api 测量函数,你可能会注意到结果是不同的。
这是由于浏览器试图保护用户免受时序攻击(timing attack)和指纹采集(Fingerprinting ),如果时间戳过于准确,黑客可以使用它们来识别用户。
例如,Firefox等浏览器试图通过将精度降低到2ms(版本60)来防止这种情况发生。
注意事项
现在,我们已经知道了要测量JavaScript函数的速度所需方法。但是,最好还要避免一些陷阱:
分而治之
开发过程中,我们可能会我发现有些模块执行速度很慢,但是我们不知道具体问题出在哪里。一种解决方案是使用前面提到的这些函数来测量代码,而不是随便猜测哪一部分比较慢。
为了跟踪它,你需要在执行速度较慢的代码块周围放置console.time语句。然后测量它们不同部分的表现。如果一个比另一个慢,那就继续往下走,直到发现问题所在。
注意输入值
在实际应用中,给定函数的输入值可能会发生很大变化。我们无法通过仅针对任意随机值测量函数的速度来获得任何实用的有价值数据。
确保使用相同的输入值运行代码。
多次运行该函数
如果你拥有一个函数,它的功能在于遍历一个数组,在对数组的每个值执行一些计算后,返回一个包含计算结果的新数组。你想知道是forEach循环还是简单的for循环性能更好。
function testForEach(x) { console.time('test-forEach'); const res = []; x.forEach((value, index) => { res.push(value / 1.2 * 0.1); }); console.timeEnd('test-forEach') return res; } function testFor(x) { console.time('test-for'); const res = []; for (let i = 0; i然后这样测试它们:
const x = new Array(100000).fill(Math.random()); testForEach(x); testFor(x);登入後複製
如果在 Firefox 中运行上述函数,结果:
看起来forEach慢多了,对吧?
那如果是相同的输入,运行两次呢:
testForEach(x); testForEach(x); testFor(x); testFor(x);
在第二次调用forEach的情况下,其执行效果应该是和使用for循环相同。考虑到初始值较慢,在一些性能要求极高的项目,可能就不适合使用forEach。
在多个浏览器中测试
如果我们在Chrome中运行上述代码,结果又会不一样:
这是因为Chrome和Firefox具有不同的JavaScript引擎,它们具有不同类型的性能优化。
在本例中,Firefox 在对相同输入的forEach进行优化方面做得更好。
for在两个引擎上的性能都更好,因此在一些性能要求极高的项目就需要使用for循环。
这是为什么要在多个引擎中进行测量的一个很好的例子。只使用Chrome作为度量标准可能导致你得出结论,forEach与for相比并不那么糟糕。
限制的 CPU
在本地测试时得到的结果不代表用户在浏览器中的使用情况,因为我们开发者使用的电脑通常比大多数用户的电脑配置更好。
瀏覽器有一個特性可以限制CPU效能,我們可以透過設定更貼切一些真實情況。
以上是怎麼使用Performance監控前端效能的詳細內容。更多資訊請關注PHP中文網其他相關文章!