loading...

SPA with 4x100% lighthouse score - Part 2: Building an app

rezi profile image Tomas Rezac ・5 min read

Alt Text

Setting up the app

In last part we decided to go for JS, Svelte js framework/compiler and Sapper. If you want to know more about my reasoning, why I picked these technologies, and you haven't read my previous article, go and read it ;)

This time we will attempt to build together a demo app and hopefully reach our goal. In this part of the serie, we just finetune the Sapper hellow world app to max lighthouse score. In next part we'll build on top of our skeleton and create a fancy weather forecast app.

Beware, this is not an article about Svelte/Sapper, so we will go through the implementation in rather fast pace. But don't worry, if you dont know these technologies, I'll guide you through.

Be sure you have node js, npm and npx installed. Now let's get a hello world Sapper app running.

npx degit "sveltejs/sapper-template#rollup" lighthouse-app
cd lighthouse-app

npm install
npm run dev

Our Sapper app is now running on http://localhost:3000
If we run the lighthouse audit now, we are in green numbers but we won't get 4x100%. There are more reasons for getting 100% score:

  1. Page is server rendered every time we hit a url
  2. CSS assets are loaded via <link> element
  3. Image is not in future format (webP)
  4. Image doesn't have optimal size
  5. Page metadata are missing
  6. Missing apple touch icons
  7. App is not running neither on http2 nor https
  8. Http is not redirected to https

Let's fix them one by one:

1) Page is server rendered all the time we hit a url

This one is easy, Sapper has another set of commands for exporting web app as a set of static pages and assets. Just run the build and commands like this instead of the one metioned above

npm run export & npx serve __sapper__/export

Done app is running on http://localhost:5000 and the performace is already rated to 100% ;)

2. CSS assets are loaded via <link> element

The <link> behavior is not that bad, css files are made per component. So once you hit other page with same component, you already have the CSS cached by service worker. The default behavior seems to be good for apps with heavily repeating components, not for our app ;) Probably better for us, in terms of speed, is lighthouse's proposed way of injecting crucial css inline to the <head> element and thus avoiding the render-blocking request for sepearate css files. Let's change it.
Open rollup.config.js and change emitCss flag in svelte plugin to false.
There is one more render blocking request and it is the global.css. Go to src/template.html and comment it out (we will maybe use it later).
For now lets use some base css directly in template.html. Add following code above the commented stylesheet link:

     <style>
      html {
        box-sizing: border-box;
        font-size: calc(14px + 6 * (100vw / 1280));
      }

      @media screen and (min-width: 1280px) {
        html {
          font-size: 20px;
        }
      }

      body {
        margin: 0;
        font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen,
          Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
        line-height: 1.5;
        color: #333;
      }
    </style>

Congratulations, you just made your app half way responsive.

The weird font size calculation is my minified version of something called poly fluid sizing. your base font size now scales from 14px to 20px as the screen grows to 1280px. To whatever you apply size in rems, will scale with it. 1rem is now something between 14-20px depending on screen width.

Rebuild the app and voilà. Even with the regular npm run dev we get 100% performance.

3) Image is not in future format (webP)

Just take the image static/great-success.png and turn it first to jpg (so we loose alpha channel). Then convert it for instance with webP convertor. Copy the image to the static folder and adjust its html in src/routes/index.svelte accordingly.

4) Image Doesn't have optimal size

Not only it has wrong size, it is also one and only size the img tag offer. We can do better, we will use <picture> with srcset tag. Go to responsivebreakpoints.com and upload our webP img, set max resolution to 400 (it is the image max size it can get in our app). Save images to static folder. Replace old img tag with new picture one, which was generated by the tool. Done.

5) Page metadata are missing

Go to src/routes/index.html and any other page(route) you want to optimize and add following metatag into the <svelte:head> tag.

 <meta
    name="description"
    content="Welcome to sapper app" />

Now we get 100% SEO rating on every page where we added the meta description

6) Missing apple touch icons

Grab some nice simple icon and process it with realfavicongenerator.
Copy generated code to src/template.html and files to static folder

7) App is not running neither on http2 nor https

Now it is time to move our app to the internet. We could also run our local server with npm spdy server and self signed certificates. But it won't prepare us for real internet conditions. We will deploy our app to Netlify. it is free for testing purposes, it is fast, works as CDN, and even provides serverless functions and auth middleware.

Easiest setup is to create netlify app from github repository. Create a repository on github and push your lighthouse-app to it.
Now sign up for netlify. In your netlify site page, click New site from git. Once you are in step 3, you will be asked for Build command and Publish directory. Fill first with npm run export and second with __sapper__/export. Then click Deploy site

Each time you push to a chosen branch in github, netlify hooks into it and builds and deploys a new version.

8) Http is not redirected to https

This is already done, as Netlify provides https, http/2 and redirects out of the box.

Now we only need to measure the results. Copy the url netlify generated for your web app. For me it is https://eloquent-dijkstra-d8bd7b.netlify.com/.

Now go to the new online measuring tool (lighthouse) web.dev. We should measure by this one, not by plugin in chrome or dev tools. The web.dev tool give you more stable results, not influenced by quality of your internet connection and your computer's power.

Here are the results, not bad, considering, it took us 10 minutes from start to end.

Lighthouse audit results

Lighthouse audit times

We have a skeleton for a future app. We are backed by full featured component based framework with all the things like 2-way-binding, state management, routing, animations etc.

In the next part of this serie we try to turn this hello world app to a more real-world app example: a Weather forecast app. We will see whether we are able to keep our perfect score with a growing complexity and connection to an external API.
In the meantime you should go and learn some basics about svelte and sapper.

See you next time ;)

Discussion

markdown guide