
Ask HN: What are the common pitfalls/problems of Single Page App's? - rajangdavis
I am working on a Single Page Application (SPA) using Angular 1.5 and while I have spent a lot of time building it, I keep reading about various &quot;issues&quot; regarding javascript frameworks in general. To be specific, I am building the server side API as well as the front end, so I have a lot of freedom to adjust both sides as much as needed (which can be good and bad).<p>I feel like a SPA style app works well with the use case of the site I have been building in that we are making it easier for our customers to find their information in the shortest amount of time possible (think Google for product support).<p>With that said, I have a bias towards this design choice because I built it. I know that I might be missing something and I want to consider more use cases than what I can come up with.<p>What I want to know is when does it make sense to use a more traditional server-client architecture? I had read about Twitter reverting back from a SPA to server rendering, but I feel like their use case was specific to their needs or to a specific metric and possibly user experience for their site (lower time to first tweet, smaller page load, and cleaner URL&#x27;s).<p>I am aware of SEO drop with Angular, but I feel like problems like clean URL&#x27;s, back button not working, and UI thrashing are fairly solveable by good planning (in the case of the URL&#x27;s) and Web Workers (to address UI Thrashing).<p>I chose Angular because I was already very familiar with it and I found it to be fairly flexible for what I need it to do, it just needs some nudging at times (ng-if vs. ng-show&#x2F;hide). Also, at the time, I couldn&#x27;t get the JSX tooling for React to work properly and I think Babel couldn&#x27;t do transpiling for the latest version of React at that time (I think it was around 1.4). I had used Mithril which was a huge joy and very powerful, but the way my site works (it&#x27;s an Oracle product using PHP&#x2F;CodeIgnitor), I couldn&#x27;t get the client side routing to work properly.
======
cweagans
Speaking as a backend dev who generally avoids writing client-side code, the
biggest problem that I've had is figuring out the "right" way to build the
fucking thing. The JS fatigue is real.

~~~
rajangdavis
What kind of stack are you working with? Honestly, this isn't the first time I
tried building this on the site that we have, but this has been the farthest I
have gotten without UI jank or slow initial loading. I would have much
preferred to use Mithril, but I couldn't get the routing how I liked it; this
is more of how the server is set up which I have limited access to.

~~~
cweagans
Been looking at Ember, Angular 2, and Backbone + React. It's all just such a
CF though. I was reasonably okay at Angular 1.x apps, but the learning curve
is just so steep with these new frameworks that I don't even know where to
begin. I've been making okay progress with Backbone and straight jQuery, but
jQuery is slow and huge, so I'm slowly coming around to the idea of replacing
it with something else.

~~~
rajangdavis
I really liked Mithril, I would recommend checking it out. It definitely is
kind of weird to write out your HTML with javascript, but it gets easier as
you go along. The performance is ridiculous as well and it was a lot easier to
troubleshoot as well. Also, the code kind of aligns with a JSON structure, so
if you have a lot of control with the server response, it's really simple to
grok how to structure your front end code.

------
lollipop25
\- Angular doesn't enforce a convention. This gives you flexibility at the
cost of zero convention. Developers tend to abuse this flexibility "because
they can do it this way instead of that". Choose a convention among all the
methods possible, then stick with it.

\- Angular can be hard to debug when using some architectures. Two-way binding
is pretty hard to track down. Scope inheritance is also a pain, especially
with nested controllers, or when devs just stick everything to the root scope
and expect it to be there when it doesn't at times. Even tools like Batarang
and Inspector can be a pain to use.

\- Long-running SPA's tend to accumulate unreleased memory if not managed
properly. When stuff like this happens, worst case scenario is to have the
user reload the page and lose state. Not taking this seriously, the browser
may crash. You don't want this to happen in the middle of an operation, like
say, a payment.

\- State management is also a pain to work with. I often hear our devs talk
about "invalid state". How can it be invalid? That's unless not all changes
are accounted for. This is where the Flux architecture shines, as it isolates
state in one location, away from the moving parts, and state only mutates via
one mechanism - actions.

\- It's huge size. One issue with Angular is that it uses too much boilerplate
code, most of which is because of the structure and dependency injection
mechanisms. Minifiers can only do so much to lessen the size, and not sure if
Rollup can do much against these helper structures.

~~~
rajangdavis
I am going to respond to each point

\- Nailed it on this comment. I have been trying to figure out something that
"makes sense" in terms of file structure and even code organization. So far,
it hasn't been too much of a hindrance, but it I know the more code I write,
the more unwieldy things will probably get.

\- This is something that I have been very aware of and the way I have been
handling it is limiting the number of controllers in general so I do not
introduce new scopes. For the most part, all of the pages do not need to
introduce new scopes and the data should be consistent across the pages
anyways, so this hasn't been too much of an issue (yet).

-I haven't had this issue yet, but I haven't sufficiently tested this. From some initial testing, the memory footprint has been pretty light, but I need to do a lot more on this end once I have completed more of the app.

-State management hasn't been too tricky because there's only two states that I really care about: whether a user has logged in or if they are logged off. This part I have been addressing by loading JSON for contact information into the page to both limit the amount of AJAX requests and to enforce routing. Fortunately, this state is also managed by HTTPS Cookies so this is handled server side as well.

-I don't know how to validate or dispute this, but it looks like loading the minified scripts from CDNJS for both Angular and React were the same filesize. In any case, I have been using A LOT of what comes with Angular out of the box and have been using the UI Router library as well as the ng-Sanitize library for what I need. Haven't had too many issues with this.

------
k__
In my experience SPA load rather fast. Only the first load and some crazy
animations are really an issue.

And the first load issue can be circumvented with pre-rendering the first page
on the server.

I tried this with React and it worked like a charm. It even went so far, that
the whole page doubled for SPA and complete-server-side app, when JS was
deactivated.

~~~
rajangdavis
That sounds really cool! How simple/difficult was it to set up the
prerendering and what kind of stack are you running?

~~~
k__
The only ugly part was getting the client side React to pick up the server
side markup when it comes "pre-filled" with dynamic data.

The client needs to use the same data or else it will throw away the whole
markup and re-render it.

Fetch the data on the server before you render.

Render the (static) markup with React and the data.

Send the markup AND the (serialized) data to the client.

The client will render the markup normally before React kicks in.

The client React then needs a way to use the serialized data with the pre-
rendered markup.

Redux, for example, is a way to pass down this data to every component, so
it's available at render.

Normally, when writing React apps, you can simply load data async and show a
place-holder while the request runs.

If you want to render on server, all the data has to be fetched before, if
it's loaded async, so the server can render synchronously.

------
eecks
If you need to load everything up front then you might need a loading page
like Gmail has

~~~
rajangdavis
I feel like Web Workers have been a HUGE help in this regard.

~~~
k__
I don't know if that's true.

I read that the workers can't get font measurements, which prevents layouting
on webworkers.

~~~
rajangdavis
You don't have access to the DOM with Web Workers but you can still perform an
AJAX request and load in the data without disrupting the thread that renders
the HTML. I highly recommend checking out the Pokedex progressive Web app for
such a use case.

I use it on the first page load so that I can get all of the data I need for
most of the routing upfront so that I don't need to perform an AJAX request
for every state change. I was originally having the server render JSON inline
with HTML but Web Workers ended up being faster.

------
jonesb6
Angular gives you plenty of room to shoot yourself in the foot via premature
optimizations and over engineering. Multiple times a day you need to step
back, gather outside feedback, and look at the big picture.

Also, get designer(s) involved in the process ASAP.

------
bikamonki
1\. Perceived render speed. You'll spend a lot of time tweaking your views
(nested ones in particular) to load and render as fast as good old HTML
rendered on the server. 2\. You already mentioned: SEO. But if this app of
yours is behind a login that is not a problem.

~~~
rajangdavis
Right now the render speed is good, but could be better.

One strategy that seems to work for this project has been to load in large,
JSON objects using web workers while the first page is loading. This will
allow the page to load and not disrupt the view rendering.

From there, I can use the large JSON objects to build the nested routes. I
will use one view and link to it but add parameters to filter out the data.
Ng-if has been making the rendering way way way faster going this route.

On a slow connection, two things that slows down the entire app is AJAX calls
and rendering images. That is why I have tried to pull in the large JSON
objects upfront so that the initial load might take an extra half second, but
the rest of the app navigation is speedy.

SEO is my biggest concern, but for now, I am thinking I can use older links
and do a 301 redirect to the needed resource. The only issue that I foresee is
that I cannot do a server-side render; I was thinking of just routing bots to
a non-ajax site so we can still get good SEO but it's while before I will get
to this part.

------
possibleNoob
Curious, how are Web workers helping you counter UI Thrashing?

~~~
rajangdavis
Web Workers essentially make your Javascript multi-threaded. What this means
is you can run multiple processes concurrently. You do not have access to the
DOM with Web Workers, but they are really handy for anything that is long
running or memory intensive. The benefit of this is that you can allocate Web
Workers to fetch data or run some long process and this will not disrupt the
rendering thread within the DOM.

Initially, I was having the server load up large JSON objects inline with the
DOM so that I could avoid doing multiple AJAX calls. This worked with 2 very
large JSON Objects, but as soon as I hit 3, the page would have a noticeable
jank trying to evaluate these JSON Objects and render the page.

I remembered this article from Hacker News a while back
([http://www.pocketjavascript.com/blog/2015/11/23/introducing-...](http://www.pocketjavascript.com/blog/2015/11/23/introducing-
pokedex-org)) and started using Web Workers based on the speed of the app
(it's pretty fast, check it out pokedex.org). There was a HUGE difference with
having that first initial page load with the AJAX calls in the background. The
only thing I would recommend is that you should make your first page load not
contingent upon any AJAX calls or any loops (ng-repeat in the case of
Angular). This will allow you to load in the page without having to wait for
any response from the server and usually, the page will load alongside the
data retrieved from the Web Workers.

