Jump to content


Photo

PageSpeed ranking

pagespeed mobile ranking

  • Please log in to reply
8 replies to this topic

#1 Nico Wouterse

Nico Wouterse
  • Moderators
  • 255 posts
  • LocationAustralia

Posted 23 June 2016 - 03:17 PM

A while ago I submitted a SMS build webapp to Google PageSpeed
 
The results are :
 
MOBILE FRIENDLINESS
99/100 - GOOD
 
MOBILE SPEED
62/100 - POOR
 
DESKTOP SPEED
77/100 - FAIR
 
which is not too bad except for the mobile speed ranking.
 
The recommendations from PageSpeed were to basically minify everything (css, javascript, html) and to optimise images
Not too difficult 
 
The following recommendations were deemed 'Must do' changes
 
Browser caching
"The estimate is that nearly half of all downloaded responses can be cached by the browser, which is a huge savings for repeat pageviews and visits."
 
The solution would be to add a meta tag in the html file, something like
<meta http-equiv="Cache-control" content="public">
 
 
Prioritise visible content
"If the amount of data required exceeds a certain threshold (14.6kB compressed), it will require additional round trips between server and browser. For users on networks with high latencies such as mobile networks this can cause significant delays to page loading."
 
The solution would be to split the application in an 'immediate render' part (above the fold) and a 'deferred render' part (below the fold).
This basically means splitting all css and js files in two.
Not sure how this would work using standard SMS
 
 
Eliminate render blocking
"By default JavaScript blocks DOM construction and thus delays the time to first render."
 
One of the options would be to add the "async" parameter to all external js files, something like
<script async src="my.js">
 
 
Has anyone experience with these issues ?

  • two_dog_knight and AlexissNag like this
Nico Wouterse

#2 markus_ja

markus_ja
  • Members
  • 319 posts
  • LocationAustria

Posted 24 June 2016 - 09:25 AM

I had problems with caching on iOS in the web browser. If you have a lot of pictures (eg. thumbnails), some pictures are not shown, because of caching limitiations on iOS. In my case, I had to put a random number on every image url to get not cached. With that approche all pictures are shown.

 

Unfortunately, when you add your web page to the iOS Desktop, loading images doesn't work anymore. I can only show about 5 - 7 thumbnails :(

 

So far I found out in the net is, that apple wants to prohibit web apps on iOS. You should distribute all apps through the online store (where they get money).

 

Does anybody have experience with converted web apps to real apps with cordoba (phone gap)? Is there caching also an issue?



#3 Nico Wouterse

Nico Wouterse
  • Moderators
  • 255 posts
  • LocationAustralia

Posted 29 June 2016 - 05:46 PM

Partial solution to increase PageSpeed ranking : prioritise visible content

 

This 'above the fold' and 'below the fold' concept of Google bugged me a bit.

I can see the point, on high latency networks it is better to show something immediately and load the rest of the app code asap asynchronously

 

A standard sms visual project produces apps completely below the fold and an uncached project of say 2MB on mobile does take quite a while to load.

 

The solution is to split the app in two, or rather make two apps

 

In the most simple form this entails a small loader app (1) with the only function to load the resources of the main app (2)

Code below is inspired by the post in the september 2015 vault

uses
  W3C.HTML5, W3C.DOM, W3C.Console;
 
// style body
var BodyStyle := JHTMLBodyElement(Document.body).style;
BodyStyle.setProperty('border', '0');
BodyStyle.setProperty('margin', '0');
BodyStyle.setProperty('padding', '0');
BodyStyle.setProperty('overflow', 'hidden');
 
// load main script asynchronous
var MainScript := JHTMLScriptElement(Document.createElement('script'));
MainScript.&type := 'text/javascript';
MainScript.async := true;
MainScript.src := 'main-org.js';   //the .js file of the main application (2)
 
Window.addEventListener('load', lambda
    Console.Log('Main script loaded');
    Document.body.appendChild(MainScript);
  end);
 

The effect of the above is that the loader app (1) loads really quickly, after all it is only some 2kB

However for the user it doesn't offer much relief as it results in a black screen until the main js file is loaded

 

To make it a bit more interesting the things which come to mind are to enhance the loader app (1) by

- adding a picture or some text or a splash screen animation

- recreating the look and feel of the first form in the main app which will be replaced by the actual form soon as all resources are loaded

- showing a progress indicator

 

or all of the above

 

The code below produces a progress indicator 

uses
  W3C.HTML5, W3C.Console, W3C.XMLHttpRequest, W3C.ProgressEvents, W3C.Dom;
 
// style body
var BodyStyle := JHTMLBodyElement(Document.body).style;
BodyStyle.setProperty('border', '0');
BodyStyle.setProperty('margin', '0');
BodyStyle.setProperty('padding', '0');
BodyStyle.setProperty('overflow', 'auto');  //hidden
 
//ProgressBar
var ProgressBar := JHTMLProgressElement(Document.createElement('progress'));
ProgressBar.max := 100;
ProgressBar.value := 0;
Document.body.appendChild(ProgressBar);
 
