building-performant-expressionengine-sites



building-performant-expressionengine-sites

0 0


building-performant-expressionengine-sites

#pEErfmatters - Building Mobile Optimized ExpressionEngine Sites

On Github shaekuronen / building-performant-expressionengine-sites

#pEErfmatters

Building Mobile Optimized ExpressionEngine Sites

#pEErfmatters

Building Attempting To Build Mobile Optimized ExpressionEngine Sites

Why Perfomance Matters

For e-commerce sites, a .1 second increase will decrease conversions by 1%. A 1 second increase will reduce conversions by 15%. A 5 second increase will reduce conversions by 75%.

Benchmarks

  • .1s - Feels Instantaneous
  • 1s - Keeps User Focus
  • 2s - 77% of Users Expected Load Time
  • 3s - 57% Likely To Leave

Mobile Performance Issues

  • HTTP Requests More Costly
  • Less Powerful Processors

Mobile Performance Guidelines

  • Concatenate and Minify JS + CSS Files
  • Defer Loading of JS
  • Optimize and Serve Pre-Scaled Images
  • Inline Small Images, JS, and CSS
  • Only Load Necessary Assets
  • Rev Assets to Improve Caching
  • Serve Static Assets From CDN
  • Compress Components with GZip

Build Strategy

AutoMin

AutoMin is an ExpressionEngine module and extension that automates the combination and compression of your source files and currently supports CSS, JavaScript, LESS, and HTML compression.

AutoMin Code


  <!-- HEAD -->
  <!-- css -->
  {exp:automin:css
    attribute:rel="stylesheet"
  }
  <!-- reset -->
  <link rel="stylesheet" href="/assets/css/vendor/reset/base.css">
  <!-- end reset -->

  <!-- vendor -->
  <link rel="stylesheet" href="/assets/css/vendor/flexslider.css">
  <!-- end vendor -->

  <!-- site -->
  <link rel="stylesheet" href="/assets/css/site/global/globals.css">
  <link rel="stylesheet" href="/assets/css/site/global/header.css">
  <link rel="stylesheet" href="/assets/css/site/global/footer.css">
  <link rel="stylesheet" href="/assets/css/site/global/hero_image.css">
  <link rel="stylesheet" href="/assets/css/site/global/callouts.css">
  <link rel="stylesheet" href="/assets/css/site/global/featured_games.css">
  <link rel="stylesheet" href="/assets/css/site/global/flip_animation.css">
  <link rel="stylesheet" href="/assets/css/site/global/diagonal_lines_bg.css">
  <link rel="stylesheet" href="/assets/css/site/pages/home.css">
  <link rel="stylesheet" href="/assets/css/site/pages/team.css">
  <link rel="stylesheet" href="/assets/css/site/pages/games.css">
  <link rel="stylesheet" href="/assets/css/site/pages/careers.css">
  <link rel="stylesheet" href="/assets/css/site/pages/press.css">
  <link rel="stylesheet" href="/assets/css/site/pages/santa.css">
  <!-- end site -->

  <!-- responsive -->
  <link rel="stylesheet" href="/assets/css/site/responsive/skeleton.css">
  <link rel="stylesheet" href="/assets/css/site/responsive/0_500px.css">
  <link rel="stylesheet" href="/assets/css/site/responsive/500_750px.css">
  <link rel="stylesheet" href="/assets/css/site/responsive/750_1000px.css">
  <link rel="stylesheet" href="/assets/css/site/responsive/1000_1250px.css">
  <link rel="stylesheet" href="/assets/css/site/responsive/1250_infinity.css">
  <link rel="stylesheet" href="/assets/css/site/responsive/retina_display.css">
  <!-- end responsive -->
  {/exp:automin:css}
  <!-- end css -->
  <!-- END HEAD -->

  <!-- FOOTER -->
  <!-- js -->

  {exp:automin:js}
  <!-- vendor -->
  <script type="text/javascript" src="/assets/js/vendor/jquery-1.10.1.min.js"></script>
  <script type="text/javascript" src="/assets/js/vendor/jquery-migrate-1.2.1.min.js"></script>
  <script type="text/javascript" src="/assets/js/vendor/jquery.flexslider-min.js"></script>
  <script type="text/javascript" src="/assets/js/vendor/matchMedia.js"></script>
  <script type="text/javascript" src="/assets/js/vendor/picturefill.js"></script>
  <script type="text/javascript" src="/assets/js/vendor/jquery.lazyload.js"></script>
  <script type="text/javascript" src="/assets/js/vendor/prevent_console_errors.js"></script>
  <script type="text/javascript" src="/assets/js/vendor/ios-orientationchange-fix.js"></script>
  <!-- end vendor -->

  <!-- site -->
  <script type="text/javascript" src="/assets/js/site/global_object.js"></script>
  <script type="text/javascript" src="/assets/js/site/lazyload_init.js"></script>
  <script type="text/javascript" src="/assets/js/site/thought_bubbles.js"></script>
  <script type="text/javascript" src="/assets/js/site/flexslider_init.js"></script>
  <script type="text/javascript" src="/assets/js/site/header_nav_mobile.js"></script>
  <script type="text/javascript" src="/assets/js/site/featured_games.js"></script>
  <script type="text/javascript" src="/assets/js/site/nav_hover.js"></script>
  <script type="text/javascript" src="/assets/js/site/remove_empty_text_nodes.js"></script>
  <script type="text/javascript" src="/assets/js/site/scroll_to_element.js"></script>
  <script type="text/javascript" src="/assets/js/site/modal.js"></script>
  <script type="text/javascript" src="/assets/js/site/vertically_center.js"></script>
  <!-- end site -->
  {/exp:automin:js}

  <!-- page specific js -->
  {if embed:page_specific_js}
    {embed:page_specific_js}
  {/if}
  <!-- end page specific js -->

  <!-- end js -->
  <!-- END FOOTER -->

  <!-- LAYOUT -->
  {embed="globals/head"

    page_body_class="page-careers"

  }

  {embed="globals/header"}

  {embed="globals/hero_image"}

  {embed="careers/callout1"}

  {embed="careers/benefits_and_perks"}

  {embed="careers/open_positions"}

  {embed="globals/footer"}

  {embed="globals/scripts"

    page_specific_js='<script type="text/javascript" src="path/to/file.js"></script>'

  }

  {embed="globals/foot"}


