ProvidenceGeeks / website-frontend

UI frontend repository for the Providence Geeks website
https://www.pvdgeeks.org
7 stars 9 forks source link

improve performance of external requests (Google Font API) happening in the Critical Rendering Path #152

Open thescientist13 opened 6 years ago

thescientist13 commented 6 years ago

Type of Change

Although the score is "good". Having to make an external HTTP request kind of defeats the purpose of inlining critical CSS.

Looking at the network tabs shows this request can be lengthy (117ms ) relative to say API requests the app makes (< 50ms).

screen shot 2018-04-27 at 10 17 30 am screen shot 2018-04-27 at 10 21 13 am

Summary

paul-barry-kenzan commented 6 years ago

I have a "vendor" .scss file that I use to include 3rd-party styles (icons, fonts, etc).

Here's how I include two (2) different fonts in various styles.

@import url("https://fonts.googleapis.com/css?family=Montserrat:200,400,700|Lato:400,700,400italic,700italic");

Hope this helps.

EDIT: Here's the network overview

screen shot 2018-04-27 at 10 45 22 am

EDIT 2: After some digging, seems you have a similar pattern. Sorry to get you excited for a solution you already have. 🤦‍♂️

thescientist13 commented 6 years ago

thanks @paul-barry-kenzan .

We are doing that currently too, but that external request (https://fonts.googleapis.com/) is what I see as a potential bottleneck in our case.

It's just a hunch, but I'm hoping by taking an external HTTP request out of the Critical Rendering Path results in the app taking less time to paint something, even if the performance isn't significantly improved. At least to the user there is the perception of something happening on the page, which is better than the three seconds of white screen, IMO.

In the network tab, it looks like 110ms of the request to the font is just time waiting for the request to be fulfilled (the TTfB) 😮

screen shot 2018-04-27 at 10 40 04 am

Compare it to the time for our CSS chunk to load off CloudFront CDN

screen shot 2018-04-27 at 10 53 01 am

Almost 100ms second difference.

That's a lot of time to pay per user, per request, just for the convenience. A couple hours playing around on NPM setting this up manually if need be, will make our application completely non-dependent on that service, and thus that perpetual "convenience" tax. 🕐 💰 📦

thescientist13 commented 6 years ago

I wonder if just copying the contents of the response to the request into a .scss file would work? I don't think the font files getting loaded themselves will be render blocking at that point, at least from what I am seeing, they get loaded towards the end... 🤔

thescientist13 commented 6 years ago

Found this recommendation of using preconnect to load external resources.

Google Fonts are great. The service is reliable and generally fast due to Google's global CDN. However, because @font-face rules must first be discovered in CSS files before making web font requests, there often can be a noticeable visual delay during page render. We can greatly reduce this delay by adding the preconnect hint below!

I'm thinking of trying to put Google Fonts URL in index.html and adding that attribute manually as an alternative to what I'm trying to do in #153 (bundling fonts from the local filesystem).

Once we do that, it’s easy to spot the difference in the waterfall charts below. Adding preconnect removes three round-trips from the critical rendering path and cuts more than a half second of latency.

thescientist13 commented 6 years ago

Saw this good talk on web font performance and a tool called subfont

Tried it out but ran into snag. 🎀

thescientist13 commented 6 years ago

results of various optimizations

index.html (without html-critical-webpack-plugin)

<!DOCTYPE html>
<html lang="en">

  <head>
    <base href="/" />
    <title>Providence Geeks</title>  
    <meta charset="utf-8">
    <meta name="description" content="The goal of Providence Geeks is to help Rhode Island’s digital innovators connect, collaborate, and ultimately make the City-State and its geeks info-technology leaders."/>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">

  <meta name="apple-mobile-web-app-title" content="Providence Geeks" /><meta name="apple-mobile-web-app-capable" content="yes" /><meta name="apple-mobile-web-app-status-bar-style" content="default" /><meta name="theme-color" content="#1a2930" /><link rel="apple-touch-startup-image" href="icon_1024x1024.915638a2e87880eaf307a0baf1abb24e.png" /><link rel="apple-touch-icon" sizes="512x512" href="icon_512x512.182f900ea4c1bf1883c11413b67364df.png" /><link rel="apple-touch-icon" sizes="167x167" href="icon_167x167.7a223e8ceb81b340d9099a64df02e34a.png" /><link rel="apple-touch-icon" sizes="180x180" href="icon_180x180.ae62ab86f45f19c58e995096c11bc9f7.png" /><link rel="apple-touch-icon" sizes="152x152" href="icon_152x152.be7c2455346e0d37b432eb67ba150122.png" /><link rel="apple-touch-icon" sizes="120x120" href="icon_120x120.a13fab9321c4d18b10fd0f77e80ea5b2.png" /><link rel="apple-touch-icon" sizes="87x87" href="icon_87x87.04983c9d18d15515f3f4075be15ebbcc.png" /><link rel="apple-touch-icon" sizes="80x80" href="icon_80x80.1e1e27f7d5dfda91b748947e59c20283.png" /><link rel="apple-touch-icon" sizes="72x72" href="icon_72x72.d867a890d58b552d4f30996f52abf51b.png" /><link rel="apple-touch-icon" sizes="60x60" href="icon_60x60.a4bea2cdeb17d15721bfb280f1505905.png" /><link rel="apple-touch-icon" sizes="58x58" href="icon_58x58.36090288ae1be803e6dd4b5d6d19dfeb.png" /><link rel="apple-touch-icon" sizes="48x48" href="icon_48x48.09614ee52de1df6fafeab1f16ad211a6.png" /><link rel="apple-touch-icon" sizes="40x40" href="icon_40x40.0c1ff8367f50dc25320d1e304d70e441.png" /><link rel="manifest" href="manifest.7fa4bf81bf98eff860156d48681ab0d9.json" /><meta name="mobile-web-app-capable" content="yes"><meta name="theme-color" content="#bcbfc2"><meta name="application-name" content="Providence Geeks"><link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png"><link rel="shortcut icon" href="icons/favicon.ico">

<link href="0.ca7c27d106cde55c9e35.css" rel="stylesheet"></head>

  <body>
    <div id="root">Loading ...</div>  
  <script type="text/javascript" src="0.b462a4b0ea0f53ce5925.js"></script><script type="text/javascript" src="5.fe98990a0cfe22bbeeb8.js"></script><script type="text/javascript" src="index.36d0a4091875de9b05d7.bundle.js"></script></body>

</html>

index.html (with html-critical-webpack-plugin)

<!DOCTYPE html>
<html lang="en">

  <head>
    <base href="/">
    <title>Providence Geeks</title>  
    <meta charset="utf-8">
    <meta name="description" content="The goal of Providence Geeks is to help Rhode Island’s digital innovators connect, collaborate, and ultimately make the City-State and its geeks info-technology leaders.">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">

  <meta name="apple-mobile-web-app-title" content="Providence Geeks"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="theme-color" content="#1a2930"><link rel="apple-touch-startup-image" href="icon_1024x1024.915638a2e87880eaf307a0baf1abb24e.png"><link rel="apple-touch-icon" sizes="512x512" href="icon_512x512.182f900ea4c1bf1883c11413b67364df.png"><link rel="apple-touch-icon" sizes="167x167" href="icon_167x167.7a223e8ceb81b340d9099a64df02e34a.png"><link rel="apple-touch-icon" sizes="180x180" href="icon_180x180.ae62ab86f45f19c58e995096c11bc9f7.png"><link rel="apple-touch-icon" sizes="152x152" href="icon_152x152.be7c2455346e0d37b432eb67ba150122.png"><link rel="apple-touch-icon" sizes="120x120" href="icon_120x120.a13fab9321c4d18b10fd0f77e80ea5b2.png"><link rel="apple-touch-icon" sizes="87x87" href="icon_87x87.04983c9d18d15515f3f4075be15ebbcc.png"><link rel="apple-touch-icon" sizes="80x80" href="icon_80x80.1e1e27f7d5dfda91b748947e59c20283.png"><link rel="apple-touch-icon" sizes="72x72" href="icon_72x72.d867a890d58b552d4f30996f52abf51b.png"><link rel="apple-touch-icon" sizes="60x60" href="icon_60x60.a4bea2cdeb17d15721bfb280f1505905.png"><link rel="apple-touch-icon" sizes="58x58" href="icon_58x58.36090288ae1be803e6dd4b5d6d19dfeb.png"><link rel="apple-touch-icon" sizes="48x48" href="icon_48x48.09614ee52de1df6fafeab1f16ad211a6.png"><link rel="apple-touch-icon" sizes="40x40" href="icon_40x40.0c1ff8367f50dc25320d1e304d70e441.png"><link rel="manifest" href="manifest.7fa4bf81bf98eff860156d48681ab0d9.json"><meta name="mobile-web-app-capable" content="yes"><meta name="theme-color" content="#bcbfc2"><meta name="application-name" content="Providence Geeks"><link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png"><link rel="shortcut icon" href="icons/favicon.ico">

<style type="text/css">
    @import url(//fonts.googleapis.com/css?family=Josefin+Sans:300,400,400i,700|Josefin+Slab:300,400,400i,700);*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}@-ms-viewport{width:device-width}html{box-sizing:border-box;-ms-overflow-style:scrollbar}*,:after,:before{box-sizing:inherit}body{font-family:Josefin Sans,sans-serif}
  </style>
  <link href="0.ca7c27d106cde55c9e35.css" rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link href="0.ca7c27d106cde55c9e35.css" rel="stylesheet"></noscript>
  <script>!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this);</script></head>

  <body>
    <div id="root">Loading ...</div>  
  <script type="text/javascript" src="0.b462a4b0ea0f53ce5925.js"></script><script type="text/javascript" src="5.fe98990a0cfe22bbeeb8.js"></script><script type="text/javascript" src="index.36d0a4091875de9b05d7.bundle.js"></script></body>

</html>
  1. adds inline <style> tag

index.html (with html-critical-webpack-plugin + subfont)

<!DOCTYPE html><html lang="en"><head>
    <base href="/">
    <title>Providence Geeks</title>  
    <meta charset="utf-8">
    <meta name="description" content="The goal of Providence Geeks is to help Rhode Island’s digital innovators connect, collaborate, and ultimately make the City-State and its geeks info-technology leaders.">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">

  <meta name="apple-mobile-web-app-title" content="Providence Geeks"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="theme-color" content="#1a2930"><link rel="apple-touch-startup-image" href="icon_1024x1024.915638a2e87880eaf307a0baf1abb24e.png"><link rel="apple-touch-icon" sizes="512x512" href="icon_512x512.182f900ea4c1bf1883c11413b67364df.png"><link rel="apple-touch-icon" sizes="167x167" href="icon_167x167.7a223e8ceb81b340d9099a64df02e34a.png"><link rel="apple-touch-icon" sizes="180x180" href="icon_180x180.ae62ab86f45f19c58e995096c11bc9f7.png"><link rel="apple-touch-icon" sizes="152x152" href="icon_152x152.be7c2455346e0d37b432eb67ba150122.png"><link rel="apple-touch-icon" sizes="120x120" href="icon_120x120.a13fab9321c4d18b10fd0f77e80ea5b2.png"><link rel="apple-touch-icon" sizes="87x87" href="icon_87x87.04983c9d18d15515f3f4075be15ebbcc.png"><link rel="apple-touch-icon" sizes="80x80" href="icon_80x80.1e1e27f7d5dfda91b748947e59c20283.png"><link rel="apple-touch-icon" sizes="72x72" href="icon_72x72.d867a890d58b552d4f30996f52abf51b.png"><link rel="apple-touch-icon" sizes="60x60" href="icon_60x60.a4bea2cdeb17d15721bfb280f1505905.png"><link rel="apple-touch-icon" sizes="58x58" href="icon_58x58.36090288ae1be803e6dd4b5d6d19dfeb.png"><link rel="apple-touch-icon" sizes="48x48" href="icon_48x48.09614ee52de1df6fafeab1f16ad211a6.png"><link rel="apple-touch-icon" sizes="40x40" href="icon_40x40.0c1ff8367f50dc25320d1e304d70e441.png"><link rel="manifest" href="manifest.7fa4bf81bf98eff860156d48681ab0d9.json"><meta name="mobile-web-app-capable" content="yes"><meta name="theme-color" content="#bcbfc2"><meta name="application-name" content="Providence Geeks"><link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png"><link rel="shortcut icon" href="icons/favicon.ico">

<script>try{new FontFace('\'Josefin Sans__subset\'','url(data:font/woff2;base64, url(data:font/woff;base64',{}).load()}catch(e){}</script><link rel="stylesheet" href="/subfont/fonts-1065551d8b.css"><link rel="preconnect" href="https://fonts.googleapis.com" crossorigin="anonymous"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"><style>
    *,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}@-ms-viewport{width:device-width}html{box-sizing:border-box;-ms-overflow-style:scrollbar}*,:after,:before{box-sizing:inherit}body{font-family:'Josefin Sans__subset', Josefin Sans,sans-serif}
  </style>
  <link href="0.ca7c27d106cde55c9e35.css" rel="preload" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link href="0.ca7c27d106cde55c9e35.css" rel="stylesheet"></noscript>
  <script>!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this);</script></head>

  <body>
    <div id="root">Loading ...</div>  
  <script type="text/javascript" src="0.b462a4b0ea0f53ce5925.js"></script><script type="text/javascript" src="5.fe98990a0cfe22bbeeb8.js"></script><script type="text/javascript" src="index.36d0a4091875de9b05d7.bundle.js"></script>

<script>(function(){var el=document.createElement('link');el.href='http://fonts.googleapis.com/css?family=Josefin+Sans:300,400,400i,700|Josefin+Slab:300,400,400i,700'.toString('url');el.rel='stylesheet';document.body.appendChild(el)}())</script><noscript><link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Josefin+Sans:300,400,400i,700|Josefin+Slab:300,400,400i,700"></noscript></body></html>
  1. Critical CSS gone?
thescientist13 commented 5 years ago

Update! After doing some research, it looks like only woff and woff2 need to be supported, as support goes as far back as IE9 https://caniuse.com/#search=woff

DevLab2425 commented 4 years ago

What's the status of this one @thescientist13. The latest performance audit with the font-display: swap property doesn't report on the font being render blocking. Is there more needed for this issue?

thescientist13 commented 4 years ago

I was still trying to fix up #153 by bundling the Google fonts with the project, rather than hosted off of googleapis.