| @ -0,0 +1,159 @@ | |||
| ## Results | |||
| ## Optimize Images | |||
| Since images are the largest portion of a website's size, optimizing and reducing the | |||
| size of images will greatly decrease load time. In a perfect web development world, everyone would | |||
| use SVG images which are extremely small and don't need compression. However, since most of us | |||
| use PNG and JPEG images I wrote a script to automatically optimize images for the web. | |||
| ``` | |||
| #!/bin/bash | |||
| # Simple script for optimizing all images for a website | |||
| # | |||
| # @author Jeffery Russell 7-19-18 | |||
| WIDTH="690>" # the ">" tag specifies that images will not get scaled up | |||
| folders=("./entries" "./img") | |||
| for folder in "${folders[@]}"; do | |||
| for f in $(find $folder -name '*.jpg' -or -name '*.JPG'); do | |||
| convert "$f" -resize $WIDTH "$f" | |||
| jpegoptim --max=80 --strip-all --preserve --totals --all-progressive "$f" | |||
| done | |||
| for f in $(find $folder -name '*.png' -or -name '*.PNG'); do | |||
| convert "$f" -resize $WIDTH "$f" | |||
| optipng -o7 -preserve "$f" | |||
| done | |||
| done | |||
| ``` | |||
| When ran, this script will go through the img, and entries folder recursively and optimize all | |||
| the images in there. If an image is more than 690px wide, it will scale it down to save size. In | |||
| most cases, it is useless to have images with a width greater than 690 because it will just get | |||
| scaled by the client's web browser. | |||
| If you are running a Debian based linux distro, you can download the dependencies for this script with | |||
| the following commands: | |||
| ``` | |||
| apt-get install jpegoptim | |||
| apt-get install optipng | |||
| ``` | |||
| The goal of this script is to make most of the images under 100kb for the web. It is ok to have | |||
| a few images above 100k, however, you should really avoid having images above 200kb. | |||
| ## Take advantage of Async calls | |||
| One of the largest benefits of node is its async abilities where code is executed in a | |||
| multi-threaded fashion. This can become a callback hell if not handled correctly, but, with | |||
| good code structure it can become very easy. When code is executed in parallel, you can greatly | |||
| decrease run time by doing other stuff while waiting on costly file IO or database calls. | |||
| The problem with async code is that it is hard to coordinate. Node has a lot of ways to handel | |||
| synchronization, but, I prefer to use [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) | |||
| . Here is a simple example where async code can become inefficient. | |||
| Good Code Async: | |||
| ``` | |||
| Promise.all([includes.printHeader(), | |||
| require(file).main(filename, request), | |||
| includes.printFooter()]).then(function(content) | |||
| { | |||
| res.write(content.join('')); | |||
| res.end(); | |||
| }).catch(function(err) | |||
| { | |||
| console.log(err); | |||
| }); | |||
| ``` | |||
| Bad Async Code: | |||
| ``` | |||
| includes.printHeader(res).then(function() | |||
| { | |||
| return require(file).main(res, filename, request); | |||
| }).then(function() | |||
| { | |||
| return includes.printFooter(res); | |||
| }).catch(function(err) | |||
| { | |||
| console.log(err); | |||
| }) | |||
| ``` | |||
| In the first example three blocks of async code are executed in parallel and in the second example | |||
| three blocks of async code are executed one after another. Many people may initially do the second | |||
| option because it may seem like you have to create and render the footer after you render the header | |||
| and body of the page. | |||
| A great way to handel async calls is by having most of your methods returning promises which resolve to | |||
| the HTML or DB information that they produce. When you run Promise.all, it returns an array of the | |||
| objects which enables you to preserve the order ie header, body, footer. After you do this for all of | |||
| your code, it creates a "perfect" async tree which actually runs very fast. | |||
| Another Good Async Example: | |||
| ``` | |||
| /** | |||
| * Calls posts and sidebar modules to render blog contents in order | |||
| * | |||
| * @param requestURL | |||
| * @returns {Promise|*} | |||
| */ | |||
| main: function(requestURL) | |||
| { | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| Promise.all([renderPost(requestURL), | |||
| require("../sidebar/sidebar.js").main()]).then(function(content) | |||
| { | |||
| resolve(content.join('')); | |||
| }).catch(function(error) | |||
| { | |||
| reject(error); | |||
| }) | |||
| }); | |||
| } | |||
| ``` | |||
| ## Client Side Caching | |||
| ``` | |||
| var eTag = crypto.createHash('md5').update(content).digest('hex'); | |||
| result.writeHead(200, {'Content-Type': 'text/css', 'Cache-Control': | |||
| 'public, max-age=2678400', 'ETag': '"' + eTag + '"', | |||
| 'Vary': 'Accept-Encoding'}); | |||
| result.write(content); | |||
| result.end(); | |||
| cache.put(path, content); | |||
| ``` | |||
| ## Server Side Caching | |||
| ## Enable Compression (GZIP) | |||
| ## Purge Unused CSS Definitions | |||
| ## Minify CSS and Javascript | |||