or, the âfine-tune the machineâ part.
if your performance is poor, it should be considered Bug #1 and a huge reason people wonât use your product. if your page takes more than a couple seconds to load, people will abandon it and potentially never come back again. a speedy app shows shows consideration for your userâs time â the faster they can get what they need done and move on with their life, the more they will consider it a indispensable tool.
make sure you measure how your app is actually behaving in the wild! just because it âworks for youâ in the lab of your home or office doesnât mean it actually works for the wide variety of people using your app with a wide variety of connection speeds, browsers, and usage patterns.
there are a lot of facets to attaining performance gains. Google puts out a model called RAIL as a helpful mental model when thinking about prioritizing work here. in general, Google dominates in this area of knowledge â most of the resources below are from them. hereâs a list of approaches to consider.
one of the simplest things you can do is reduce the number of HTTP requests to your server. by bundling together your assets (JS, CSS, and traditionally your icons into a sprite file) you can reduce the number of round-trip requests from clientâserver and improve speed drastically. you can get this for free basically via your bundler.
JS at the bottom
place JS <script>
tags at the bottom of your HTML (near the </body>
closing tag) whenever possible to improve page load. JS blocks page execution so saving it for the end makes your page more usable faster. additionally, one can use the async
or defer
attributes on a <script>
tag to make execution faster of other parts of the page. async
will download the file asynchronously and pause HTML execution once itâs received, and not necessarily in order. defer
on the other hand waits until HTML execution is done, but it will execute in order.
however, on the other hand, HTTP2 turns the previous item in this list on its head and says, âno need to bundle assets anymore!â in practice, iâve found that you shouldnât go whole hog and unbundling everything â the right balance is needed. it will take playing with your app and itâs particular needs a little bit to see what things can be unbundled vs. not to see what works best.
ahh, this falls into the perceptual speed, or âfake it âtil you make itâ đ. here, we add visual assets that load quickly on page load to give the user the impression that something is happening. here are some examples of this looks like in practice.
gives your app offline capability and faster loading times.
PRPL pattern related to Service Workers is the standard being put out to:
you usually donât need all the images to load on page load. the browser does optimizations of its own to load what it considers more important (JS and CSS) but you can also help out by reducing the amount of traffic your user has to download for your app and, likewise, reduce the amount of traffic your server has to handle.
below the fold
no need to load the images below whatâs currently visible on the page. you can load them in as the user scrolls! check out any page with video thumbnails on YouTube for an example of this works as you scroll (i point it out because i implemented this at YouTube back in the day when i worked there đ). check out IntersectionObserver as a way of doing this.
image format
pick an appropriate image format. PNG is lossless and will tend to be larger. JPG is probably your best bet. there are a new set of image formats coming out but theyâre not widely supported yet.
placeholders
again, you can add placeholders for your images, perhaps getting a . check out any Medium article to get a sense of this.
sprites
when it makes sense, you can use a sprite file to combine your images. however, as mentioned above, if HTTP2 is enabled on your server, you might not even need this.
optimized images (or, consider using SVGs instead of images)
there are several tools available to reduce the sizes of your images.
predetermined size for images
a surprisingly expensive operation for your browser is resizing your images on the fly to fit into a particular space, especially if itâs a high quality image. if you resize your images beforehand to be the correct size for where theyâre going to be displayed youâll gain some benefits in page responsiveness.
loading attribute
a new HTML attribute that will help hint to the browser that an image can be loaded later. just add loading="lazy"
to your images.
there are a couple of HTML attributes that can work in your favor:
preload, prefetch, preconnect
these attributes on your <link>
tags can optimize your resources loading in more quickly.
loading attribute
a new HTML attribute that will help hint to the browser that an image can be loaded later.
importance
not supported anywhere yet but would let you specify priority hints on particular assets.
optimizing fonts is probably one of the last things you should look at, itâs more of a micro-optimization. but you can fine-tune unicode ranges and things like that. WOFF is widely supported these days and safe to go with these days.
my backend-optimization-fu isnât the most robust but i can offer a couple suggestions to delve into.
HTTP Cache-Control and ETag
make sure your assets have caching headers on them.
static files
donât have your app serving static resources. make sure nginx is configured to serve up static files without hitting your app server.
gzip
goes without saying maybe, but your server should have this enabled when serving assets.
in-memory key-value store
tools like redis will cache data in memory which would be much faster than retrieving it from your DB or somewhere else.
Save-Data HTTP header
an HTTP header that hints to your server that you should serve up a more lightweight page, if possible.
if youâre app will need to be searchable by search engines or will need to be shared on social media, then youâre going to need to look at server-side rendering. the bonus to this is that it can help perceived load time for the user since the user will get HTML immediately instead of waiting for your JS bundle to download, parse, and execute.
on the other hand, if youâre building an app that is more in the lines of something like Gmail that shouldnât be searchable, then by all means, skip the SSR step. for a compelling argument towards this path check out this article.
if thatâs all confusing (and it is for most people), hereâs a great article to help you decide and weigh the benefits.
a couple things to keep in mind in the JS world during runtime and load. JS is probably your most expensive resource. since JS is single-threaded and tied with UI updates, youâve got about 16ms of time to do any processing before your app starts feeling janky.
code splitting
load only whatâs really necessary right now and lazy-load other stuff later. webpack, along with a bunch of other libraries, provide support for this. i would recommend that you consider at least splitting off:
React.lazy + Suspense
speaking of code splitting, newer versions of React offer the ability to dynamically split on components vs. splitting on routes. itâs awesome. word of warning that this isnât working on server-side quite yet at the time of writing (Jan 2019).
dynamically loading modules
you can load in JS on demand via the import()
function.
minification
tools like webpack and the like will take your code and reduce its size significantly.
tree shaking
discover whatâs in your bundle but not being actually used. webpack also provides supports for this.
throttle/debounce events
events such as mousemove send a ton of events to your JS. you should take a sample of them every so often (reducing it to at least once every 33ms is a good start).
scrolling speed
donât run (too much) JS when scrolling. if you do, take a look at the âpassiveâ option to make sure your scrolling stays smooth.
requestAnimationFrame
use this function instead of setTimeout for visual updates.
requestIdleCallback
a new function (not supported by all browsers yet) that lets you do work when the user is inactive or when the browser has spare cycles it can give you.
React.PureComponent / React.memo
a very simple win for your React components is to use React.PureComponent or React.memo. they will help reduce the amount of times your component needs to re-render.
in general, fine-tuning CSS is one of the hardest areas to squeeze out performance gains and i recommend saving it for last. that being said, there are a couple areas that are worth mentioning.
never use the *
selector
CSS is parsed from right-to-left. that is to say, for a rule like html body div span a
the browser has to start with the a
tag, finding alllll the anchor tags on a page before parsing the rest of the rule. thus, an especially expensive rule is something like .someClass *
which forces the browser to look at allll elements on the page first and then match by the class.
reduce the number of descendant/child selectors
along with the previous rule, in general, you should reduce the number of selectors such that your browser has to do less of the right-to-left traversal of the DOM tree. a rule like .anchorTag
is much more performant than something like html div span a
which effectively might match the same thing, but the former rule is more specific in its intention.
transform: translateZ(0)
the classic rule to put an element on the GPU layer to have it render faster. the will-change
rule mentioned above should supersede this one but will-change
isnât completely supported yet (not on Edge, as per usual).
contain not widely supported yet but can ostensibly give you some performance gains during rendering.
will-change gives a performance hint to the browser on how a particular element will change over time.
AMP was a strongarming/vendor lock-in technology from Google that promised speed to users but really ended up being a disingenous play by Google to lock-in more to the Google ecosystem. iâm not going to hyperlink to it here because itâs not worth doing so â just know that as, a web developer, you should push back on any efforts to add AMP rendering to your stack.
on top of the obvious category of speed, is analyzing your product for usage patterns. are your users using the product as you intend it? are they using it in creative ways that perhaps you never imagined? with insights into how your users are navigating around your product, youâll gain insight into what areas of your app need improvement, in addition to which areas of your product are the most popular and perhaps deserve even more devoted attention. youâll want to track things like:
tracking clicks will help you gain insight on what your users are using and whether they are finding your UI usable in the first place.
an example of this is your user going from searchâproduct pageâdetails tab. or, perhaps most users donât even use search or the home page and get to your product pages directly and organically via links passed around by friends! in that example, you could consider that your homepage or search might not need to be bundled by default into your app and loaded in later on demand as needed. optimize your app for the most common usage patterns that you observe your users to be taking.
looking at the browsers your users use (especially mobile vs. desktop skew) will help inform your appâs direction and support you should be providing.
maybe youâre big in Japan and you donât even know it! embrace that there are nearly 200 countries in the world and your user base might not be in your backyard or even on your continent. (just wait til we have people on Mars and then start talking to me about latency speeds đ)
performance
API to measure your app's JS performance.