|
| 1 | +I love starting new projects! |
| 2 | + |
| 3 | +It's so much fun to architect an app from scratch. |
| 4 | + |
| 5 | +My latest project is another app starter, this time focusing on server-side rendered React... |
| 6 | + |
| 7 | +...with the regular hooks into Firebase... and a special focus on performance. |
| 8 | + |
| 9 | +It's basically an app shell that scores well on Lighthouse and incorporates everything that I need. |
| 10 | + |
| 11 | +Once this app shell is running smoothly I'll use it to start my next production app... |
| 12 | + |
| 13 | +...so it's super important that I get this done right... |
| 14 | + |
| 15 | +...because my business will succeed or fail based on how well this app appeals to users. |
| 16 | + |
| 17 | +PAUSE FOR INTERSTITIAL |
| 18 | + |
| 19 | +PAUSE FOR INTERSTITIAL |
| 20 | + |
| 21 | +PAUSE FOR INTERSTITIAL |
| 22 | + |
| 23 | +PAUSE FOR INTERSTITIAL |
| 24 | + |
| 25 | +I'm calling this initial project Firebase SSR Starter, where SSR stands for server-side rendered. |
| 26 | + |
| 27 | +And of course I have benchmarks for this project, which is why this is interesting. |
| 28 | + |
| 29 | +First, the app needs to be SEO-friendly and server-side rendered. |
| 30 | + |
| 31 | +Second, I need an architecture that will last for another three-to-five years. |
| 32 | + |
| 33 | +And finally, I need to max out my Lighthouse performance score. |
| 34 | + |
| 35 | +I've been doing a bunch of research and landed on Next.js with React. |
| 36 | + |
| 37 | +Next.js has an excellent server-side rendering system. |
| 38 | + |
| 39 | +Next is a framework, which I kinda hate... but it's flexible enough... |
| 40 | + |
| 41 | +...and it solves so many problems that I'd have to solve myself... |
| 42 | + |
| 43 | +...and I know that I couldn't roll a stronger framework than Next. |
| 44 | + |
| 45 | +Now for my maintainability requirement... |
| 46 | + |
| 47 | +Three-to-five years is a steep requirement in the JavaScript ecosystem... |
| 48 | + |
| 49 | +...so I'm not using any of the fancy, new stuff that I kinda want to use. |
| 50 | + |
| 51 | +I need long-term maintainability, so I'm sticking with React. |
| 52 | + |
| 53 | +I'd love to try lit-html or use Preact... but I can't justify it. |
| 54 | + |
| 55 | +React has the ecosystem momentum these days, and I'm going to need a lot of continuity. |
| 56 | + |
| 57 | +I need the lowest maintenance costs possible in years two through five of this app's lifespan. |
| 58 | + |
| 59 | +My last big, long-term app was written in AngularJS, and we all know how AngularJS went... |
| 60 | + |
| 61 | +...year one was great, two was a little sad, and years three and four have been a failure. |
| 62 | + |
| 63 | +The last time I had to build my old AngularJS app from scratch I spent 12 hours resurrecting it. |
| 64 | + |
| 65 | +I'm not doing that again, so I'm sticking with tried-and-true React. |
| 66 | + |
| 67 | +So now it's time for performance! |
| 68 | + |
| 69 | +Lighthouse is a Chrome extension that will be an integrated part of DevTools in Chrome 69. |
| 70 | + |
| 71 | +Lighthouse runs hundreds of tests on your app and tells you that your performance is horrible. |
| 72 | + |
| 73 | +So then you spend the next few days improving your app's performance with Lighthouse's checklist... |
| 74 | + |
| 75 | +...and you either get to a perfect score of 100 or, like me, give up in the high 90s. |
| 76 | + |
| 77 | +Lighthouse includes a bunch of nice-to-have optimizations. |
| 78 | + |
| 79 | +A truly generic app architecture should be able to score a 100 on Lighthouse pretty easily... |
| 80 | + |
| 81 | +...however, once you start to make app-specific decisions, scoring 100 can be impossible. |
| 82 | + |
| 83 | +For instance, I'm using a React Material Design framework which doesn't support passive scrolling. |
| 84 | + |
| 85 | +And of course Lighthouse requires passive scrolling for a perfect one hundred... |
| 86 | + |
| 87 | +...and I'm not about to hack into the internals of this Material Design framework... |
| 88 | + |
| 89 | +...especially not for very minor passive-scroll improvements. |
| 90 | + |
| 91 | +So I'm not scoring a perfect one hundred... but I got pretty close. |
| 92 | + |
| 93 | +The hardest part of this entire Lighthouse performance audit was getting Firebase to load. |
| 94 | + |
| 95 | +Each part of Firebase is available as its own SDK... |
| 96 | + |
| 97 | +...so once the main `firebase dash app` library is imported, you can import each piece separately. |
| 98 | + |
| 99 | +But even in separate parts, Firebase is a heavy library. |
| 100 | + |
| 101 | +All of those juicy Firebase features don't come for free. |
| 102 | + |
| 103 | +I optimized my Firebase imports by not bundling Firebase with the rest of my app. |
| 104 | + |
| 105 | +I added script tags for each part of the Firebase SDK into the app shell. |
| 106 | + |
| 107 | +But I made sure to put those script tags near the end of my body tag... |
| 108 | + |
| 109 | +...because they have to be loaded in a synchronous, blocking manner. |
| 110 | + |
| 111 | +And I don't want one hundred kilobytes of Firebase SDK to block my app shell's initial paint time. |
| 112 | + |
| 113 | +Putting the Firebase SDK scripts at the bottom of my app shell... |
| 114 | + |
| 115 | +...allows the rest of the shell to render before the SDK scripts block the page. |
| 116 | + |
| 117 | +The tricky part of this process is that my app requires Firebase to boot... |
| 118 | + |
| 119 | +...so my app code needs to load just after the Firebase scripts are done loading. |
| 120 | + |
| 121 | +This method has some benefits and one, big cost. |
| 122 | + |
| 123 | +The cost is that my app will not be interactive until Firebase is fully loaded. |
| 124 | + |
| 125 | +This hurts my time-to-interactive performance on the initial page load. |
| 126 | + |
| 127 | +I really didn't want to sacrifice my initial time-to-interactive... but it had to happen. |
| 128 | + |
| 129 | +See, the benefits outweigh this cost, at least in my case... |
| 130 | + |
| 131 | +...and that's because I don't want to suffer through the misery of making my app asynchronous. |
| 132 | + |
| 133 | +It's possible to only block the parts of the app that rely on Firebase... |
| 134 | + |
| 135 | +...but that requires handling a bunch of asynchronous rending within the app itself. |
| 136 | + |
| 137 | +And since Firebase is so core to app's architecture... |
| 138 | + |
| 139 | +...there wouldn't be much of the app that could load before the Firebase SDK. |
| 140 | + |
| 141 | +So the benefits of going all asynchronous with the Firebase loading aren't huge... |
| 142 | + |
| 143 | +...and building the entire app around asynchronous Firebase loading would be quite challenging. |
| 144 | + |
| 145 | +So I'm using Firebase's CDN links for the SDK... |
| 146 | + |
| 147 | +...which means that the client's browser may already have the CDN files cached. |
| 148 | + |
| 149 | +And I've got full Service Worker caching in place... |
| 150 | + |
| 151 | +...only the very first page load is ever effected. |
| 152 | + |
| 153 | +Once the client browser has the Firebase SDK files, the app will load almost instantly. |
| 154 | + |
| 155 | +So it's not perfect, but shipping code is all about making smart compromises... |
| 156 | + |
| 157 | +...which means accepting that you can't have all of the nice things at once. |
| 158 | + |
| 159 | +You can find my Firebase SSR Starter code on GitHub. |
| 160 | + |
| 161 | +You're welcome to copy it, but I'd recommend using it as a reference instead. |
| 162 | + |
| 163 | +I used to use other people's app starters for every project. |
| 164 | + |
| 165 | +It's a great feeling to jump right into building my app-specific features... |
| 166 | + |
| 167 | +...but building an app starter involves so many small decisions... |
| 168 | + |
| 169 | +...and you need to make those decisions yourself to truly understand how the app works. |
| 170 | + |
| 171 | +So I'll probably reference this starter app a lot going forward... |
| 172 | + |
| 173 | +...but I'll most likely only use it for my next app... |
| 174 | + |
| 175 | +...because I'll have learned so much by the end of that app, that I'll need to start fresh. |
| 176 | + |
| 177 | +So copy/paste and learn from the past, but don't value it too highly... |
| 178 | + |
| 179 | +...because you always know more now than when you started your last app... |
| 180 | + |
| 181 | +...and you need to be deeply familiar with your entire app if you hope to ship anything valuable. |
0 commit comments