M A N N I N G Jeremy L. Wagner FOREWORD BY Ethan Marcotte BUILDING FAST WEB PAGES IN ACTION
Web Performance in Action BUILDING FAST WEB PAGES JEREMY L. WAGNER M A N N I N G SHELTER ISLAND
For online information and ordering of this and other Manning books, please visit www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact Special Sales Department Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 Email: orders@manning.com ©2017 by Manning Publications Co. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps. Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15 percent recycled and processed without the use of elemental chlorine. Manning Publications Co. Development editor: Susanna Kline 20 Baldwin Road Review editor: Ivan Martinović PO Box 761 Technical development editor: Nick Watts Shelter Island, NY 11964 Project editor: Kevin Sullivan Copyeditor: Sharon Wilkey Proofreader: Elizabeth Martin Technical proofreader: David Fombella Pombal Typesetter: Gordan Salinovic Cover designer: Marija Tudor ISBN 9781617293771 Printed in the United States of America 1 2 3 4 5 6 7 8 9 10 – EBM – 21 20 19 18 17 16
iii brief contents 1 ■ Understanding web performance 1 2 ■ Using assessment tools 22 3 ■ Optimizing CSS 51 4 ■ Understanding critical CSS 83 5 ■ Making images responsive 102 6 ■ Going further with images 130 7 ■ Faster fonts 164 8 ■ Keeping JavaScript lean and fast 196 9 ■ Boosting performance with service workers 223 10 ■ Fine-tuning asset delivery 242 11 ■ Looking to the future with HTTP/2 274 12 ■ Automating optimization with gulp 303
(This page has no text content)
v contents foreword xi preface xii acknowledgments xiii about this book xv about the author xix about the cover illustration xx 1 Understanding web performance 1 1.1 Understanding web performance 2 Web performance and the user experience 2 ■ How web browsers talk to web servers 3 ■ How web pages load 5 1.2 Getting up and running 6 Installing Node.js and Git 6 ■ Downloading and running the client’s website 7 ■ Simulating a network connection 8 1.3 Auditing the client’s website 9 1.4 Optimizing the client’s website 11 Minifying assets 12 ■ Using server compression 15 Optimizing images 18 1.5 Performing the final weigh-in 20 1.6 Summary 21
CONTENTSvi 2 Using assessment tools 22 2.1 Evaluating with Google PageSpeed Insights 22 Appraising website performance 23 ■ Using Google Analytics for bulk reporting 25 2.2 Using browser-based assessment tools 26 2.3 Inspecting network requests 27 Viewing timing information 27 ■ Viewing HTTP request and response headers 29 2.4 Rendering performance-auditing tools 32 Understanding how browsers render web pages 32 ■ Using Google Chrome’s Timeline tool 32 ■ Identifying problem events: thy enemy is jank 35 ■ Marking points in the timeline with JavaScript 41 Rendering profilers in other browsers 42 2.5 Benchmarking JavaScript in Chrome 43 2.6 Simulating and monitoring devices 45 Simulating devices in the desktop web browser 45 ■ Debugging websites remotely on Android devices 46 ■ Debugging websites remotely on iOS devices 47 2.7 Creating custom network throttling profiles 48 2.8 Summary 50 3 Optimizing CSS 51 3.1 Don’t talk much and stay DRY 52 Write shorthand CSS 52 ■ Use shallow CSS selectors 55 ■ Culling shallow selectors 56 ■ LESS is more and taming SASS 57 ■ Don’t repeat yourself 58 ■ Going DRY 58 ■ Finding redundancies with csscss 59 ■ Segment CSS 61 ■ Customize framework downloads 63 3.2 Mobile-first is user-first 63 Mobile-first vs. desktop-first 64 ■ Mobilegeddon 67 ■ Using Google’s mobile-friendly guidelines 67 ■ Verifying a site’s mobile-friendliness 68 3.3 Performance-tuning your CSS 69 Avoiding the @import declaration 69 ■ @import serializes requests 69 ■ <link> parallelizes requests 70 ■ Placing CSS in the <head> 71 ■ Preventing the Flash of Unstyled Content 71 Increasing rendering speed 72 ■ Using faster selectors 72 Constructing and running the benchmark 73 ■ Examining the benchmark results 74 ■ Using flexbox where possible 75 Comparing box model and flexbox styles 75 ■ Examining the benchmark results 77
CONTENTS vii 3.4 Working with CSS transitions 77 Using CSS transitions 77 ■ Observing CSS transition performance 79 Optimizing transitions with the will-change property 80 3.5 Summary 82 4 Understanding critical CSS 83 4.1 What does critical CSS solve? 83 Understanding the fold 84 ■ Understanding render blocking 85 4.2 How does critical CSS work? 86 Loading above-the-fold styles 86 ■ Loading below-the-fold styles 87 4.3 Implementing critical CSS 88 Getting the recipe website up and running 88 ■ Identifying and separating above-the-fold CSS 90 ■ Loading below-the-fold CSS 96 4.4 Weighing the benefits 97 4.5 Making maintainability easier 98 4.6 Considerations for multipage websites 100 4.7 Summary 101 5 Making images responsive 102 5.1 Why think about image delivery? 103 5.2 Understanding image types and their applications 105 Working with raster images 105 ■ Working with SVG images 108 ■ Knowing what image formats to use 109 5.3 Image delivery in CSS 110 Targeting displays in CSS by using media queries 111 ■ Targeting high DPI displays with media queries 113 ■ Using SVG background images in CSS 116 5.4 Image delivery in HTML 117 The universal max-width rule for images 117 ■ Using srcset 118 Using the <picture> element 121 ■ Polyfilling support with Picturefill 125 ■ Using SVG in HTML 127 5.5 Summary 128 6 Going further with images 130 6.1 Using image sprites 131 Getting up and running 132 ■ Generating the image sprite 132 Using the generated sprite 134 ■ Considerations for image sprites 135 ■ Falling back to raster image sprites with Grumpicon 136
CONTENTSviii 6.2 Reducing images 137 Reducing raster images with imagemin 138 ■ Optimizing SVG images 143 6.3 Encoding images with WebP 145 Encoding lossy WebP images with imagemin 146 ■ Encoding lossless WebP Images with imagemin 147 ■ Supporting browsers that don’t support WebP 149 6.4 Lazy loading images 150 Configuring the markup 151 ■ Writing the lazy loader 153 Accommodating users without JavaScript 160 6.5 Summary 162 7 Faster fonts 164 7.1 Using fonts wisely 165 Selecting fonts and font variants 165 ■ Rolling your own @font- face cascade 167 7.2 Compressing EOT and TTF font formats 172 7.3 Subsetting fonts 173 Manually subsetting fonts 174 ■ Delivering font subsets by using the unicode-range property 178 7.4 Optimizing the loading of fonts 184 Understanding font-loading problems 185 ■ Using the CSS font- display property 186 ■ Using the font-loading API 188 Using Font Face Observer as a fallback 192 7.5 Summary 194 8 Keeping JavaScript lean and fast 196 8.1 Affecting script-loading behavior 197 Placing the <script> element properly 197 ■ Working with asynchronous script loading 199 ■ Using async 199 Using async reliably with multiple scripts 201 8.2 Using leaner jQuery-compatible alternatives 203 Comparing the alternatives 203 ■ Exploring the contenders 203 Comparing file size 204 ■ Comparing performance 204 Implementing an alternative 205 ■ Using Zepto 205 Understanding caveats on using Shoestring or Sprint 206
CONTENTS ix 8.3 Getting by without jQuery 207 Checking for the DOM to be ready 207 ■ Selecting elements and binding events 208 ■ Using classList to manipulate classes on elements 210 ■ Reading and modifying element attributes and content 211 ■ Making AJAX requests with the Fetch API 214 Using the Fetch API 214 ■ Polyfilling the Fetch API 215 8.4 Animating with requestAnimationFrame 217 requestAnimationFrame at a glance 217 ■ Timer function-driven animations and requestAnimationFrame 217 ■ Comparing performance 218 ■ Implementing requestAnimationFrame 219 Dropping in Velocity.js 221 8.5 Summary 222 9 Boosting performance with service workers 223 9.1 What are service workers? 224 9.2 Writing your first service worker 226 Installing the service worker 226 ■ Registering the service worker 227 ■ Intercepting and caching network requests 230 Measuring the performance benefits 233 ■ Tweaking network request interception behavior 233 9.3 Updating your service worker 236 Versioning your files 237 ■ Cleaning up old caches 238 9.4 Summary 240 10 Fine-tuning asset delivery 242 10.1 Compressing assets 243 Following compression guidelines 244 ■ Using Brotli compression 247 10.2 Caching assets 251 Understanding caching 252 ■ Crafting an optimal caching strategy 256 ■ Invalidating cached assets 259 10.3 Using CDN assets 261 Using CDN-hosted assets 261 ■ What to do if a CDN fails 264 Verifying CDN assets with Subresource Integrity 265 10.4 Using resource hints 267 Using the preconnect resource hint 267 ■ Using the prefetch and preload resource hints 269 ■ Using the prerender resource hint 271 10.5 Summary 272
CONTENTSx 11 Looking to the future with HTTP/2 274 11.1 Understanding why we need HTTP/2 275 Understanding the problem with HTTP/1 275 ■ Solving common HTTP/1 problems via HTTP/2 278 ■ Writing a simple HTTP/2 server in Node 281 ■ Observing the benefits 284 11.2 Exploring how optimization techniques change for HTTP/2 286 Asset granularity and caching effectiveness 286 ■ Identifying performance antipatterns for HTTP/2 287 11.3 Sending assets preemptively with Server Push 289 Understanding Server Push and how it works 289 ■ Using Server Push 290 ■ Measuring Server Push performance 293 11.4 Optimizing for both HTTP/1 and HTTP/2 294 How HTTP/2 servers deal with HTTP/2-incapable browsers 295 Segmenting your users 295 ■ Serving assets according to browser capability 297 11.5 Summary 302 12 Automating optimization with gulp 303 12.1 Introducing gulp 304 Why should I use a build system? 304 ■ How gulp works 305 12.2 Laying down the foundations 307 Structuring your project’s folders 307 ■ Installing gulp and its plugins 308 12.3 Writing gulp tasks 311 The anatomy of a gulp task 312 ■ Writing the core tasks 313 Writing the utility tasks 320 12.4 Going a little further with gulp plugins 323 12.5 Summary 325 appendix A Tools reference 327 appendix B Native equivalents of common jQuery functionality 332 index 345
xi foreword “May you live in interesting times,” goes the old half-curse. And I don’t know about you, but it feels like the web is perpetually stuck in interesting times. We’re designing for an ever-expanding number of mobile devices, each one more powerful than most laptops I’ve owned throughout my career. But we’re also designing for a web that trav- els over the aging infrastructure of developed economies, as well as to cheaper, low- powered mobile devices in younger, emerging markets. In other words, the web is more broadly accessed today than ever before—but over a network that’s far more fragile than we might like to think. Once a user requests one of our web pages, any number of things can fail. Maybe a connection drops, or a net- work’s latency is too high for an asset to load. Or maybe the users exceeded their data allotment for the month. We’re building digital experiences—some responsive, some not—that are more beautiful than anything produced at any other point in the web’s history. But we need to start designing for performance as well. We need to create sites and services opti- mized for the fragility of the network, as well as the widths of our users’ screens. Thankfully, you’ve begun reading Web Performance in Action, a book that can help you do just that. Jeremy Wagner has written an invaluable, accessible reference for the modern web developer, one that demystifies even the most arcane-sounding acronyms and frames even the most arcane-seeming web optimization tricks in approachable, plain language. In interesting times like these, Jeremy’s guide is indispensable: As you travel through these pages, you’ll gain the skills to ensure your sites are as beautiful as they are fast, nimble, and bandwidth-friendly. ETHAN MARCOTTE DESIGNER, ETHANMARCOTTE.COM AUTHOR OF RESPONSIVE WEB DESIGN
xii preface Well before I ever entertained the notion of writing a book, the idea that websites ought to be fast was a high priority in all my projects. In my humble opinion, slow web- sites are not a mere inconvenience. They are a critical sort of user experience prob- lem. Until a website loads, no user experience exists. The longer it takes for a site to load, the more this absence is felt by the user. When I proposed this book to Manning in 2015, I was hardly the first to write on the topic of web performance. Many authors before me had written in this space, and I knew that I would be standing on the shoulders of giants. My goal with Web Perfor- mance in Action was to provide a modern guide for today’s web developers that would give them the knowledge they need to make their websites faster than ever. I think this book meets that goal. When web performance is discussed, it’s often tied to financial concepts. The idea that a poorly performing website can affect sales or ad revenues is hardly new. What we don’t hear enough about, however, is how such a website can be potentially costly for the user on a restricted data plan. Or how slow websites are an impassible sort of barrier for people mired in an antiquated internet infrastructure. So much of the world has such a difficult time accessing the web. While infrastructure is slowly improving, we as developers can move the needle for users by developing sites with performance in mind. I wrote Web Performance in Action to help you meet your goals, and the folks at Man- ning participated in refining it. In an age where the web is becoming increasingly complex, the time has never been more appropriate to tackle this problem. I think this book will help you get to where you want to be.
xiii acknowledgments It takes a ton of people, beyond the author, to put a book together. The people at Manning played a huge role in getting this book from a mere proposal to what you’re reading right now. Brace yourself, because this section is brimming with gratitude. I’ll start by thanking the first person from Manning I talked to, an acquisitions edi- tor named Frank Pohlmann. The proposal phase of this book took quite some time, and Frank coached me on what to do and what not to do, and most importantly, let me know exactly what I was getting into. Thank you, Frank, for guiding me in the early part of this process. As this is my first book, I’d like to extend my gratitude to Manning’s publisher Marjan Bace, who saw fit to grant me this opportunity. Green-lighting any book proposal is a risk, and that’s especially true when the proposal comes from someone who hasn’t made a name for himself prior to that point, so it took some courage to take that risk. Thank you, Marjan. Behind every author is an editor pushing them to write the best manuscript they can. Susanna Kline was the development editor for this book, and here I offer my sin- cerest thanks and gratitude for her hard work and indispensable guidance on this project. Susanna not only played the role of an editor, she was also a great coach who understood the vulnerability I felt in this vast and new undertaking, especially when there was so much uncertainty in the early stages of development. Her guidance in this project was essential to its success. Thank you, Susanna, for all your help. Every technical book, of course, needs a technical editor. Nick Watts did a superb job in this role. His informed perspective, valuable input, and willingness to challenge
ACKNOWLEDGMENTSxiv my assertions and points of view certainly contributed positively to the quality of the final text. Thank you, Nick. This book was also reviewed by many people at various stages in its development, including Alexey Galiullin, Amit Lamba, Birnou Sebarte, Daniel Vasquez, John Huff- man, Justin Calleja, Kevin Liao, Matt Harting, Michael Martinsson, Michael Sperber, Narayanan Jayaratchagan, Noreen Dertinger, Omer Faruk Celebi, Simone Cafiero, and William Ross. Their feedback gave valuable insight into what public perception of the book could be. I would like to thank them for their input and suggestions, which made this book better than what I could have achieved on my own. The final polish of a book is also very important. I’d like to thank David Fombella Pombal for his thorough and excellent technical proofing of the manuscript, which identified issues I would have otherwise missed. Sharon Wilkey meticulously combed through and copyedited the final manuscript, which further refined it, for which I’m grateful. Elizabeth Martin filed off the rough edges and reined in some of my excesses with keen precision. On top of all this, Kevin Sullivan did a great job of coordinating the preproduction and production phases. Thank you so much, guys. You did great work in that last, critical mile of the project. I also wish to extend my gratitude to Ethan Marcotte, a person whose work has irrevocably changed how we all develop for the web. When I contacted Ethan to see if he would be interested in writing the foreword to Web Performance in Action, I was pleas- antly surprised that he had time to reply, let alone read the manuscript. A foreword is not a matter to be taken lightly. It’s an endorsement of a book’s quality. To know that Ethan endorses the material in this book is one of the proudest moments of my pro- fessional career. Thank you, Ethan. I’d like to thank my father Luke and my mother Georgia for supporting me in all that I’ve done and attempted to do, even the harebrained stuff. I’d also like to thank my brother Lucas who has always been an incredible example to me, and led the way in showing what’s possible if you’re willing to work hard for something. Thank you so much. Lastly, I owe gratitude and thanks to my wife Alexandria. Her unwavering support and selflessness has been a source of strength for me throughout this endeavor. Her gentle encouragement and belief in my potential has helped more than she knows. To anyone else I may have overlooked, know that you were a part of making this book what it is. For that, I thank you.
xv about this book The purpose of Web Performance in Action is to teach you how to create faster websites, and through the course of this book, I’ll help you get here. The techniques you’ll learn as you read should also come in handy for improving performance on existing websites. Who should read this book This book focuses heavily (though not exclusively) on improving website perfor- mance on the client side. This means that it’s targeted toward front-end developers who have a good command of HTML, CSS, and JavaScript. You, the reader, should be comfortable working with these technologies. This book occasionally strays into the server side where appropriate. For instance, some server-side code examples are in PHP. These examples are intended to be illus- trative of a concept, and are often peripheral to the task at hand. Chapter 10 covers server compression, including the new Brotli compression algorithm, which fits into the server-side category. Chapter 11 explains HTTP/2, so having an interest in how this new protocol can affect how you optimize your site can be helpful. You should also be somewhat comfortable on the command line, but even if you’re not, you’ll still be able to follow along in the examples provided. Now, let’s talk about how this book is structured.
ABOUT THIS BOOKxvi Roadmap Unlike most other Manning titles, this book is not divided into parts, but it does follow a logical flow of sorts. Chapter 1 is an introduction to the fundamentals of web perfor- mance—bedrock stuff, such as minification, server compression, and so forth. If you’re already a performance-minded developer, this chapter will feel familiar to you. It’s intended for the front-end developer who’s new to the concept of web perfor- mance. Chapter 2 covers performance assessment tools, both online and in the browser, with a focus on using Chrome’s developer tools. From there, we’ll venture into the realm of optimizing CSS. Chapter 3 is a grab bag of topics and examples of how you can make your CSS leaner, and use native CSS features that can help increase the responsiveness of your website to user input. Chapter 4 is about critical CSS, a technique that can give your site’s rendering performance a real shot in the arm. Then, we’ll tackle image optimization. Chapter 5 focuses on different image types and how to use them, as well as how to deliver them optimally to different devices both in CSS and inline in HTML. Chapter 6 covers how to reduce the file size of images, automating the creation of image sprites, Google’s WebP image format, and how to lazy load images by writing a custom lazy loading script. After all of that, we’ll turn our focus away from images toward fonts. Chapter 7 covers optimizing fonts. This ranges from creating an optimal @font-face cascade to font sub- setting, using the unicode-range CSS property, compressing legacy font formats on the server, and how to control the loading and display of fonts with CSS and JavaScript. Chapters 8 and 9 focus on JavaScript. Chapter 8 speaks more to the need for mini- malism in JavaScript by advocating the use of in-browser features, rather than relying on jQuery and other libraries. For those who can’t abandon jQuery, I talk about jQuery-compatible alternatives that offer a subset of what jQuery does, but with less overhead. This chapter also talks about proper placement of the <script> tag, as well as how to use the async attribute, and animating with the requestAnimationFrame method. Chapter 9 ventures into the territory of JavaScript service workers. In this chapter, you’ll learn how you can serve content to users who are offline, as well as how you can improve the performance of pages for online users with this technology. Chapter 10 is yet another grab bag of topics. It covers the impact of poorly config- ured server compression, the new Brotli compression algorithm, resource hints, con- figuring caching policies, and the benefits of using CDN-hosted resources. Chapter 11 covers HTTP/2, the performance problems that it solves, how optimiza- tion practices differ between it and HTTP/1, Server Push, and a proof of concept of how you can adapt the delivery of your website’s content to accommodate both ver- sions of the protocol. Chapter 12 takes a good chunk of what you’ve learned and automates it with the gulp task runner. In this chapter, you’ll learn how to automate various aspects of opti- mizing your website’s performance, which will help you optimize your sites as you code them, saving you valuable time.
ABOUT THIS BOOK xvii There are two appendixes. Appendix A is a tools reference. Appendix B highlights common jQuery functions and shows you how to accomplish the same tasks by native means. Tools used in this book While following examples in this book, you’ll have open your favorite text editor and a command-line window. Beyond that, two tools are used consistently throughout, so you’ll want to have them installed. Node.js Node.js, sometimes referred to as Node, is a JavaScript runtime that allows you to use JavaScript outside of the browser. It can be used for all kinds of crazy stuff that, some years ago, no one would have thought that JavaScript would be used for. I’m talking about task runners, image processors, and even web servers. All these things are installed using the Node Package Manager (npm). In your optimization efforts throughout the book, you’ll use Node for all of that. You’ll often use it to run local web servers using the Express framework for examples that we’ll work through together. In chapter 11, you’ll even use it to run a local HTTP/2 server. You’ll use Node in chapter 6 to optimize images in bulk, and in chapter 12, you’ll use it to automate common optimization tasks with gulp. It’s used in nearly every chap- ter for one kind of function or another. If you’re serious about working your way through this book, you’ll need to have Node installed. If you don’t have it set up, go to https://nodejs.org and head to the downloads section. If you’re feeling trepidation because you don’t know Node, don’t worry! Everything is explained, and if you follow the directions, you should be fine. But, if you feel that you’d benefit from a deep dive into how Node works later on, check out Node.js in Action, another title from Manning (https://www.manning.com/ books/node-js-in-action). Just know that a deep knowledge of Node is not necessary to navigate through this text. Git Git is a version control system used for keeping track of changes in software applications. There’s a good chance you’ve used it, but if not, you’ll get to use it in this book. Git is used to download code for examples from this book’s collection of GitHub repositories hosted at https://github.com/webopt. You can download Git at https://git-scm.com. Why use Git instead of downloading zip files of code examples? For one, using a version control system like Git on the command line makes it easier to grab things and go. The biggest advantage, though, is that you’ll be able to easily skip ahead to fin- ished code examples if you get stuck or just want to see the final result. If you’ve never used Git, don’t sweat it. All the instructions for using it are delin- eated clearly, and you’ll be able to follow along. If you prefer not to use Git, you can go to https://github.com/webopt and download zip files from each repository.
ABOUT THIS BOOKxviii Other tools Most of the tools you’ll use in this book will be installed by Node Package Manager, and are thus dependent on Node. There are two instances, however, where you’ll get an opportunity to use tools beyond Node. In chapter 3, there’s an example where you’ll apply the DRY (don’t repeat your- self) principle to CSS, which entails combining redundant rules under multiple selec- tors. A Ruby-based tool named csscss is used in this example to detect redundancies. If you have a Mac or you’re running any other UNIX-like operating system, this may already be available for you. If you’re running Windows, you’ll have to download Ruby at http://rubyinstaller.org. In chapter 7, there’s an example where you’ll subset fonts to make them smaller. You’ll use a Python-based tool called pyftsubset. Like Ruby, there’s a good chance that on UNIX-like systems, Python will already be available. If you use Windows, you’ll want to head over to www.python.org and grab the installer. Code conventions Code in this book is written in a fashion that most developers will be comfortable with. All source code in the book is in a fixed-width font like this, which sets it off from the surrounding text. In code snippets throughout the book, relevant portions are annotated for clarity. Changed portions of an existing snippet are typically set in bold font like this. Regarding the code you download from GitHub, indentations are done with tabs. When it comes to how many spaces you want a tab character to repre- sent, that’s up to you. When I wrote the examples, I went with four spaces. The code snippets in the book follow that convention. Source code for all working examples is available on the publisher’s website (www.manning.com/books/web-performance-in-action) as well as GitHub (https:// github.com/webopt). Author Online Purchase of Web Performance in Action includes free access to a private web forum run by Manning Publications where you can make comments about the book, ask techni- cal questions, and receive help from the author and from other users. To access the forum and subscribe to it, point your web browser to www.manning.com/books/ web-performance-in-action. This page provides information on how to get on the forum once you’re registered, what kind of help is available, and the rules of conduct on the forum. Manning’s commitment to our readers is to provide a venue where a meaningful dialog between individual readers and between readers and the author can take place. It’s not a commitment to any specific amount of participation on the part of the author, whose contribution to the forum remains voluntary (and unpaid). We suggest you try asking the author challenging questions lest his interest stray! The Author Online forum and the archives of previous discussions will be accessi- ble from the publisher’s website as long as the book is in print.
xix about the author Jeremy Wagner is a professional front-end web developer with over ten years of experience in various agencies and large compa- nies. In addition to his writings on web performance, he also speaks at conferences on a variety of web development–related topics. He can be found on the web at https://jeremywagner.me or @malchata on Twitter.
Comments 0
Loading comments...
Reply to Comment
Edit Comment