AutoMin Result


  <!-- HEAD -->
  <link href="/assets/cache/94c0ef419b3fbad19c8228a5c90d54cc.css?modified=1385405187" rel="stylesheet">
  <!-- END HEAD -->

  <!-- FOOTER -->
  <script src="/assets/cache/600f856bb79d1db6ada3688551fb830f.js?modified=1372887497"></script>
  <!-- END FOOTER -->

Picturefill

Picturefill allows you to specify different src attributes for an image, each image file corresponding to a different media query. Thus a large image will be fetched if – and only if – the screen size requires it, and likewise a mobile-optimised version of an image will be fetched and displayed as appropriate.

Picturefill Code


  <!-- hero image -->
  <div class="hero-image-wrapper">

    <div class="container">

      {exp:channel:entries
        channel="hero_images"
        search:hero_image_page_visibility="{segment_1}"
        limit="1"
        disable="categories|category_fields|member_data|pagination"
        dynamic="no"
      }
      <a href="{hero_image_url}" class="hero-image-link">

        <!-- responsive images using picturefill.js -->
        <span data-picture="" data-alt="" class="hero-image">

          <!-- for browsers that don't support media queries fallback to full size image -->
          <span class="hero-image" data-src="{hero_image:1000px}"></span>
          <!-- end for browsers that don't support media queries fallback to full size image -->

          <span class="hero-image" data-src="{hero_image:500px}" data-media="(max-width: 499px)"></span>
          <span class="hero-image" data-src="{hero_image:750px}" data-media="(min-width: 500px) and (max-width: 749px)"></span>
          <span class="hero-image" data-src="{hero_image:1000px}" data-media="(min-width: 750px)"></span>

          <!-- fallback for non-js browsers -->
          <noscript>
            <img src="{hero_image:thumbs}" alt="">
          </noscript>
          <!-- end fallback for non-js browsers -->

        </span>
        <!-- end responsive images using picturefill.js -->

      </a>
      {/exp:channel:entries}

    </div>

  </div>
  <!-- end hero image -->

Picturefill Result


  <div class="hero-image-wrapper">

    <div class="container">

      <a href="/careers" class="hero-image-link">

        <span data-picture="" data-alt="" class="hero-image">

          <span class="hero-image" data-src="http://www.example.com/images/uploads/hero_images/_1000px/image.jpg"></span>

          <span class="hero-image" data-src="http://www.example.com/images/uploads/hero_images/_500px/image.jpg" data-media="(max-width: 499px)"></span>

          <span class="hero-image" data-src="http://www.example.com/images/uploads/hero_images/_750px/image.jpg" data-media="(min-width: 500px) and (max-width: 749px)"></span>

          <span class="hero-image" data-src="http://www.example.com/images/uploads/hero_images/_1000px/image.jpg" data-media="(min-width: 750px)">
            <img alt="" src="http://www.example.com/images/uploads/hero_images/_1000px/image.jpg">
          </span>

          <noscript>&lt;img src="http://www.example.com/images/uploads/hero_images/_thumbs/image.jpg" alt=""&gt;</noscript>

        </span>

      </a>

    </div>

  </div>

