Website Performance Optimization

Pre-requisites

This project requires the following programs to build from scratch:

Assuming that you have node already, the others are easily installed.

On Linux, this can be done as follows:


$ sudo apt-get install yui-compressor
$ sudo apt-get install imagemagick
$ sudo apt-get install graphicsmagick
$ sudo npm install -g gulp

For other OS's, please see the instructions on the respective project websites. YUI Compressor, ImageMagick, GraphicsMagick.

Once these are installed, you can download and build website as follows:


$ git clone https://github.com/alcarruth/frontend-p4-website-optimization.git
$ cd frontend-p4-website-optimization
$ npm install
$ gulp --gulpfile pizza_app/gulpfile.js
$ gulp --gulpfile mobile_portfolio/gulpfile.js

Project Overview

The original nanodegree project has been extensively refactored. First, the project has been split into two, Pizza App and Mobile Portfolio. Each of these has its own subdirectory containing src and dist subdirectories, and a gulpfile.js. However, the two projects share a single node_modules directory and package.json file in the root directory of this repository.

Mobile Portfolio

The mobile portfolio site was optimized and tested with PageSpeed Insights. The following optimizations were made:

Stylesheets:

Scripts:

images:

Pizza App

The Pizza App site has been completely overhauled. The main.js file was split into six files:

Extensive documentation can be found in the comments in these files, but here is an overview.

pizza_app.js

pizza_app.js is the main entry point into the code. It defines, instantiates and initializes a PizzaApp object.


window.pizzaApp = PizzaApp();

pizza_designer.js

pizza_designer.js contains the code for generating the pizzas for the menu. A big change is that it only generates what I'm calling semantic pizzas, that is JavaScript objects containing no HTML. These are later combined with a view template to create the actual HTML for the page.

So pizza_designer.js includes the lists of nouns, adjectives and ingredients from the original. The random generator code has been cleaned up and modularized in an attempt to make it more cohesive, comprehensible and "easy on the eyes". And the two default pizzas, The Udacity Special, and The Cameron Special, which were previously a part of index.html, are now semantic secialty pizzas in the pizza designer.

pizza_menu.js

pizza_menu.js contains the code that generates the menu pizzas, using PizzaDesigner and Mustache to fill the menu items portion of the page. It also contains the code to update the pizza sizes when the slider is changed.

sliding_pizzas.js

sliding_pizzas.js contains the code for the crazy sliding pizzas page background. It contains two class definitions, SlidingPizza, and SlidingPizzasBackground. Again, this code has been tidied up considerably, moving non central functionality like timing and animation to separate files. This results in code that allows one to focus on the core functionality with minimal obfuscation.

timer.js

The timing code in the original is very cool as Cameron has testified, but I felt like it cluttered things up considerably. It was/is used in three places

  1. generating pizzas, 2. resizing pizzas and 3. scrolling updates, and each of these had essentially the same timer code. I tried to abstract the similar timing code and ended up with a function timerWrap() which could wrap another function and do the timing. This generalization resulted in much cleaner code for the functions that were being timed.

animation_loop.js

animation_loop.js is probably the most interesting of the bunch. In trying to get my head around requestAnimationFrame() I realized that I had to use the tail-recursive calls as in all the examples, but I didn't want the loop to run forever, only when scrolling. So I needed to be able to exit the loop when we were no longer scrolling and start it again when a scroll event happened.

After wrestling with it in situ, I decided to pull it out into a separate function so that updatePositions() could focus on updating positions and not have to worry about the bigger picture.

Eventually I realized that there was an inherent mis-match here between scroll events and a scrolling condition, the former being ephemeral or instantaneous, and the latter having some longer duration. I wanted the animation to start when a scroll event was received continue until we were no longer scrolling, which meant that we had not received a scroll event in some period of time.

So, anyway, separating the animation management from the position updating allowed me to focus on one at a time, without being concerned with the other. And it seems that the resulting code animationLoop() says something fundamental about the relationship between events and conditions that might be re-used elsewhere, certainly in animation but possibly beyond that.

Status

I'm still playing with it. Most of the optimization targets were met by just refactoring the for loops, moving unnecessary code out of inner loops and maintaining in memory a list of pizza elements so that there was no need to query the document each time around.

All of the timing tests seem to pass with flying colors except for the 60 frames per second. In DevTools, the CPU time required for each frame is usually around 10 ms, but the rest of the frame is hollow with nothing in it.

I've tried a number of things to improve the timing

I also tried:

License

Probably GNU or MIT or BSD ... I don't know.