procedure Progress(event: JProgressEvent);
begin
  If event.lengthComputable = true then
    ProgressBar.value := (event.loaded / event.total) * 100;
end;
 
//XMLHttpRequest
var req := JXMLHttpRequest.Create;
asm @req.addEventListener('progress', function(event) {@Progress(event); }); end;
req.open('POST','main-org.js',true);           //the .js file of the main application (2)
req.send;
 
//MainScript
var MainScript := JHTMLScriptElement(Document.createElement('script'));
MainScript.&type := 'text/javascript';
MainScript.async := true;
 
JGlobalEventHandlers(req).onLoad := lambda(e:JEvent)
  Console.Log('Main script ' + e.type + 'ed !');
  MainScript.innerHtml := req.responseText;
  Document.body.appendChild(MainScript);
end;
 

The trick here is that normal script loading does not produce progress events, only an 'onload' event at completion

However ajax downloads do generate progress events.

In this piece of code the main app .js file is downloaded through XMLHttpRequest.

The progress events during the ajax download are transferred to the progress bar, which updates itself accordingly.

On completion a new script is created and the ajax response (script from app 2) is copied in there.


  • Igor Savkic likes this
Nico Wouterse

#4 Igor Savkic

Igor Savkic
  • Members
  • 186 posts

Posted 29 June 2016 - 08:27 PM

Thanks for the sample, how large is resulting javascript file? I see there are few more samples of similar approach (without progress bar) here and in sms website:

http://smartmobilest...re-loader-code/
http://forums.smartm...ig-js-file-size

 

I think it would be best to include in loader first page of large application, which in my case is usually a login page, but in that case it will bring a lot of code from framework so only solutions are to write it all in raw javascript or send a static page in html. Have you done something similar, what is the better approach?

 

Ideally SMS would offer kind of Delphi packages, where common code (framework) would go into one minified js and main app would only include project code.

That way framework code could be cached and put on some global CDN (that's something SMS team could organize) and update with new SMS version.



#5 Nico Wouterse

Nico Wouterse
  • Moderators
  • 255 posts
  • LocationAustralia

Posted 30 June 2016 - 01:08 AM

The resulting javascript of the progress bar code is a mere 2K

 

A raw javascript login page shouldn't be too difficult to do, and can be nicely styled as well.

That way the initial load should be less than say 5-8K, which is OK and you can omit the login code from your main app

Just synchronise the actual login with the activation of the downloaded app javascript

 

CDN of framework code is a nice option, however that involves (async) server-client downloads as well

and needs to play nicely with smart linking


Nico Wouterse

#6 Igor Savkic

Igor Savkic
  • Members
  • 186 posts

Posted 30 June 2016 - 02:32 AM

> The resulting javascript of the progress bar code is a mere 2K

 

 

That's nothing, it will load immediatelly.

 

> A raw javascript login page shouldn't be too difficult to do, and can be nicely styled as well.

> That way the initial load should be less than say 5-8K, which is OK and you can omit the login code from

> your main app Just synchronise the actual login with the activation of the downloaded app javascript

 

Thanks for the idea, I'll probably implement something like that when I have few days to experiment. I'll either do it in raw JS or as a static html, just need to pass entered user/pass to main app code since it handles login to mORMot and sessions.

 

> CDN of framework code is a nice option, however that involves (async) server-client downloads

> as well and needs to play nicely with smart linking

 

I think major JS frameworks does it that way, for example "https://ajax.googlea...jquery.min.js",there's a good chance browser will already have it it's cache (it could be set never to expire) so there's nothing to download.

I really think SMS should include something like that and then take care of hosting, it would simplify a lot distribution to all developers working with it.



#7 Nico Wouterse

Nico Wouterse
  • Moderators
  • 255 posts
  • LocationAustralia

Posted 01 July 2016 - 11:19 AM

So to boost PageSpeed ranking the remaining topics are 'leveraging browser caching' and 'remove renderblocking js'

 

Browser caching

 

Methods to modify caching of resources in the browser :

-1 add a cache-control meta tag in the html-file

-2 add cache control info in a .htaccess file

-3 add cache-control headers in code

 

ad 1) this is not 100% reliable as not always all html is actually processed in the chain between original server and eventual browser. (proxy-caches)

ad 2) this only applies to apache servers

ad 3) So it seems that adding headers in code is the way to go.

 

References indicate that 

- using xmlhttprequests, headers must be produced between the 'open' and 'send' commands

- use 'get' instead of 'post', as post responses apparently aren't kept by most caches

- there is no established way to set infinite caching, but a period of up to 1 year seems to be acceptable

 

So f.i. the previous code (progress indicator) can then be modified to enhance browser caching as :

//XMLHttpRequest
var req := JXMLHttpRequest.Create;
req.open('GET','main-org2.js',true);
req.setRequestHeader("Cache-Control","public, max-age=31536000");   // 1 year
req.send;
 

.

 

Renderblocking

 

To remove renderblocking javascript the options are to 

 

-1) inline javascript

-2) make js asynchronous

-3) defer js loading

 

ad 1) this is meant for small js scripts only

 