EE File Manager Config

jQuery Lazyload

Lazy Load delays loading of images in long web pages. Images outside of viewport will not be loaded before user scrolls to them.

Using Lazy Load on long web pages containing many large images makes the page load faster. Browser will be in ready state after loading visible images.

jQuery Lazyload Code


  <!-- callout1 -->
  <div class="callout-copy-left-image-right gradient-border">

    {exp:channel:entries
      channel="callouts"
      url_title="games-page-callout-1"
      disable="categories|category_fields|member_data|pagination"
      dynamic="no"
    }
    <div class="container">

      <div class="two-thirds column">
        <h1>{callout_heading}</h1>
        <p>{callout_body_copy}</p>
      </div>

      <div class="callout-image one-third column">
        <a href="{callout_link_url}" target="_blank">
          <img class="lazyload" src="/assets/img/global/lazyload/transparent.gif" data-original="{callout_image}" width="280" height="280">
        </a>
      </div>

    </div>
    {/exp:channel:entries}

  </div>
  <!-- end callout1 -->

jQuery Lazyload Result


  <div class="callout-copy-left-image-right gradient-border">

      <div class="container">

          <div class="two-thirds column">

              <h1>Cosby sweater American Apparel</h1>
              <p>Cray craft beer sint, in keytar gluten-free PBR&B flannel blog stumptown. Trust fund VHS leggings mumblecore swag McSweeney's post-ironic, Brooklyn Shoreditch quis sartorial. Dolore meggings plaid, blog Vice accusamus forage dolor trust fund cornhole squid.</p>

          </div>

          <div class="callout-image one-third column">

              <a href="http://www.example.com" target="_blank">
                  <img class="lazyload" src="http://www.example.com/images/uploads/image.png" data-original="http://www.example.com/images/uploads/image.png" width="280" height="280" style="display: inline;">
              </a>

          </div>

      </div>

  </div>

CE Cache

CE Cache allows you to store and retrieve parts of your templates (fragment caching) so they don’t have to be run each time the page is loaded. Since much of the heavy lifting doesn’t have to be run on each page-load, your performance increases can be astonishing.

CE Cache also supports full-page (static) caching. The static driver, which caches entire pages, uses a combination of file-based caching and .htaccess magic to serve your pages. This allows the cached files to completely circumvent ExpressionEngine®, and to render very quickly.

CE Cache - Static Driver Installation

  • Add + Enable Module
  • Create system/expressionengine/static directory and set permissions to 777
  • Upload _static_cache_handler.php to the root directory
  • Add the {exp:ce_cache:stat:ic} tag to a template on each page
  • Add mod_rewrite rules to .htaccess
  • Update config.php
  • Configure cache breaking rules

CE Cache .htaccess


  <ifmodule mod_rewrite.c="">
    RewriteEngine On

    #------------------- remove trailing slash -------------------
    RewriteCond %{REQUEST_URI} !^/system [NC]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.+)/$ /$1 [R=301,L,QSA]

    #------------------- index.php -------------------
    #strip index.php from the URL if that is all that is given
    RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^/]+/)*index\.php\ HTTP/
    RewriteRule ^(([^/]+/)*)index\.php$ http://%{HTTP_HOST}/ [R=301,NS,L,QSA]
    #strip index.php/* from the URL
    RewriteCond %{THE_REQUEST} ^[^/]*/index\.php/ [NC]
    RewriteRule ^index\.php/(.+) http://%{HTTP_HOST}/$1 [R=301,L,QSA]

    #------------------- CE Cache Static Driver -------------------
    RewriteCond %{REQUEST_URI} !^/system [NC]
    RewriteCond %{QUERY_STRING} !ACT|URL [NC]
    RewriteCond %{REQUEST_METHOD} !=POST [NC]
    RewriteCond %{DOCUMENT_ROOT}/static/ce_cache/e24b65/static%{REQUEST_URI}/index\.html -f
    RewriteRule (.*) /_static_cache_handler.php%{REQUEST_URI}/index\.html [L,QSA]

    #------------------- EE -------------------
    #rewrite all non-image/js/css urls back to index.php if they are not files or directories
    RewriteCond $1 !\.(css|js|gif|jpe?g|png) [NC]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /index.php/$1 [L,QSA]
  </ifmodule>

CE Cache config.php


  $config['ce_cache_static_enabled'] = 'yes';
  $config['ce_cache_static_path'] = '/path/to/web/content/static';
  $config['ce_cache_seconds'] = 0;

CE Cache Breaking Rules

Mobile Performance Resources

Shae Kuronen

Senior Web Developer @ 206inc