{"id":551,"date":"2019-05-28T20:35:00","date_gmt":"2019-05-28T20:35:00","guid":{"rendered":"https:\/\/www.digitaldatatactics.com\/?p=551"},"modified":"2020-09-17T21:15:19","modified_gmt":"2020-09-17T21:15:19","slug":"adobes-performancetiming-plugin-with-some-improvements-and-an-explanation","status":"publish","type":"post","link":"https:\/\/www.digitaldatatactics.com\/index.php\/2019\/05\/28\/adobes-performancetiming-plugin-with-some-improvements-and-an-explanation\/","title":{"rendered":"Adobe\u2019s performanceTiming plugin, with some improvements and an explanation"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"alignright size-thumbnail is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/33sticks_logo-150x150.png\" alt=\"33 Sticks Logo- Orange Circle with 3|3 in it\" class=\"wp-image-538\" width=\"75\" height=\"75\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/33sticks_logo-150x150.png 150w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/33sticks_logo-300x300.png 300w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/33sticks_logo.png 441w\" sizes=\"(max-width: 75px) 100vw, 75px\" \/><\/figure><\/div>\n\n\n\n<p>(This is cross-posted form the <a href=\"https:\/\/33sticks.com\/adobes-performancetiming-plugin-improvements-explanation\/\">33 Sticks blog<\/a>)<\/p>\n\n\n\n<p>As Page Performance (rightfully) gets more and more attention, I\u2019ve been hearing more and more questions about the Performance Timing plugin from Adobe consulting.&nbsp;Adobe&nbsp;<a href=\"https:\/\/marketing.adobe.com\/resources\/help\/en_US\/sc\/implement\/performanceTiming.html\" target=\"_blank\" rel=\"noreferrer noopener\">does have public documentation for this plugin<\/a>, but I think it deserves a little more explanation, as well as some discussions of gotchas, and potential enhancements.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How It Works<\/h2>\n\n\n\n<p>Adobe\u2019s Page Performance plugin is actually just piggybacking on built-in functionality: your browser already determined at what time your content starting loading and at what time is stopped loading. You can see this in a JavaScript Console by looking at&nbsp;<em>performance.timing<\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"376\" height=\"355\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/performanceTiming.png\" alt=\"\" class=\"wp-image-552\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/performanceTiming.png 376w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/performanceTiming-300x283.png 300w\" sizes=\"(max-width: 376px) 100vw, 376px\" \/><\/figure>\n\n\n\n<p>This shows a timestamp (in milliseconds since Jan 1, 1970, which the internet considers the beginning of time) for when the current page hit certain loading milestones.<\/p>\n\n\n\n<p>Adobe\u2019s plugin does look at that performance timing data, compares a bunch of the different milestone timestamps versus each other, then does some math to put it into nice, easy-to-read seconds. For instance, my total load time would be the number of seconds between navigationStart and loadEventEnd:<\/p>\n\n\n\n<p>1556048746779 (loadEventEnd) \u2013&nbsp;1556048745659 (navigationStart) = 1120 milliseconds, or 1.12 seconds.<\/p>\n\n\n\n<p>Additionally, if I choose to, I can have the plugin grab information from the&nbsp;built-into-the-browser&nbsp;<em>performance.getEntries()<\/em>, put it into session storage (not a cookie because it can be a long list), and put it into the variable of your choice (usually a listVar or list prop) on the next page. These entries show you for EACH FILE on the page, how long they took to load.<\/p>\n\n\n\n<p>Unfortunately, if I\u2019m sending my analytics page view beacon while the page is still loading, then the browser can\u2019t tell me when \u201cdomComplete\u201d happened\u2026. because it hasn\u2019t happened yet! So the plugin writes all these values to a cookie, then on your NEXT beacon, reads them back and puts them into numeric events that you define when you set the plugin up. This means you won\u2019t get a value on&nbsp;the first page of the visit, and the values for the last page of the visit won\u2019t ever be sent in. It also means you don\u2019t want to break these metrics down by page, but rather by PREVIOUS page- so often this plugin is rolled out alongside the getPreviousValue plugin. This also means that the plugin is not going to return data for single-page visits or for the last page of visits (it may collect the data but doesn\u2019t have a second beacon to send the data in on). for this reason, your Performance Timing Instances metric may look significantly different from your Page Views metric.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What It Captures<\/h2>\n\n\n\n<p>Out of the box, the plugin captures all of the following into events:<\/p>\n\n\n\n<ul><li>Redirect Timing (seconds from navigationStart to fetchStart- should be zero if there was no redirect)<\/li><li>App Cache Timing (seconds from fetchStart to domainLookupStart)<\/li><li>DNS Timing (seconds from domainLookupStart to domainLookupEnd)<\/li><li>TCP Timing (seconds from connectStart to connectEnd)<\/li><li>Request Timing (seconds from connectEnd to responseStart)<\/li><li>Response Timing (seconds from responseStart to responseEnd )<\/li><li>Processing Timing (seconds from domLoading to loadEventStart)<\/li><li>onLoad Timing (seconds from loadEventStart to loadEventEnd)<\/li><li>Total Page Load Time (seconds from navigationStart to loadEventEnd )<\/li><li>Instances (for calculated metric- otherwise you only really get the aggregated seconds, which is fairly meaningless if your traffic fluctuates)<\/li><\/ul>\n\n\n\n<p>Which gets you reporting that looks like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"190\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReport-1024x190.png\" alt=\"\" class=\"wp-image-560\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReport-1024x190.png 1024w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReport-300x56.png 300w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReport-768x143.png 768w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReport.png 1314w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>\u2026Which, to be honest, isn\u2019t that useful, because it shows the&nbsp;<em>aggregated<\/em>&nbsp;number of seconds. The fact that our product page took 1.3 million seconds in redirect timing in this reporting period means nothing without some context. That\u2019s why that last metric, \u201cinstances\u201d, exists: you can turn any of the first 9 metrics into a calculated metric that shows you the AVERAGE number of seconds in each phase of the page load:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"829\" height=\"688\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/calculatedMetric.png\" alt=\"\" class=\"wp-image-559\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/calculatedMetric.png 829w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/calculatedMetric-300x249.png 300w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/calculatedMetric-768x637.png 768w\" sizes=\"(max-width: 829px) 100vw, 829px\" \/><\/figure>\n\n\n\n<p>This gives me a much more useful report, so I can start seeing which pages take the longest to load:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"646\" height=\"261\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReportWithMetrics.png\" alt=\"\" class=\"wp-image-558\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReportWithMetrics.png 646w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/exampleReportWithMetrics-300x121.png 300w\" sizes=\"(max-width: 646px) 100vw, 646px\" \/><\/figure>\n\n\n\n<p>As you can see, the calculated metric can use either the \u201cTime\u201d format or the \u201cDecimal\u201d format, depending on your preference.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Performance Entries<\/h3>\n\n\n\n<p>As mentioned previously, the plugin can also capture your performance entries (that is, a list of ALL of the resources a page loaded, like images and&nbsp;JS files) and put them into a listVar or prop of your choice. This returns a list, delimited by \u201c!\u201d, where each value has a format that includes:<\/p>\n\n\n\n<p><em>The name of the resource (ignoring query params)!at what second in the page load this resource started loading!how long it took for that resource to&nbsp;finish&nbsp;loading!resource type (img, script, etc).<\/em><\/p>\n\n\n\n<p>For example, on my blog, I might see it return something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"\" class=\"\">https:\/\/digitaldatatactics.com\/beaconParser\/index.html|0.0|0.9|navigation!https:\/\/www.digitaldatatactics.com\/utility\/spiffy.css|0.2|0.1|link!https:\/\/digitaldatatactics.com\/beaconParser\/decoder.css|0.2|0.1|link!https:\/\/ajax.googleapis.com\/ajax\/libs\/jquery\/2.1.1\/jquery.min.js|0.2|0.1|script!https:\/\/digitaldatatactics.com\/beaconParser\/js\/varInfo.js|0.2|0.1|script!https:\/\/digitaldatatactics.com\/beaconParser\/js\/HBvarInfo.js|0.2|0.1|script!https:\/\/digitaldatatactics.com\/beaconParser\/js\/wsse.js|0.2|0.1|script!https:\/\/digitaldatatactics.com\/beaconParser\/js\/apiConfig.js|0.2|0.2|script!https:\/\/digitaldatatactics.com\/beaconParser\/js\/apiRetrieve.js|0.2|0.1|script!https:\/\/digitaldatatactics.com\/beaconParser\/js\/decode.js|0.2|0.1|script!https:\/\/digitaldatatactics.com\/beaconParser\/js\/print.js|0.2|0.1|script!https:\/\/digitaldatatactics.com\/images\/NarrowBanner.png|0.2|0.3|img!https:\/\/digitaldatatactics.com\/beaconParser\/images\/trash.png|0.2|0.5|img!https:\/\/assets.adobedtm.com\/launch-EN3911ddbdce3b4c4697e2d3c903e9cfc5.min.js|0.4|0.1|script!https:\/\/digitaldatatactics.com\/beaconParser\/images\/background-trans.png|0.4|0.3|css!first-paint|0.5|0.0|undefined!first-contentful-paint|0.5|0.0|undefined!https:\/\/assets.adobedtm.com\/extensions\/EP4c3fcccffd524251ae198bf677f3b6e9\/AppMeasurement.min.js|0.5|0.0|script!https:\/\/jenniferkunz.d1.sc.omtrdc.net\/b\/ss\/jenniferkunztestdev\/1\/JS-2.12.0-L9SG\/s44127282881639|0.7|0.2|img<\/code><\/pre>\n\n\n\n<p>From this, I can see every file that is used on my page and how long it took to load (and yes, it is telling me that the last resource to load was my analytics beacon, which started .7 seconds into my page loading, and took .2 seconds to complete). This is a LOT of information, and at bare minimum, it can make my analytics beacons very long (you can pretty much accept that most of your beacons are going to become POST requests rather than GET requests), but it can be useful to see if certain files are consistently slowing down your page load times.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">An Enhancement: Time to Interaction<\/h2>\n\n\n\n<p>Unfortunately, the plugin most commonly used by folks omits one performance timing metric that many folks believe is the most critical:&nbsp;<strong>Time to DomInteractive<\/strong>.&nbsp;As&nbsp;<a href=\"https:\/\/www.bowlerhat.co.uk\/site-performance-time-to-interactive\">this helpful site<\/a>&nbsp;states:<\/p>\n\n\n\n<ul><li><strong>Page Load Time<\/strong>&nbsp;is the time in which it takes to download the entire content of a web page and to stabilize.<\/li><li><strong>Time to Interactive<\/strong>&nbsp;is the amount of time in which it takes for the content on your page to become functional and ready for the user to interact with once the content has stabilized.<\/li><\/ul>\n\n\n\n<p>In other words, Page Load Time might include the time it takes for a lot of background activity to go on, which may not necessarily&nbsp;stop the user from interacting with the site. If&nbsp;your page performance goal is for the best user experience, then Time To Interaction should be a key metric in measuring that. So, how do we track that? It already exists in that performance.timing object, so I tweaked the existing plugin code to include it. I can then create a calculated metric off of that (Time to Interactive\/Page Performance Instances) and you can see it tells a very different story for this site than Total Page Load Time did:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"466\" height=\"223\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/reportTimeToInteractive.png\" alt=\"\" class=\"wp-image-557\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/reportTimeToInteractive.png 466w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/reportTimeToInteractive-300x144.png 300w\" sizes=\"(max-width: 466px) 100vw, 466px\" \/><\/figure>\n\n\n\n<p>9.49 seconds DOES sound like a pretty awful experience, but all three of these top pages had a much lower (and much more consistent) number of seconds before the user could start interacting with the page.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Basic Implementation<\/h2>\n\n\n\n<p>There are three parts to setting up the code for this plugin: before doPlugins (configuration), during doPlugins (execution), and after doPlugins (definition).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Configuration<\/h3>\n\n\n\n<p>First, before doPlugins, you need to configure your usage by setting s.pte and s.ptc:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">s.pte = 'event1,event2,event3,event4,event5,event6,event7,event8,event9,event10,event11'\ns.ptc = false; \/\/this should always be set to false for when your library first loads<\/pre>\n\n\n\n<p>In my above example, here is what each event will set:<\/p>\n\n\n\n<ul><li><strong>event1=<\/strong>&nbsp;Redirect Timing (seconds from navigationStart to fetchStart- should be zero if there was no redirect)- set as Numeric Event<\/li><li><strong>event2=<\/strong>&nbsp;App Cache Timing (seconds from fetchStart to domainLookupStart)- set as Numeric Event<\/li><li><strong>event3=<\/strong>&nbsp;DNS Timing (seconds from domainLookupStart to domainLookupEnd)- set as Numeric Event<\/li><li><strong>event4=<\/strong>&nbsp;TCP Timing (seconds from connectStart to connectEnd)- set as Numeric Event<\/li><li><strong>event5=<\/strong>&nbsp;Request Timing (seconds from connectEnd to responseStart)- set as Numeric Event<\/li><li><strong>event6=<\/strong>&nbsp;Response Timing (seconds from responseStart to responseEnd )- set as Numeric Event<\/li><li><strong>event7=<\/strong>&nbsp;Processing Timing (seconds from domLoading to loadEventStart)- set as Numeric Event<\/li><li><strong>event8=<\/strong>&nbsp;onLoad Timing (seconds from loadEventStart to loadEventEnd)- set as Numeric Event<\/li><li><strong>event9=<\/strong>&nbsp;Total Page Load Time (seconds from navigationStart to loadEventEnd )- set as Numeric Event<\/li><li><strong>event10=<\/strong>&nbsp;Total Time to Interaction (seconds from connectStart to timeToInteraction)- set as Numeric Event. NOTE- THIS IS ONLY ON MY VERSION OF THE PLUGIN, OTHERWISE SKIP TO INSTANCES<\/li><li><strong>event11=<\/strong>&nbsp;Instances&nbsp;\u2013 set as&nbsp;<strong>Counter<\/strong>&nbsp;Event<\/li><\/ul>\n\n\n\n<p>I\u2019d also need to make sure those events are enabled in my Report Suite with the correct settings (everything should be a&nbsp;<em>Numeric Event<\/em>, with the exception of instances, which should be a&nbsp;<em>Counter Event<\/em>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Execution<\/h3>\n\n\n\n<p>Within doPlugins, I need to just run the s.performanceTiming function. If I don\u2019t want to capture performance entries (which is reasonable- not everyone has the listVars to spare, and it can return a VERY long value that can be difficult to get value out of), then I fire the function without any arguments:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">s.performanceTiming()<\/code><\/pre>\n\n\n\n<p>If I DO want those performance entries, then I&nbsp;add the name of that variable as an argument:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">s.performanceTiming(\"list3\")<\/code><\/pre>\n\n\n\n<p>Also, you\u2019re going to want to be capturing Previous Page Name into a prop or eVar if you aren\u2019t already:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">s.prop1=s.getPreviousValue(s.pageName,'gpv_pn');<\/code><\/pre>\n\n\n\n<p>(If you are already capturing Previous Page Name into a variable, you don\u2019t need to capture it separately just for this plugin- you just need to be capturing it once somewhere).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Definition<\/h3>\n\n\n\n<p>Finally, where I have all of my plugin code, I need to add the plugin definitions. You can get Adobe\u2019s version from their documentation, or if you want it with Time To Interactive, you can use my version:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">\/* Plugin: Performance Timing Tracking - 0.11 BETA - with JKunz's changes for Time To Interaction. \nCan you guess which line I changed ;)?*\/\ns.performanceTiming=new Function(\"v\",\"\"\n+\"var s=this;if(v)s.ptv=v;if(typeof performance!='undefined'){if(perf\"\n+\"ormance.timing.loadEventEnd==0){s.pi=setInterval(function(){s.perfo\"\n+\"rmanceWrite()},250);}if(!s.ptc||s.linkType=='e'){s.performanceRead(\"\n+\");}else{s.rfe();s[s.ptv]='';}}\");\ns.performanceWrite=new Function(\"\",\"\"\n+\"var s=this;if(performance.timing.loadEventEnd&gt;0)clearInterval(s.pi)\"\n+\";try{if(s.c_r('s_ptc')==''&amp;&amp;performance.timing.loadEventEnd&gt;0){try{\"\n+\"var pt=performance.timing;var pta='';pta=s.performanceCheck(pt.fetc\"\n+\"hStart,pt.navigationStart);pta+='^^'+s.performanceCheck(pt.domainLo\"\n+\"okupStart,pt.fetchStart);pta+='^^'+s.performanceCheck(pt.domainLook\"\n+\"upEnd,pt.domainLookupStart);pta+='^^'+s.performanceCheck(pt.connect\"\n+\"End,pt.connectStart);pta+='^^'+s.performanceCheck(pt.responseStart,\"\n+\"pt.connectEnd);pta+='^^'+s.performanceCheck(pt.responseEnd,pt.respo\"\n+\"nseStart);pta+='^^'+s.performanceCheck(pt.loadEventStart,pt.domLoad\"\n+\"ing);pta+='^^'+s.performanceCheck(pt.loadEventEnd,pt.loadEventStart\"\n+\");pta+='^^'+s.performanceCheck(pt.loadEventEnd,pt.navigationStart);pta+='^^'+s.performanceCheck(pt.domInteractive, pt.connectStart);\"\n+\"s.c_w('s_ptc',pta);if(sessionStorage&amp;&amp;navigator.cookieEnabled&amp;&amp;s.pt\"\n+\"v!='undefined'){var pe=performance.getEntries();var tempPe='';for(v\"\n+\"ar i=0;i&lt;pe.length;i++){tempPe+='!';tempPe+=pe[i].name.indexOf('?')\"\n+\"&gt;-1?pe[i].name.split('?')[0]:pe[i].name;tempPe+='|'+(Math.round(pe[\"\n+\"i].startTime)\/1000).toFixed(1)+'|'+(Math.round(pe[i].duration)\/1000\"\n+\").toFixed(1)+'|'+pe[i].initiatorType;}sessionStorage.setItem('s_pec\"\n+\"',tempPe);}}catch(err){return;}}}catch(err){return;}\");\ns.performanceCheck=new Function(\"a\",\"b\",\"\"\n+\"if(a&gt;=0&amp;&amp;b&gt;=0){if((a-b)&lt;60000&amp;&amp;((a-b)&gt;=0)){return((a-b)\/1000).toFix\"\n+\"ed(2);}else{return 600;}}\");\ns.performanceRead=new Function(\"\",\"\"\n+\"var s=this;if(performance.timing.loadEventEnd&gt;0)clearInterval(s.pi)\"\n+\";var cv=s.c_r('s_ptc');if(s.pte){var ela=s.pte.split(',');}if(cv!='\"\n+\"'){var cva=s.split(cv,'^^');if(cva[1]!=''){for(var x=0;x&lt;(ela.lengt\"\n+\"h-1);x++){s.events=s.apl(s.events,ela[x]+'='+cva[x],',',2);}}s.even\"\n+\"ts=s.apl(s.events,ela[ela.length-1],',',2);}s.linkTrackEvents=s.apl\"\n+\"(s.linkTrackEvents,s.pte,',',2);s.c_w('s_ptc','',0);if(sessionStora\"\n+\"ge&amp;&amp;navigator.cookieEnabled&amp;&amp;s.ptv!='undefined'){s[s.ptv]=sessionSt\"\n+\"orage.getItem('s_pec');sessionStorage.setItem('s_pec','',0);}else{s\"\n+\"[s.ptv]='sessionStorage Unavailable';}s.ptc=true;\");\n\/* Remove from Events 0.1 - Performance Specific, \nremoves all performance events from s.events once being tracked. *\/\ns.rfe=new Function(\"\",\"\"\n+\"var s=this;var ea=s.split(s.events,',');var pta=s.split(s.pte,',');\"\n+\"try{for(x in pta){s.events=s.rfl(s.events,pta[x]);s.contextData['ev\"\n+\"ents']=s.events;}}catch(e){return;}\");\n\/* Plugin Utility - RFL (remove from list) 1.0*\/\ns.rfl=new Function(\"l\",\"v\",\"d1\",\"d2\",\"ku\",\"\"\n+\"var s=this,R=new Array(),C='',d1=!d1?',':d1,d2=!d2?',':d2,ku=!ku?0:\"\n+\"1;if(!l)return'';L=l.split(d1);for(i=0;i&lt;L.length;i++){if(L[i].inde\"\n+\"xOf(':')&gt;-1){C=L[i].split(':');C[1]=C[0]+':'+C[1];L[i]=C[0];}if(L[i\"\n+\"].indexOf('=')&gt;-1){C=L[i].split('=');C[1]=C[0]+'='+C[1];L[i]=C[0];}\"\n+\"if(L[i]!=v&amp;&amp;C)R.push(C[1]);else if(L[i]!=v)R.push(L[i]);else if(L[i\"\n+\"]==v&amp;&amp;ku){ku=0;if(C)R.push(C[1]);else R.push(L[i]);}C='';}return s.\"\n+\"join(R,{delim:d2})\");<\/code><\/pre>\n\n\n\n<p>You\u2019ll also need to have s.apl and s.split.<\/p>\n\n\n\n<p>You can see a&nbsp;full example of what your plugins code might look like, as well as a deobfuscated picking-apart of the plugin, on&nbsp;<a href=\"https:\/\/github.com\/33-Sticks\/performanceTiming33Sticks\/\">our gitHub<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Performance Entries Classifications<\/h3>\n\n\n\n<p>I recommend if you ARE capturing Performance Entries in a listVar, setting up 5 classifications on that listVar:<\/p>\n\n\n\n<ul><li>Resource\/File<\/li><li>Starting Point<\/li><li>Duration<\/li><li>Duration- Bucketed (if desired)<\/li><li>Resource Type<\/li><\/ul>\n\n\n\n<p>Then set up a Classification Rule, using this regex string as the basis:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">^(.+)\\|(.+)\\|(.+)\\|(.+)<\/pre>\n\n\n\n<p>In our git repo, I have a&nbsp;<a href=\"https:\/\/github.com\/33-Sticks\/performanceTiming33Sticks\/raw\/master\/classificationRules-PTentries.xlsx\">full list of the classification rules and regex I used<\/a>, including how to bucket the durations so you get less granular values like \u201c0.5-1.0 seconds\u201d, which can give you a report like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"658\" height=\"605\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/bucketedDuration.png\" alt=\"\" class=\"wp-image-554\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/bucketedDuration.png 658w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/bucketedDuration-300x276.png 300w\" sizes=\"(max-width: 658px) 100vw, 658px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Implications for Single Page Apps<\/h2>\n\n\n\n<p>Unfortunately, this plugin will NOT be able to tell you how long a \u201cvirtual page\u201d on a single page app (SPA) takes to load, because it relies on the performance.timing info, which is tied to a when an initial DOM loads. This isn\u2019t to say you can\u2019t deploy it on a Single Page App- you may still get some good data, but the data will be tied to when the overall app loads. Take this user journey for example, where the user navigates through Page C of a SPA, then refreshes the page:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"206\" src=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/SPAflow-1024x206.png\" alt=\"\" class=\"wp-image-555\" srcset=\"https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/SPAflow-1024x206.png 1024w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/SPAflow-300x60.png 300w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/SPAflow-768x155.png 768w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/SPAflow-1536x309.png 1536w, https:\/\/www.digitaldatatactics.com\/wp\/wp-content\/uploads\/2020\/09\/SPAflow.png 1708w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>As you can see, we\u2019d only get performanceTiming entries twice- once on Page A and once on the refreshed Page C. Even without the \u201cvirtual pages\u201d, it may still be worth tracking- especially since a SPA may have a lot of upfront loading on the initial DOM. But it\u2019s not going to tell the full story about how much time the user is spending waiting for content to load.<\/p>\n\n\n\n<p>You can still try to measure performance for state changes\/\u201dvirtual pages\u201d on a SPA, but you\u2019ll need to work with your developers to figure out a good point to start measuring (is it when the user clicks on the link that takes them to the next page? Or when the URL change happens?) and at what point to stop measuring (is there a certain method or API call that brings in content? Do you having a \u201cloading\u201d icon you can piggy back on to listen to the end?). Make sure if you start going this route (which could be pretty resource intensive), you ask yourselves what you can DO with the data: if you find out that it takes an average 2.5 seconds to get from virtual page B to virtual page C, what would your next step be? Would developers be able to speed up that transition if the data showed them the current speed was problematic?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Use the Data<\/h2>\n\n\n\n<p>Finally, it\u2019s important to make sure after you\u2019ve implemented the plugin, you set aside some time to gather insights and make recommendations. I find that this plugin is one that is often used to just \u201ccheck a box\u201d- it\u2019s nice to know you have it implemented in case anyone ever wants it, but once it is implemented, if often goes ignored. It&nbsp;<em>is<\/em>&nbsp;good to have in place sooner rather than later, because often, questions about page performance only come up after a change to the site, and you\u2019ll want a solid baseline already in place. For instance, if you\u2019re migrating from DTM to Launch, you might want to roll this plugin out in DTM well in advance of your migration so after the migration, you can see the effect the migration had on page performance. Consider setting a calendar event 2 weeks after any major site change to remind you to go and look at how it affected the user experience.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(This is cross-posted form the 33 Sticks blog) As Page Performance (rightfully) gets more and more attention, I\u2019ve been hearing more and more questions about the Performance Timing plugin from Adobe consulting.&nbsp;Adobe&nbsp;does have public documentation for this plugin, but I think it deserves a little more explanation, as well as some discussions of gotchas, and &#8230; <a title=\"Adobe\u2019s performanceTiming plugin, with some improvements and an explanation\" class=\"read-more\" href=\"https:\/\/www.digitaldatatactics.com\/index.php\/2019\/05\/28\/adobes-performancetiming-plugin-with-some-improvements-and-an-explanation\/\" aria-label=\"Read more about Adobe\u2019s performanceTiming plugin, with some improvements and an explanation\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23],"tags":[57,3,45,53,52],"_links":{"self":[{"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/posts\/551"}],"collection":[{"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/comments?post=551"}],"version-history":[{"count":5,"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/posts\/551\/revisions"}],"predecessor-version":[{"id":566,"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/posts\/551\/revisions\/566"}],"wp:attachment":[{"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/media?parent=551"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/categories?post=551"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.digitaldatatactics.com\/index.php\/wp-json\/wp\/v2\/tags?post=551"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}