ad 2) external js files like jspdf, accounting.js etc. can be safely loaded asynchronously as they usually are not needed 'above the fold'

         ...

         <script type="text/javascript" async src="lib/jspdf.js"></script>

         <script type="text/javascript" async src="lib/accounting.min.js"></script>

 

ad 3) covered previously in this thread

 

 

Conclusion

 

I expect using a combination of (all of) the above the PageSpeed's "mobile speed ranking" will improve considerably
I'll post the new comparable rankings when available.

Nico Wouterse

#8 Nico Wouterse

Nico Wouterse
  • Moderators
  • 255 posts
  • LocationAustralia

Posted 03 July 2016 - 12:17 PM

New PageSpeed results after implementing most of the above :

 
DESKTOP SPEED
93/100 - GOOD  (was : fair - 77/100)
 
MOBILE SPEED
83/100 - FAIR     (was : poor - 62/100)
 
MOBILE FRIENDLINESS
99/100 - GOOD 
 
--------------------------------------------------------------------------------------------------------------------------------------
 
Desktop ranking :
desktop.JPG
 
 
---------------------------------------------------------------------------------------------------------------------------------------------------
 
Mobile ranking
 
Mobile.JPG
 
 
-------------------------------------------------------------------------------------------------------------------------
 
There is some room for improvement but this looks pretty much ok

Nico Wouterse

#9 Nico Wouterse

Nico Wouterse
  • Moderators
  • 255 posts
  • LocationAustralia

Posted 19 October 2016 - 04:32 PM

Looking at increasing the mobile PageSpeed results further (see Prioritize Visual Content) it occurred to me that instead of a splash screen or a spinner showing until the main js file is loaded, it would actually help to have a quick pre-render of the first form.

 

The recipe for this could be something like this :

 

0) develop the project as per normal

 

1) get the rendered html code of the main form

    (in Chrome open the developer console and type in 'copy(document.body.innerHTML);'

    This will copy the generated html <body> code onto the clipboard

    A simple form with a button and a label generates this

  <script type="text/javascript"> /* This prevents the window being moved by touches,
 to give the impression of a native app */
document.ontouchmove = function(e) { e.preventDefault(); }
 </script>
 
<script type="text/javascript" src="main.js"></script>
<div id="OBJ1" class="TW3Display" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; width: 1407px; height: 522px;">
<div id="OBJ2" class="TW3DisplayView" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; height: 522px; width: 1407px;">
<div id="OBJ3" class="TW3CustomForm" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; -webkit-transform: none; width: 1407px; height: 522px;">
<button id="OBJ4" class="TW3Button" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 144px; top: 88px; width: 128px; height: 32px;">W3Button</button>
<fieldset id="OBJ5" class="TW3Label" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 160px; top: 160px; height: 32px; width: 128px;">
<div id="OBJ6" class="TW3LabelText" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 7px; text-overflow: ellipsis; white-space: nowrap; width: 65px; height: 18px;">W3Label
</div></fieldset></div></div></div></div>

2) make a copy of the project index.html file, delete the <body> content and replace it with the code above

 

3) add a simple script to load the 'main.js' project file on dom-ready

<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "main.js";
document.body.appendChild(element);
window.location.href="index.html";
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>

You'll end up with this 

<!DOCTYPE html>
<html manifest="app.manifest">
<head> 
  <meta charset="UTF-8" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="viewport" content="width=device-width, maximum-scale=1.0, initial-scale=1.0, user-scalable=no"/>
  <link rel="manifest" href="webapp.json">
  
 
<title>SSR-V01</title>
 
<link rel="stylesheet" type="text/css" href="res/app.css"/>
 
  
</head>
 
<body>  <script type="text/javascript"> /* This prevents the window being moved by touches,
 to give the impression of a native app */
document.ontouchmove = function(e) { e.preventDefault(); }
 </script>  
 
<script type="text/javascript" src="main.js"></script>
<div id="OBJ1" class="TW3Display" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; width: 1407px; height: 522px;">
<div id="OBJ2" class="TW3DisplayView" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; height: 522px; width: 1407px;">
<div id="OBJ3" class="TW3CustomForm" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; -webkit-transform: none; width: 1407px; height: 522px;">
<button id="OBJ4" class="TW3Button" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 144px; top: 88px; width: 128px; height: 32px;">W3Button</button>
<fieldset id="OBJ5" class="TW3Label" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 160px; top: 160px; height: 32px; width: 128px;">
<div id="OBJ6" class="TW3LabelText" style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 7px; text-overflow: ellipsis; white-space: nowrap; width: 65px; height: 18px;">W3Label
</div></fieldset></div></div></div></div> 

<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "main.js";
document.body.appendChild(element);
window.location.href="index.html";
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
</body>
 
</html>

The above will

- render an image of the first form straight away and very fast. This image will be styled beautifully

- load the main js file immediately after

- optionally redirect to the original index.html file after the js has been cached

 

In terms of user experience an image of the first form will look better than a spinner. It won't be responsive until the js logic has loaded but it least it will look good

In terms of PageSpeed score (and SEO ranking) this will boost both of them considerably


  • ielite, Igor Savkic and two_dog_knight like this
Nico Wouterse




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users