They can be overkill. But usually aren't, even for a static site, if the site will be maintained or updated, or has a modicum of reused views, such as a gallery.
Extra complexity. If you're building something simple, it's often quicker or easier to reason about if you just put it together with a basic web framework.
But if you're doing anything reasonably advanced or specifically need the "application-like" feel of an SPA, I don't think there are really any compelling arguments against it. Maaaaaybe the size of the initial payload, but there are strategies to mitigate against that anyway.
Just to provide a counter point, I find it much simpler to work with SPAs. You can build a clean API which only deals with JSON, and ship a separate frontend which is deployed reliably to a CDN. I can see why 'one thing' would seem simpler than 'two things', but like functions and modules, often having more makes things simpler to grok.
I'd be inclined to agree with you once the application has reached a certain level of complexity. If you're doing anything vaguely complicated around how you fetch/display/mutate your server data, then it can be a much cleaner pattern to keep a REST/GraphQL API separate from your client logic.
But on a super simple app (static site, basically), you have to deal with the overhead of the things that @aclimatt is referring to (browser history, routing, loading states etc) that basically come out of the box in your browser before you break them.
I mean, I was able to build my own single page app in a few hundred lines of javascript front-end code, and a few hundred more lines of javascript for some interface elements.
This also includes server-side rendering of the initial page.
I didn't use Angular or React or Ember or any other framework.. I just went ahead and made my own with ES6. The latest Javascript ES6 makes all of this easier.
If you're worried about complexity, just ignore the existing frameworks and do it from scratch.
You'll have to weigh how much these affect overall user experience on various browsers and how it affects your business. I don't recommend people optimize for all platforms, because you will spend forever on it. It's perfectly fine to say "Ok, my site is only for iPhone users on iOS 10+ and Windows Edge 12+." or some limited subset of users.
It would be more honest to acknowledge the downsides, and say "this site is only for iOS 10 or IE Edge users without disabilities and who never use the back button".
I'm not convinced that introducing the limitations of native apps to a non-native platform is a benefit, or a feature that makes the non-native platform more competitive.
It's certainly quicker to build something simple without it, but if the site needs to be maintained, things are much easier if your data is separate from your view.
Even if all your code resides on the backend, there's no reason why you can't maintain a similar separation between front end/display logic, and backend logic where data can be passed between the two layers but neither makes assumptions about the other.
The high level answer is "absolutely" and the more detailed answer is "it depends how you build them".
If built without server-side rendering, they're often obscenely slow. Browse the Modern Web™ on 2G and it's rage-inducing. A comment I made when this same question came up recently: https://news.ycombinator.com/item?id=13212789
If built poorly, all of the browser features you've come to rely on break. Like the back button. And links. And scrolling.
Does your website need to be accessible? Better remember to build that in too.
How about SEO? AMP? Browser history?
Most of this stuff works out of the box with traditional HTML and server-side rendered pages. So even though it's 2017, I think the question you should still ask yourself is, do you plan to re-implement all of this functionality? If not, 10MB of the hottest new JS framework may not be for you...
(And this is coming from someone who builds SPAs for clients for a living.)
I would add to your detailed answer... for what reason you're building a browser based app.
There are use cases where a browser delivered application is desirable, but you really aren't targeting "the public" meaning arbitrary consumers or business customers via a website. For example, there are good reasons to build browser based interfaces to certain enterprisy business apps for internal corporate user consumption. In these cases, you may be able to reduce the cost of maintaining full applications by relying on more simple and ubiquitous browsers while trying to keep some of the richer GUI functionality of a full fat client by building an SPA.
Nonetheless, your other points do apply (except for SEO and perhaps some of the mobile concerns, context mattering here). How you build them still matters; you just typically have greater control over much of the footprint and whether or not it's the right approach still needs to considered (SPA is not a default).
I appreciate that this isn't the typical area of interest for those coming to Hacker News, but it still exists and can be good to think of from time to time.
No. And I'm not sure I get your point. Individual users, in the context of my comment, don't get a choice... They may well have influence at some point... But it's not a technical preference at login or on ongoing debate.
Now I've seen plenty of corporate tech management make those decisions on behalf of their user communities as well as vendors providing offerings to those same corporate institutions/managers which make that choice.
The SPA frameworks and web APIs available to us now are pretty good - browserHistory as you mention, is available via `window.history.pushstate` and abstracted away for you in most SPA frameworks.
> If built poorly, all of the browser features you've come to rely on break
Alternatively phrased: "you may have to learn some new APIs"
But the SEO can be an issue (Google is not consistently executing AJAX) and render performance is definitely an issue.
I made https://www.prerender.cloud/ to address the SEO and rendering performance but that still doesn't make SPA the obvious choice.
Do you need a rich client experience? ( a UI that continuously updates to reflect the state of a data store, or more generally, a complicated UI that doesn't do a hard page refresh ) If yes, then SPA is the better solution.
If you just need a non-interactive set of pages (a blog), then a SPA probably isn't the answer.
> If not, 10MB of the hottest new JS framework may not be for you...
What hot new JS framework is 10MB? React is ~60kb gzipped
The auth frameworks (auth0.com or AWS Cognito) are large though, ~150KB gzipped. But with code splitting you can eliminate that from your main page. Or you can move the auth part of your app to a subdomain (app.example.com) and keep example.com lightweight w/out auth0/cognito)
Yeah, but you have to then call those APIs. It's like buffer overflows, which can be prevented with trivial checking, but those checks have to be done everywhere, which is why they're still happening in 2017.
It would be actively difficult to break back, links, or scrolling with server side rendering. Even on good SPA I run into bugs I have to work around on a daily basis.
I really like them - when they are done well, ie not obscenely slow.
Like most people, I hate it when something like an article or a wiki turns itself into an SPA for no reason. But I do find myself thinking that the space between "should be an SPA" and
"should be a static site" is not very big.
Very interesting! I'm trying the demos on http://intercoolerjs.org/docs.html and noticed its a little slow to update the form (after the mouse enter demo).
The demos all use mockjax (great library: https://github.com/jakerella/jquery-mockjax) with an intentional slight delay to simulate a server round trip. Otherwise, it's just a DOM swap, which should be very fast.
SPAs don't bring major benefits but they certainly make the implementation complex.
Github is a good example of it. Server side renders are generally faster than when done using front-end framework.
I will just post this tweet from a google employee here :
In my tests, a client-rendered SPA, + caching, + service worker, is still slower than an uncached server render
I think SPAs make sense for a very limited set of applications. I worked on Grooveshark, which was an SPA by necessity - you don't want page refreshes breaking music playback. We had to do an insane amount of work to get initial load times down, and it was never as fast as we wanted it to be. Some things about an SPA were nice, your server side code can be very fast and very simple and scalable when you can rely on the client side to maintain state for example, but with that comes a lot of fragility and ultimately, a lot more work.
Working on a SPA can be lots of fun, so I'd consider it for a hobby project, but I wouldn't go the SPA route for a production app unless there were some very compelling reasons to do so.
I think Grooveshark helped me create more code than coffee did for the few years it was active.
Just wanted to take a moment to thank you, and all the others who worked on it for making that possible. I know I wasn't alone to feel very, very lost and sad when it closed up.
Your bad experiences are not because SPA is a bad idea, but because you did it wrong. Once an app is loaded there's simply no reason to every throw it out and start over. Do native apps have to throw any data away every time they open a dialog, or change an aspect of the gui? No, because that is silly and stupid. The only reason page refreshes even exist is because the original design of the internet was for viewing documents. However that's not was the web is used for any longer, for the most part. The web is not simply for viewing documents. It's at least as much for running apps now. A good webapp loads ONCE and then simply makes ajax calls to get JSON from the server when it needs to re-render part of the screen or even most of the screen, but if you are still doing entire page refreshes in 2017, then i'm just sorry but you are clueless. That's just not the way to do it. Web development is complicated, and I admit just reloading the entire page every time something happens is easier, but that doesn't make it better. Like the old expression "anything worth doing is worth doing right". It applies here.
What is your favorite SPA? HN's experience on a mobile phone, loading pages when clicking links, is far superior to any SPA I know of. Compare HN to glitchy sites like Twitter or Reddit, where swipe from left back doesn't work without flashing.
If you are simply rendering content and don't require high degrees of in-page interactivity, your traditional web page centric app will be much better user experience.
SPA's for content dominant sites are often a crutch to work around slow backend. A full page refresh (with optional pre fetching of next click HTML) is vastly superior to SPA's and spinners while loading. Browsers are good at loading web pages.
Once your browser has loaded the page, it's always (and I mean 100% of the time) more efficient to get whatever data you need from your server in the form of JSON, and then use that json to update ONLY the part of the page that is changing. Even if your entire DOM tree is changing that's STILL no excuse to do a page reload. The only page reload that ever needs to happen is when the user first browses to your url.
Why do you think executing JavaScript to render a piece of content fetched from the server as JSON is always more efficient?
Browsers are really efficient rendering HTML, so it all depends on how complex your JSON to DOM element transformation is. With async parsing / loading of JavaScript, your new page can be rendered before the JS is even loaded, so the extra initializaion per page view is hidden from the user.
My site is not finished, so you can't judge it by the experience. However, it is most definitely vastly superior to Wikipedia. meta64 is hierarchical like a directory tree, and can go to any depth. I haven't written the whitepaper yet, but my design makes wikipedia look like the ancient artifact it truly is compared to meta64.
All my API is fully JSON up and JSON down(REST-like), and is like a microservice api. The server is a service with an API. The javascript client is in control of rendering the data it gets from the server. I'm convinced that's the most efficient design.
Yoiks. 12 seconds until the first piece of content is displayed (compared to Wikipedia's 0.6s). That there (TTFC - Time to first content) is the perennial problem of client-side apps, the inability to get the first piece of content immediately to the user.
Since you say it's all JSON APIs, it should be trivial to build something on the server to server-render the initial page with content and serve that up while the rest of the application tries to download itself into the browser.
if I had minification on, your comparison would be apples-to-apples. I have it off for now, for debugging. Also my APP installs itself the first time you visit the page. Think of it as an APP INSTALL. Wikipedia is NOT an app, and has nothing to install.
Thinking of it as an app install feels like an anti-pattern. Like when you turn on the Xbox for a quick five minute play, and have to sit through a 15 minute update before you can even do anything. It's a bad user experience.
This approach was a commercial failure on the native platform level (those Office-like app suites with network install of components each time you needed them). It was also a commercial failure on the "build my desktop from scratch when I boot up" environments.
It yet another tick in the "does not belong in a browser" list.
Perhaps Single Page Applications need to run inside a window where there's no browser navigation bar, no url bar. Stripped down so far it's not actually a functioning browser. That feels very much like a native GUI library at that point. Except for that ludicrous notion of the app needing to be installed each time the chrome/window is launched.
I can't see how "install the app each time you land on this website" is competitive against native's "install this app once, and run many times" approach.
A well-designed web app will have a version number embedded in the filename(s) of the large file(s), (like the filename of a minified JavaScript file containing the entire app) so that upon first visit to the page the app is downloaded and "installed" (cached), but on subsequent visits your browser grabs the file from its local cache. This is really a seamless and excellent form of deployment, and when done correctly the browser only waits for new downloads when a new "version" of the app has been deployed to the server. So well written SPAs like meta64 do have the "install this app once, and run many times" behavior.
What are you talking about? Where does he say he throws anything out? The guy built Grooveshark, one of best SPAs ever so I'm pretty sure he knows what he's doing.
A page refresh is what I call "throwing things out". Once you load your page, you don't need to load it again. Just make ajax calls to server from then on, and use the JSON response from the server to update the DOM. A page-refresh blows away the entire DOM, and is just a bad design IMO.
He said that he built a single-page app to avoid page refreshes; where did you see in his comment (or when using Grooveshark? somewhere else?) that he refreshed the page or otherwise threw things out?
meta64 is more of an APP than a webpage. For example, opening and closing dialogs is done by creating and destroying a DIV. It's not done by reloading the page, and rendering a new template on the server to send down. The reason I don't want to destroy the state of the DOM and client side variables is this: Have you ever seen a desktop app that resets all its variables back to null when a user clicks a button? Have you ever seen a desktop app that calls its 'main()' routine over and over again? Ask yourself why. There's just no reason to reload everything. Just start the app, and let it run. DOM is perfectly capable of updating the screen/page without a URL request being sent. Sending a url request to update something is stone-age thinking and 1990s design. It's the exact reason the world is moving to SPAs
If you intend to get single pageview mobile visits, sure. But if your mobile users are expected to interact at length with the page, then you get the benefits of an SPA.
I find most SPAs especially painful on mobile. HN is a great example that "classic" HTML pages can be very easy to navigate, independently of the screen size. It is very fast and behaves exactly as it should.
I see, breaking back button and links is one problem with that is mentioned a lot with SPAs. However, with a good routing library can easily solve both of these, especially when targeting modern browsers, IE11+, Android 4+ and iOS 8+.
Most routers have a "onBeforeNavigate" hook that you can use to cache the scroll position. In that regard, it's not difficult... but where to cache it is a whole 'nother question!
1. They don't work well in multiple browser tabs. Specifically if you're storing a lot of data in memory it gets duplicated between tabs. Perhaps shared workers might be a solution in the future, but not now.
2. They allow designers to "enhance" the UI in new and wonderful ways, which isn't always good for user experience. It's like being tele-ported back to the windows native apps days. Plain old web apps forced simplicity and consistency in how things worked across the web. With spa's not so much.
3. Harder to test/re-create error conditions because often state of an application isn't represented in the url. (though it can, with good app state storage/structure, actually be better I think)
4. Harder to deploy new versions to apps sitting in users browser. So a user loads your wonderful spa app and leaves it open all day in their browser. You deploy an updated version mid day... how exactly does that user know to load the new version? In traditional web apps, updated versions are loaded on next page load.
5. You have to do something for page loading indication, since the browser no longer gives any indication it's waiting on the server for new data.
For me, perfect example why not move to SPA in 2017 is Freshbooks, I'm their active user and found that their new version(introduced recently) is slow, initial load slow (very), transitions between tabs is slow etc. Personally I dont see the reason they moved to SPA (they using Ember framework which I heard very optimized in compare other frameworks(not libraries as React))
The latest version of Ember has improved performance considerably although generally it's considered slow compared to modern alternatives like Angular 2, React, Cycle, etc.
Traditional applications are simpler implementation-wise and impose fewer demands on the client (in terms of performance, using the latest browser, etc).
SPAs = Single Page Apps (not that everyone here wouldn't know that definition...but acronyms seriously suck ;). They create miscommunication and people who haven't been initiated yet are afraid to ask what it means. This one in particular has a lot of meanings: http://www.acronymfinder.com/Information-Technology/SPA.html (it likely means something completely different to a security person vs a network admin vs a front end dev.)
Lots of arguments here against an SPA are along the lines of "it probably is the wrong answer to your requirement most of the time". Related question. When is it best to use an SPA? My hunch is that it's not purely about aesthetics but something functional like Grooveshark/soundcloud not stopping a song that's playing when changing the page. Just not sure what qualifies as needing an SPA.
A poorly written SPA has worse usability than a poorly written Rails CRUD app, because your potential screw-up surface area is larger.
If you have a lot of experience writing SPAs and a team that's eager to learn, maybe you'll get everything right: server-side rendering, graceful error handling when the app plumbing fails, appropriate separation of server-side and client-side workloads, good performance on shitty connections, consistent behavior across browsers...
Personally, I enjoy using well-made SPAs, but often find myself fighting against many of them because there are so many considerations that go into making them right.
Great question. Recently I decided against splitting my application into two different apps: backend and frontend. I landed on a simple Rails app with an internal API with webpack included for ES6/React/Mobx stuff.
The engineer in me loves the idea of them being separate. For performance, for technical reasons and for scalability.
The 9-to-5'er in me doesn't agree. I would spend too much time orchestrating. For example, saving user JWT tokens and having to handle requesting one for each api query. Too much time spent on things other than my core business problem.
With a simple Rails app, I can sign in my users with Devise, no brainer. And build out React stuff on HAML templates as needed.
Yes, basically it's much easier to work with and the tools/development experience are much better - which correlates to being able to deliver faster, sometimes much faster.
Time to market is often a very important metric for a web app.
However SPAs make for a smoother experience. Most client facing apps live or die based on the user experience, and there are very few Product Managers who'd agree to a full old-style multi-page app.
As far as I'm concerned there are only two scenarios where I'll prefer an old-style app over an SPA:
1. when building internal apps for employees
2. when time to production is extremely important.
https://github.com/Clay-Ferguson/meta64 --> is still a work in progress, but if you want to see a great example of modern architecture in a web app, check it out. Yes it's mine. It's running Google Polymer, JQuery, TypeScript, SpringBoot (with embedded Tomcat), Apache Oak JCR, and is a SPA that only updates the portion of the screen that changes. It never reloads the page. Full page reloads as a part of webapp architure has been totally obsolete and bad desing for at least 7 years now. SPAs are the best architecture for a lot of different reasons.
There is one part of it that is mildly interesting (on the site itself) which is the RSS reader capability here:
lol. I forgot to say look at the github code, not the site, because I have minification turned off right now, and I do have all modules loaded into a single file (so, big initial download right now). The app is not ready for users, it's in development. I will edit to show the github link instead. thanks. (It's the architecture of it that I'm bragging on, it's not fully production-ready code yet.)
But what about it do you think is better than a traditional quickly rendered sever HTML? You have to do all this work that the browser would do for you via complicated JavaScript frameworks, merely to simulate a traditional HTML rendered page.
Compare your site to Wikipedia for example. Which feels smoother, especially on a mobile page?
Here's one of a million scenarios i can think of: Say someone is browsing nodes on the meta64 tree, then decides to edit one. I popup an editor and let them edit, they can then cancel out and go back EXACTLY where they were, without a page reload and scrolling back to where they were. SPAs are all about the fact that everything maintains state in a Javascript app until it's changed.
A javascript application is like a computer program running in your browser. Whether you realize it or not, every page reload is a 'restart' of that app. Do you think an app needs to RESTART just because someone changes screens or soemthing? It's insane right? Without an SPA that is that is genuinely whats really happening.
The browser will maintain the history if you opened the editor in a new page like HN does. For example, if I hit the back button or if there was a cancel button when composing this comment, I would return to exactly where I left off in the full list of comments.
I agree you can get into a slightly weird situation if you hit the back button after composing a new comment but your site would suffer the same.
In my site, the back button takes you to the app you were at BEFORE you went to meta64. If you want to call that a problem fine. Most SPAs abhor the back button, don't want it, don't need it, etc. There is a reason native apps don't use the 'back' feature, but simply have 'cancel' buttons to get out of things etc. The fact that the back button is such a thorn in the side of non-spa apps is a good hint that non-spa apps are not ideal, unless browsing static documents.
BTW even hacker news has a 'back functionality' that sucks. You have to go back then refresh. Meta64 is already vastly superior to other online social media experiences including wikipedia and including better than this HN site.
Most Native apps do have a back button. On android it is built into the phone.
Also, serious question: if I navigate your rss outline, how do I go back and chose a different section to browse? Right now you are stuck in the section you chose and the back button closes the app.
My GUI design is definitely not yet perfected. I experiment with lots of different things. The website is not ready for users, but mostly DOES work. The RSS is pretty much usable however. One of my own main news sources. The triangle icon at the top is what takes you to the Parent node. BTW, i'm 49yrs old so my definition of "native" still means "desktop". lol. Apps like MSFT Word (and all others) don't have back buttons, becasue the back button was invented for web browsers showing static documents, back in 1993. It just still exists, but today but is valueless (and even problematic) for SPAs.
Switching to an SPA will cause resource problems, if you have leaky memory use (i.e. maps or arrays that continually grow). An app that refreshes pages a lot (non-SPA) will have those bugs masked, because essentially every page refresh is a clean slate (release of memory). Don't blame the SPA-pattern itself for that problem.
I tried to reply to your post replying to this about using BACK button, but that is also a disaster for apps. Back button is NEVER used in SPAs. Anyway HN is not showing me a reply button so I can't reply to your actual post to the child node of this.
We all know the web was invented for static document browsing, and that was the REASON the back button was invented. Nowadays with Javascript everywhere on all devices everyone is writing APPS that run in the browser. It is simply bad design to have those apps rely on a browser-level back button, for anything at all, because the back button assumes your previous URL 10 seconds ago was different, and that's a very stupid assumption. If you can define what "back" even means in a way that applies to all APPs go for it. You can't. It's a nonsensical type of question for an APP.
As they said, perhaps browsers and SPA's aren't a good fit. It's not a good idea to have a control on screen that will potentially break the app. If you want to run in a browser environment then you need to adhere to the browser environment.
What most SPAs/apps do is 'tolerate' clicks on the back button, by taking you to some reasonable prior place, within the same site/app. Now that I think about it more, i might even support this in meta64 just to appease the masses. I could just make it where it sets the url to the NODE-permalink (maybe changing the hash part only), so that if some rogue person hits the browser back button they just to go whatever prior node they were looking at before. You talked me into it. thanks.
False. It's relatively easy for SPAs to handle the back button gracefully, you can change the URL to indicate the state of the app and add it to the browser history, listen for url change events to then change the state, etc. Grooveshark handled them just fine, you could even copy/paste a song, artist, album, playlist, etc url directly into url bar for the already open app and it would open the appropriate page without doing a refresh and interrupting your playback experience.
However, even if the back button were as terrible as you say for SPAs, that would be a mark against SPAs not browsers or the back button, which is of great utility, which is why those pesky users insist on pressing it.
lmfao. Maybe Microsoft Word hasn't figured out yet how much more usable Word would be if it scrolled you to the top every time you opened a dialog and closed it. That's hilarious.
Are you guys actually arguing that closing a dialog or popup in a web-app should be done with the back button? Or merely that the back button should DO that functionality? Once you write a true "application-like SPA" you see the flaws in the back button, for anything other than static docs, but unless you have the experience you won't "just get it". There's a reason no "back button" was ever invented for "desktop apps" and ONLY came into existence in web browsers browsing static docs.
Apps which have a meaning for "the previous place I was at", benefit from a way to go back. I do plan on a "back button" in meta64 that takes you to the prior NODE (and will be a full history of nodes), but this most definitely will not be a page refresh of the entire page. Will be a button INSIDE the app, just like all other APP functions. Meta64 is a mobile APP also, so relying on a browser-level back button is not an option.
In a way modern SPAs with a large JS installed payload/app on a browser are very simiar to a 'desktop' app communicating via a form of RPC to some remote services. The main "epiphany" most developers are still missing today is how to make a SPA/WebApp consume ONLY a data-serving API in the browser that gets back ONLY JSON (no HTML) from the server calls, and yet be able to render itself into HTML. The main leap of knowledge is to learn how to do HTML rendering on the browser, and not the client. Let the server be only an raw Data API, that sends back DATA and no presentation. Like Microservices.
- Can’t close the page without an “unsaved changes” prompt even though I didn’t interact with it at all.
- Doesn’t maintain state when restarting the browser or navigating through history.
- Doesn’t keep my scroll position when switching between tabs. Very painful if I clicked one accidentally.
- Doesn’t seem to be possible to focus or activate tabs via keyboard.
- Takes 3 seconds to load on my network. This is mostly up to latency, but making 119 requests totalling nearly 3 MB with no design to speak of yet probably doesn’t help.
- Takes a further 7 seconds to start fading in (why does it fade in?) on my device (2012 MacBook Pro 13") on a cold start, 4 seconds if just hitting Enter in the address bar.
- “Processing request” flashes on for a fraction of a second with an animated progress bar. How many progress states are there?
And it doesn’t even do anything in this state except let you flip between tabs. If things that are not this way are obsolete, I will gladly hang back and be obsolete with them.
There's nothing interesting on the site itself. It's a tool, i'm building. Like I said in the post the architecture is what I'm bragging about, not the app. The app is not tested, and all the things you mention don't surprise nor do they bother me. If you want to critique it, critique the code. I'm well aware what the state of the site itself is. Show me one issue in the code and I will thank you, but you won't find much fault there. :P
All the things I listed (and more) work by default. Your architecture broke them and didn’t benefit the user as far as I could see. Maybe list such a benefit if you’d like to argue for it?
I showed you an F1 race car up on cinder blocks being worked on so you could see the engine design. You climb in, try to drive it away, and can't so you conclude the engine design is all wrong? Genius. It's not ready to drive yet (by the public), but that doesn't mean the architecture is wrong. The architecture is state-of-the-art.
Your metaphorical engine weighs a metric ton and it’s very difficult to imagine how it’ll develop into anything resembling a racer. I’d put more effort into the car analogies but they’re making me physically ill.
> The architecture is state-of-the-art.
You should also probably back this statement up with something. Right now it appears to be a lot of self-horn-tooting.
But hey, sure, let’s spend the same half-minute to talk about the code. I open to a random TypeScript file – thank goodness you’re using TypeScript, because the JavaScript you’ve put under version control for some reason would make any non-vendor code in the language difficult to find in this time – https://github.com/Clay-Ferguson/meta64/blob/d9f3740118e8099....
declare var Dropzone;
Oh okay I guess we’re not using types here
let config: Object = {
⋮
var submitButton = document.querySelector("#" + thiz.id("uploadButton"));
This… this isn’t how you reference elements in your “state-of-the-art” architecture, right?
if (!submitButton) {
console.log("Unable to get upload button.");
}
submitButton.addEventListener("click", function(e) {
I guess the state of the art means you can’t use your browser’s debugger. Or it’s just for consistency given that you’re circumventing the rest of the browser too.
What? I have no idea what this does (`meta64` isn’t declared, I guess because this file is allergic to modules. it also appears to be a singleton or global, neither of which particularly screams “good design” when named in reference to your app) but it looks like some kind of indiscriminate state update given that nothing is being passed to it. Maybe it’s not that, but no time to find out. We’re running out of seconds here.
> This… this isn’t how you reference elements in your “state-of-the-art” architecture, right?
oh no it is. Bonus points for kebab-case when the other two we’ve seen so far are camel. I’m nitpicking style because a meaningless literary device involving cars made me irritable. Sorry.
let ret: boolean = false;
for (let file of this.fileList) {
if (file["name"].toLowerCase().endsWith(".zip")) {
return true;
}
}
return ret;
That was a useful variable! I would write this:
return this.fileList.some(isZipFile);
and then probably not write this in the end because it’s a filename check but we’re here to talk about architecture or something, not that:
const isZipFile = /\.zip$/i!test;
`!` is a macro for bound property access in my state-of-the-art architecture.
F1 race car analogy still works: I show you an engine on the test stand, saying it's a good design, and you proceed to point out all the loose wires, duct taped electrical connections, and all the stuff that's there temporarily because it's on a test stand, or are details that simply haven't even been addressed yet. I stand by the 'state-of-the-art' claim, and you didn't find any unfinished work that was news to me. My todo list is 10x as long as your list, but the architecture itself is perfect. SpringBoot, JSON API from browser, JCR, Mongo, GooglePolymer, etc., and the way it is all combined is the best architure for a modern app. and ESPECIALLY the SPA aspect.
I bragged specifically about the state-of-the-art architecture. Architecture can be perfected months or even years before a codebase itself is production-ready, and free of all unimportant band-aids/short-cuts. I'm not going thru my todo list for you to explain why each thing on it hasn't been done yet. You guys are hilarious.
What definition of “architecture” are you using here? Is it at a very high level (“I’m using an SPA and an MV* JSON API”)? If it is, I hate to break it to you, but that’s how most of these things have already worked for ages.
I use the standard every-day definition of "architecture". You're right about JSON and SPAs really catching on finally. That's why I shared this post, and to help people out who've been brainwashed into thinking SPAs are bad. I hope some people also see the code and find out how awesome TypeScript is. Also the JCR. Lots of well kept secrets encapsulated in meta64.
The "framework" is Google Polymer. The only thing I use JQuery for is trivial stuff like selecting elements, cookie support, and DOM manipulations, which is exactly what the rest of the industry uses JQuery for, but i bet you didn't know any of that.
> Show me one issue in the code and I will thank you, but you won't find much fault there.
You need to get off your high horse. Not even Jon Skeet thinks he creates perfect code.
Your project is nowhere near "great example of modern architecture in a web app". You depend on mysterious global variables everywhere, mix jQuery with HTML tags hard coded in strings (with classes and everything) and have no unit or E2E tests. I applaud your overconfidence though. Most people are ashame of their code, even when it's ten times better than this.
Serious tip: invest time in learning React+Redux/MobX or Vue. I promise you it will make your life much better once you realize your shortcomings.
Those are not global variables. They're TypeScript namespaces. Everything is in a namespace. I'm using namespaces to accomplish the "singleton pattern" in TypeScript, and to avoid globals. Most modern Java-Spring Server-side code usually uses lots of singleton services, and i'm doing the same sort of pattern, in TypeScript.
Regarding the generating of the presentation code being done in pure JS/TS rather than a bunch of template files rendered on the server is actually another innovation in disquise. This app is so dynamic that even if it were template-based the templates themselves would be 90% logic, which is why just generating presentation code on the client is ideal. This IS mainly a client-side app, that consumes a server-side JSON API, rather than getting any actual HTML from the server. Yes it's inverted from the way the rest most people are still doing it, but what I'm doing is the future. Learn a bit about microservices and RESTfull stuff and you might begin to see the light, about how a browser can consume an API that sends back JSON DATA rather than rendered HTML. It's the future. I'm doing it right. You just don't get it yet. And if you're irked by my confidence, frankly that makes me happy.
I haven't seen your code, but using lots of Singletons is usually a code smell.
> Learn a bit about microservices and RESTfull stuff and you might begin to see the light, about how a browser can consume an API that sends back JSON DATA rather than rendered HTML. It's the future.
RESTful APIs are great but you are 10 years late to the party.
Using RESTful APIs is exactly the same thing web services were back 15 years ago with XML.
I actually don't like RESTful stuff. I used the term to point out that the conversation between client and server is pure JSON which is the technique REST popularized. meta64 has a 'functional' interface to the server, and yes it's state-of-the-art. JSON up and JSON down.
Also, singletons are simply bad practice so that's just another sign that your code is far from "state of the art".
> Yes it's inverted from the way the rest most people are still doing it, but what I'm doing is the future.
Of course you consume JSON through a REST API and let the client handle HTML generation when building an SPA. "The future" you're speaking of has been industry standard for 10 years. Nothing radical or innovative about it. How do you think React, Vue or Angular work?
That's funny that you don't like singletons. Not sure how you got brainwashed about them, but they are good and VERY widely used today by all major codebases. Singleton is literally the "default scope" for all Spring Beans, so you are so completely clueless about that.
You need to learn how to deal with dependencies properly. When reading a file it should always be obvious where the dependency comes from.
There are a few use cases for singletons but using them as globals to avoid passing dependencies around is simply bad practice. Your code is a tightly coupled and untestable mess at its current state.
It's funny that you arrogantly just dismiss every single person in this thread as idiots. You need to take a step back and consider that you might actually be wrong.
haha. There is ONE variable that violates the naming convention. srch!=search.ts. I had a reason for doing that but just never changed it back.
The code will easily convert to modules if I ever decide to. For now I'm just keeping it simple. (i.e. no circular-reference risk, etc) It's easy to throw rocks at a design, but if you spent a day working in meta64 you'd realize how intuitive it is. Obscure is never the word you'd use.
If you want a critique of the code: it shows a clear lack of understanding of Typescript/JS modules. Your Typescript is compiled to a single mega file and outputs a giant global variable. The namespace keyword in Typescript is often considered bad form versus proper module organization. You even have a module loader installed (SystemJS) to use, but you aren't actually using it for module loading, its almost just a glorified <script> tag with the way you have the code architected. With a little more configuration you could get a lot more mileage out of SystemJS. (Furthermore, your code retains comments with mentions at failures to understand modules in general and a previous attempt with RequireJS specifically.)
I'm not sure why you would assume no one could find faults in the code when they even have comments right there about said faults.
Based on your definition of "giant global variable" the $ in JQuery is also precisely that. lmfao. Seriously bro, you know i'm right. Yes the m64 namespace is designed to encompass the entire app, to keep meta64 from having ANY global scope variables except for that one. Precisely like the JQuery $. I'm doing namespaces precisely by-the-book.
So the mere fact that you said "giant global variable" is proof beyond any doubt that you don't know jack about namespaces, and all you were barely able to do was notice the lack of module loaders at the top of my files.
You may not even know this, but packaging an app into a single downloadable JS file (modules not even necessary) is actually the modern approach. It lends itself to better compression+minification. Once installed in a browser cache it's usable just like an 'installed' piece of software. But I thank you for the phrase "giant global variable". I'll be laughing at that for years to come.
«Based on your definition of "giant global variable" the $ in JQuery is also precisely that.»
Which is part of why I haven't used JQuery in ages. The last time I saw someone mention JQuery as a best practice example was arguably a decade ago, if not longer than that. If you are doing things by a book, it's not a book that was written any time recently.
At this point, any library I see that uses a global variable and doesn't support proper module loading is a code smell and a sign that the library is probably not well maintained.
«You may not even know this, but packaging an app into a single downloadable JS file (modules not even necessary) is actually the modern approach. It lends itself to better compression+minification.»
Actually, this has changed a lot in recent years, and your ignorance of how module systems work also shows up here. First of all, you can have compression+minification and modules. Even in the bad old RequireJS days when AMD loaders were state of the art there were good AMD bundlers. With modern EcmaScript 2015 modules (ES2015) there are a lot of great bundler options, Webpack being a particular darling right now, but there's also others.
This sort of bundling, compression, and minification is now seen as the last step in the chain, the job of the application/website developer and no longer something that every JS library under the sun needs to do bespoke. A big reason for this is developer experience (it's easier to debug when everything is a large collection of modules), but it also makes for a better user experience (the final application knows more precisely what modules it needs to operate [tree shaking], and in what order [optimizing bundles for specific use cases/first impressions]).
Overall, frontend web development is shifting to share modules with more backend operations and the node ecosystem's npm (and relatives that piggyback on it like jspm and yarn) has become the package/module management ecosystem of choice. This is great for developer experience, whether or not you are doing your backend development in Node, because you have an easier access to a larger ecosystem of libraries, an increasing number of which are "universal"/"isomorphic" running just as well in the browser as on a server running Node (or app running in Electron or an app running in Cordova).
Furthermore, for some of these platforms bundling+compression+minification isn't even necessary, such as an app or server loaded directly from the filesystem instead of shipped to a browser via HTTP. Even that is changing best practices because HTTP/2 mitigates a lot of the reasons that you even need to bundle in the first place (HTTP/1.x often can only handle a single file per connection while HTTP/2 was designed from the start to bulk download a collection of files in a single connection; the connection overhead being the biggest reason to bundle in HTTP/1.x).
There's a lot of great stuff to learn about the current state of the art with respect to modules and ES2015 and modern frontend development ecosystem if you thought to give it a try instead of sticking to old practices now long considered harmful (not just in JS, even, but in about any language: giant global stateful singletons like JQuery are a miserable antipattern any time they show up in any language in the history of programming).
I use Ember, which abstracts most of the things you're mentioning, but if you have resources (other than the websites for the tools you mention) you'd like to recommend they'd be much appreciated!
What I've skimmed of recent PluralSight courses on the subjects seem to be good resources, especially if you are the sort of learner that prefers video tutorials as benefits some of my colleagues.
There's a lot of scattered Medium articles and "Awesome Lists" I've seen from time to time, but I don't seem to have any organized bookmarks on the matter.
It's hilarious that you say bundling+compression+minification is "obsolete" and your suggested better way is "stop using HTTP, load from a filesystem." You had to bend over backwards THAT ridiculously far to try and find a fault with it. The technique 99.999% of the world is moving to. so funny. And saying that JQuery's namespace ($) is a "big global variable", and therefore you hate it, will be something i'll be telling buddies about for years to come.
All I can suggest is you try rereading my comments with an open mind.
I did not say bundling+compression+minification is "obsolete", I said things had shifted in how front-end development uses those things as tools in their toolbelt. It's a very different world these days in JS and lot of "must" best practices became "when needed" best practices. All I was trying to point out was that you don't start with bundling+minification+compression, you pull in those tools as and if you need them.
As for JQuery, I'm not the only engineer that sees it as a problem. It's the second sentence of the Wikipedia article summary on the Singleton pattern, for instance:
« There are some who are critical of the singleton pattern and consider it to be an anti-pattern in that it is frequently used in scenarios where it is not beneficial, introduces unnecessary restrictions in situations where a sole instance of a class is not actually required, and introduces global state into an application. »
1) Bundling: Glad you're backing off your anti-bundling position. Good move.
2) JQuery: Any JS library is better when it adds only a single variable to global scope. JQuery uses $. My app uses 'm64'. Anyway what is the better DOM-manipulation API I should be using? Do tell. It has to be an actual competitor to JQuery (i.e. lightweight pure JS library with same DOM capabilities.)
3) Singletons: I use Spring Beans on the server side to be sure I have singletons 'correct', and I use TypeScript namespaces on the browser side. The most common way people screw up singletons is related to the instantiation issues, and circular-references. By using Spring @Component and TypeScript namespaces, I avoid the exact issues you are concerned about. Based on my architecture it's literally IMPOSSIBLE to encounter those issues even if I tried.
No-one has argued against bundling. You keep misreading all the comments, adding "bet you didn't know that" to the most obvious things in the world. Why is that?
> Any JS library is better when it adds only a single variable to global scope.
Because of the naming convention i'm using for namespaces, if I ever decide to convert to modules, adding the import statements will be straightforward and nothing will even be renamed. The namespaces would simply 'become' modules, without any editing of the implementation. This was not an accident. I designed it that way.
I already had modules on my TODO list, implemented it, didn't like it, took it back out, and converted to TS namespaces.
I have been waiting for React to emerge as a clear winner before using it. I only use libraries, APIs, languages, databases, etc. that I've concluded will still be around 10yrs into the future. I don't like Angular, and don't know Vue. If React doesn't replicate some future plans Google has for Polymer ecosystem i might try it.
> Based on your definition of "giant global variable" the $ in JQuery is also precisely that.
jQuery’s `$` and `jQuery` are giant global variables when you include jQuery as a <script> or by concatenation. When you load it as a module, it adds no globals, which is rather nice. Look at https://www.npmjs.com/package/jquery’s “Babel” section, which is also how you use it with TypeScript.
You also seem to be under the impression that polluting the global namespace is the only bad thing about globals. It’s one factor, but it’s also just generally an indicator of bad design. Organizing global mutable state into singletons and namespaces doesn’t help with this. You’re also crowing about TypeScript but throwing away many of its benefits by not using modules or typings for the libraries you depend on.
> You may not even know this, but packaging an app into a single downloadable JS file (modules not even necessary) is actually the modern approach. It lends itself to better compression+minification.
(ES6) modules actually quite necessary if you want your minifier/bundler to be able to remove unused code reliably. Closure Compiler can only do so much.
1) It's too hard. And I think this argument gets way less attention than it deserves. Most businesses these days win or lose based on agility and time to market. The more energy you spend innovating your tech stack, the less energy you have available to innovate your business model.
2) Javascript bloat. This is increasingly important as mobile gradually overtakes desktop, and cheap Android phones proliferate. The more client-side code, the more your performance is limited by your users' own crappy devices.
3) It's not trendy anymore. There was only one truly exemplary, popular SPA, and that was Gmail. Isomorphic/universal is the new hotness, and that requires server-side rendering.
4) The Stack Overflow Effect[1] didn't take. Every client-side framework is so painful that people can't wait to migrate their production apps to something different. So now there are dozens, probably hundreds of frameworks in production, and each community lacks the stability and size to allow newcomers to bootstrap quickly.
5) It's just bad UX. People still manage to get SPAs wrong in 2017. My bank just rolled out a new SPA interface to their suite of consumer budgeting tools, and didn't bother with the History API. Every time I navigate back, it takes me out of the suite entirely and I have to find my way back manually to where I was. I think this problem is a consequence of #1 and #4, though.
SPA is very broad. Static content work best when delivering only the minimal assets needed to render. Googles AMP does a very good job at this.
Dynamic apps have a very different set of concerns. All SPAs aren't the same. Only the stateless, pure renders, like React and Vue, offer a compelling potential to unify the concerns of static content and apps that need to run across multiple platforms and network conditions. It comes at increased complexity and cost to maintain a multi-platform structure. If there's no business or consumer benefit there's not need to over engineer a solution to use a SPA.
147 comments
[ 0.24 ms ] story [ 171 ms ] threadBut if you're doing anything reasonably advanced or specifically need the "application-like" feel of an SPA, I don't think there are really any compelling arguments against it. Maaaaaybe the size of the initial payload, but there are strategies to mitigate against that anyway.
But on a super simple app (static site, basically), you have to deal with the overhead of the things that @aclimatt is referring to (browser history, routing, loading states etc) that basically come out of the box in your browser before you break them.
See: https://www.futureclaw.com
This also includes server-side rendering of the initial page.
I didn't use Angular or React or Ember or any other framework.. I just went ahead and made my own with ES6. The latest Javascript ES6 makes all of this easier.
If you're worried about complexity, just ignore the existing frameworks and do it from scratch.
Broken back button: check. Broken scrolling: check. Broken accessibility: check.
You'll have to weigh how much these affect overall user experience on various browsers and how it affects your business. I don't recommend people optimize for all platforms, because you will spend forever on it. It's perfectly fine to say "Ok, my site is only for iPhone users on iOS 10+ and Windows Edge 12+." or some limited subset of users.
Since I build so many SPAs, at this point I can spin up and work on a new one much faster than I could for a non-SPA.
If built without server-side rendering, they're often obscenely slow. Browse the Modern Web™ on 2G and it's rage-inducing. A comment I made when this same question came up recently: https://news.ycombinator.com/item?id=13212789
If built poorly, all of the browser features you've come to rely on break. Like the back button. And links. And scrolling.
Does your website need to be accessible? Better remember to build that in too.
How about SEO? AMP? Browser history?
Most of this stuff works out of the box with traditional HTML and server-side rendered pages. So even though it's 2017, I think the question you should still ask yourself is, do you plan to re-implement all of this functionality? If not, 10MB of the hottest new JS framework may not be for you...
(And this is coming from someone who builds SPAs for clients for a living.)
There are use cases where a browser delivered application is desirable, but you really aren't targeting "the public" meaning arbitrary consumers or business customers via a website. For example, there are good reasons to build browser based interfaces to certain enterprisy business apps for internal corporate user consumption. In these cases, you may be able to reduce the cost of maintaining full applications by relying on more simple and ubiquitous browsers while trying to keep some of the richer GUI functionality of a full fat client by building an SPA.
Nonetheless, your other points do apply (except for SEO and perhaps some of the mobile concerns, context mattering here). How you build them still matters; you just typically have greater control over much of the footprint and whether or not it's the right approach still needs to considered (SPA is not a default).
I appreciate that this isn't the typical area of interest for those coming to Hacker News, but it still exists and can be good to think of from time to time.
Now I've seen plenty of corporate tech management make those decisions on behalf of their user communities as well as vendors providing offerings to those same corporate institutions/managers which make that choice.
> If built poorly, all of the browser features you've come to rely on break
Alternatively phrased: "you may have to learn some new APIs"
But the SEO can be an issue (Google is not consistently executing AJAX) and render performance is definitely an issue.
I made https://www.prerender.cloud/ to address the SEO and rendering performance but that still doesn't make SPA the obvious choice.
Do you need a rich client experience? ( a UI that continuously updates to reflect the state of a data store, or more generally, a complicated UI that doesn't do a hard page refresh ) If yes, then SPA is the better solution.
If you just need a non-interactive set of pages (a blog), then a SPA probably isn't the answer.
> If not, 10MB of the hottest new JS framework may not be for you...
What hot new JS framework is 10MB? React is ~60kb gzipped
The auth frameworks (auth0.com or AWS Cognito) are large though, ~150KB gzipped. But with code splitting you can eliminate that from your main page. Or you can move the auth part of your app to a subdomain (app.example.com) and keep example.com lightweight w/out auth0/cognito)
Yeah, but you have to then call those APIs. It's like buffer overflows, which can be prevented with trivial checking, but those checks have to be done everywhere, which is why they're still happening in 2017.
It would be actively difficult to break back, links, or scrolling with server side rendering. Even on good SPA I run into bugs I have to work around on a daily basis.
Like most people, I hate it when something like an article or a wiki turns itself into an SPA for no reason. But I do find myself thinking that the space between "should be an SPA" and "should be a static site" is not very big.
Unnecessary complexity: http://intercoolerjs.org/2016/05/19/back-to-the-future.html
API Churn/Security tradeoffs: http://intercoolerjs.org/2016/02/17/api-churn-vs-security.ht...
REST was a magical idea, but not for JSON & SPAs: http://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.ht...
Do you update only after the post succeeds?
The demos all use mockjax (great library: https://github.com/jakerella/jquery-mockjax) with an intentional slight delay to simulate a server round trip. Otherwise, it's just a DOM swap, which should be very fast.
The right tool really depends on your specific use-case, and business situation...
Github is a good example of it. Server side renders are generally faster than when done using front-end framework.
I will just post this tweet from a google employee here : In my tests, a client-rendered SPA, + caching, + service worker, is still slower than an uncached server render
https://twitter.com/jaffathecake/status/799199764558401536
Working on a SPA can be lots of fun, so I'd consider it for a hobby project, but I wouldn't go the SPA route for a production app unless there were some very compelling reasons to do so.
Just wanted to take a moment to thank you, and all the others who worked on it for making that possible. I know I wasn't alone to feel very, very lost and sad when it closed up.
So, free beer in case we ever meet IRL.
If you are simply rendering content and don't require high degrees of in-page interactivity, your traditional web page centric app will be much better user experience.
SPA's for content dominant sites are often a crutch to work around slow backend. A full page refresh (with optional pre fetching of next click HTML) is vastly superior to SPA's and spinners while loading. Browsers are good at loading web pages.
Browsers are really efficient rendering HTML, so it all depends on how complex your JSON to DOM element transformation is. With async parsing / loading of JavaScript, your new page can be rendered before the JS is even loaded, so the extra initializaion per page view is hidden from the user.
Which site feels better, yours or Wikipedia?
All my API is fully JSON up and JSON down(REST-like), and is like a microservice api. The server is a service with an API. The javascript client is in control of rendering the data it gets from the server. I'm convinced that's the most efficient design.
Since you say it's all JSON APIs, it should be trivial to build something on the server to server-render the initial page with content and serve that up while the rest of the application tries to download itself into the browser.
This approach was a commercial failure on the native platform level (those Office-like app suites with network install of components each time you needed them). It was also a commercial failure on the "build my desktop from scratch when I boot up" environments.
It yet another tick in the "does not belong in a browser" list.
Perhaps Single Page Applications need to run inside a window where there's no browser navigation bar, no url bar. Stripped down so far it's not actually a functioning browser. That feels very much like a native GUI library at that point. Except for that ludicrous notion of the app needing to be installed each time the chrome/window is launched.
I can't see how "install the app each time you land on this website" is competitive against native's "install this app once, and run many times" approach.
Why? Really seriously question. Assuming everything is cached and it is not some super large app what's the problem with that?
Even for older browsers, in "no-framework" setting, I have used https://github.com/mtrpcic/pathjs with a lot of success.
Getting scroll position to stay where you left is tricky, though. Has anybody posted some tricks on getting this right?
1. They don't work well in multiple browser tabs. Specifically if you're storing a lot of data in memory it gets duplicated between tabs. Perhaps shared workers might be a solution in the future, but not now.
2. They allow designers to "enhance" the UI in new and wonderful ways, which isn't always good for user experience. It's like being tele-ported back to the windows native apps days. Plain old web apps forced simplicity and consistency in how things worked across the web. With spa's not so much.
3. Harder to test/re-create error conditions because often state of an application isn't represented in the url. (though it can, with good app state storage/structure, actually be better I think)
4. Harder to deploy new versions to apps sitting in users browser. So a user loads your wonderful spa app and leaves it open all day in their browser. You deploy an updated version mid day... how exactly does that user know to load the new version? In traditional web apps, updated versions are loaded on next page load.
5. You have to do something for page loading indication, since the browser no longer gives any indication it's waiting on the server for new data.
One of the apps I'm building has one page that needs complex interactivity. So that's the only page which will have a vuejs/vuex-driven UI.
For the rest, Turbolinks gives me high performance for (almost) free.
So for someone like me who doesn't like typing more, spending more time and fixing more issues than absolutely necessary, SPAs are a no go for 2017.
Hell even Google cannot get SPAs right using their own framework [1]
[1] - https://pbs.twimg.com/media/C0n-bqwWEAEV6eg.jpg:large
If you have a lot of experience writing SPAs and a team that's eager to learn, maybe you'll get everything right: server-side rendering, graceful error handling when the app plumbing fails, appropriate separation of server-side and client-side workloads, good performance on shitty connections, consistent behavior across browsers...
Personally, I enjoy using well-made SPAs, but often find myself fighting against many of them because there are so many considerations that go into making them right.
https://sergiotapia.me/using-webpack-and-react-in-a-rails-ap...
Best of both worlds.
The engineer in me loves the idea of them being separate. For performance, for technical reasons and for scalability.
The 9-to-5'er in me doesn't agree. I would spend too much time orchestrating. For example, saving user JWT tokens and having to handle requesting one for each api query. Too much time spent on things other than my core business problem.
With a simple Rails app, I can sign in my users with Devise, no brainer. And build out React stuff on HAML templates as needed.
Time to market is often a very important metric for a web app.
However SPAs make for a smoother experience. Most client facing apps live or die based on the user experience, and there are very few Product Managers who'd agree to a full old-style multi-page app.
As far as I'm concerned there are only two scenarios where I'll prefer an old-style app over an SPA:
1. when building internal apps for employees
2. when time to production is extremely important.
There is one part of it that is mildly interesting (on the site itself) which is the RSS reader capability here:
http://meta64.com/?id=/rss/feeds
Also, edge swipe from right or left on iPhone completely doesn't work.
Compare your site to Wikipedia for example. Which feels smoother, especially on a mobile page?
A javascript application is like a computer program running in your browser. Whether you realize it or not, every page reload is a 'restart' of that app. Do you think an app needs to RESTART just because someone changes screens or soemthing? It's insane right? Without an SPA that is that is genuinely whats really happening.
I agree you can get into a slightly weird situation if you hit the back button after composing a new comment but your site would suffer the same.
BTW even hacker news has a 'back functionality' that sucks. You have to go back then refresh. Meta64 is already vastly superior to other online social media experiences including wikipedia and including better than this HN site.
Also, serious question: if I navigate your rss outline, how do I go back and chose a different section to browse? Right now you are stuck in the section you chose and the back button closes the app.
Perhaps, where this goes is that Single Page Apps and web browsers aren't a good fit together?
It exists in quite a few dekstop apps (explorer and control panel on windows to name a couple). Android also has an ubiquitous back button.
- Can’t open any of the pages in a new tab.
- Can’t go back.
- Can’t close the page without an “unsaved changes” prompt even though I didn’t interact with it at all.
- Doesn’t maintain state when restarting the browser or navigating through history.
- Doesn’t keep my scroll position when switching between tabs. Very painful if I clicked one accidentally.
- Doesn’t seem to be possible to focus or activate tabs via keyboard.
- Takes 3 seconds to load on my network. This is mostly up to latency, but making 119 requests totalling nearly 3 MB with no design to speak of yet probably doesn’t help.
- Takes a further 7 seconds to start fading in (why does it fade in?) on my device (2012 MacBook Pro 13") on a cold start, 4 seconds if just hitting Enter in the address bar.
- “Processing request” flashes on for a fraction of a second with an animated progress bar. How many progress states are there?
And it doesn’t even do anything in this state except let you flip between tabs. If things that are not this way are obsolete, I will gladly hang back and be obsolete with them.
But hey, the menu animation is very smooth!
> The architecture is state-of-the-art.
You should also probably back this statement up with something. Right now it appears to be a lot of self-horn-tooting.
But hey, sure, let’s spend the same half-minute to talk about the code. I open to a random TypeScript file – thank goodness you’re using TypeScript, because the JavaScript you’ve put under version control for some reason would make any non-vendor code in the language difficult to find in this time – https://github.com/Clay-Ferguson/meta64/blob/d9f3740118e8099....
Oh okay I guess we’re not using types here This… this isn’t how you reference elements in your “state-of-the-art” architecture, right? I guess the state of the art means you can’t use your browser’s debugger. Or it’s just for consistency given that you’re circumventing the rest of the browser too. What? I have no idea what this does (`meta64` isn’t declared, I guess because this file is allergic to modules. it also appears to be a singleton or global, neither of which particularly screams “good design” when named in reference to your app) but it looks like some kind of indiscriminate state update given that nothing is being passed to it. Maybe it’s not that, but no time to find out. We’re running out of seconds here.Wait, back up a moment:
Isn’t a major aspect of most of these fancy SPA frameworks that you don’t have to do this? > This… this isn’t how you reference elements in your “state-of-the-art” architecture, right?oh no it is. Bonus points for kebab-case when the other two we’ve seen so far are camel. I’m nitpicking style because a meaningless literary device involving cars made me irritable. Sorry.
That was a useful variable! I would write this: and then probably not write this in the end because it’s a filename check but we’re here to talk about architecture or something, not that: `!` is a macro for bound property access in my state-of-the-art architecture. I would think a F1 racer ninja rockstar architecture would be able to bind the visibility of this button to some st...You need to get off your high horse. Not even Jon Skeet thinks he creates perfect code.
Your project is nowhere near "great example of modern architecture in a web app". You depend on mysterious global variables everywhere, mix jQuery with HTML tags hard coded in strings (with classes and everything) and have no unit or E2E tests. I applaud your overconfidence though. Most people are ashame of their code, even when it's ten times better than this.
Serious tip: invest time in learning React+Redux/MobX or Vue. I promise you it will make your life much better once you realize your shortcomings.
Regarding the generating of the presentation code being done in pure JS/TS rather than a bunch of template files rendered on the server is actually another innovation in disquise. This app is so dynamic that even if it were template-based the templates themselves would be 90% logic, which is why just generating presentation code on the client is ideal. This IS mainly a client-side app, that consumes a server-side JSON API, rather than getting any actual HTML from the server. Yes it's inverted from the way the rest most people are still doing it, but what I'm doing is the future. Learn a bit about microservices and RESTfull stuff and you might begin to see the light, about how a browser can consume an API that sends back JSON DATA rather than rendered HTML. It's the future. I'm doing it right. You just don't get it yet. And if you're irked by my confidence, frankly that makes me happy.
> Learn a bit about microservices and RESTfull stuff and you might begin to see the light, about how a browser can consume an API that sends back JSON DATA rather than rendered HTML. It's the future.
RESTful APIs are great but you are 10 years late to the party.
Using RESTful APIs is exactly the same thing web services were back 15 years ago with XML.
https://apievangelist.com/2012/12/20/history-of-apis/
Also, singletons are simply bad practice so that's just another sign that your code is far from "state of the art".
> Yes it's inverted from the way the rest most people are still doing it, but what I'm doing is the future.
Of course you consume JSON through a REST API and let the client handle HTML generation when building an SPA. "The future" you're speaking of has been industry standard for 10 years. Nothing radical or innovative about it. How do you think React, Vue or Angular work?
I think it was about line 8 in this file:
https://github.com/Clay-Ferguson/meta64/blob/master/src/main...
That's funny that you don't like singletons. Not sure how you got brainwashed about them, but they are good and VERY widely used today by all major codebases. Singleton is literally the "default scope" for all Spring Beans, so you are so completely clueless about that.
There are a few use cases for singletons but using them as globals to avoid passing dependencies around is simply bad practice. Your code is a tightly coupled and untestable mess at its current state.
It's funny that you arrogantly just dismiss every single person in this thread as idiots. You need to take a step back and consider that you might actually be wrong.
Having a naming convention is no reason not to do proper dependency management. This global approach just obscures things and makes it untestable.
The code will easily convert to modules if I ever decide to. For now I'm just keeping it simple. (i.e. no circular-reference risk, etc) It's easy to throw rocks at a design, but if you spent a day working in meta64 you'd realize how intuitive it is. Obscure is never the word you'd use.
I'm not sure why you would assume no one could find faults in the code when they even have comments right there about said faults.
So the mere fact that you said "giant global variable" is proof beyond any doubt that you don't know jack about namespaces, and all you were barely able to do was notice the lack of module loaders at the top of my files.
You may not even know this, but packaging an app into a single downloadable JS file (modules not even necessary) is actually the modern approach. It lends itself to better compression+minification. Once installed in a browser cache it's usable just like an 'installed' piece of software. But I thank you for the phrase "giant global variable". I'll be laughing at that for years to come.
Which is part of why I haven't used JQuery in ages. The last time I saw someone mention JQuery as a best practice example was arguably a decade ago, if not longer than that. If you are doing things by a book, it's not a book that was written any time recently.
At this point, any library I see that uses a global variable and doesn't support proper module loading is a code smell and a sign that the library is probably not well maintained.
«You may not even know this, but packaging an app into a single downloadable JS file (modules not even necessary) is actually the modern approach. It lends itself to better compression+minification.»
Actually, this has changed a lot in recent years, and your ignorance of how module systems work also shows up here. First of all, you can have compression+minification and modules. Even in the bad old RequireJS days when AMD loaders were state of the art there were good AMD bundlers. With modern EcmaScript 2015 modules (ES2015) there are a lot of great bundler options, Webpack being a particular darling right now, but there's also others.
This sort of bundling, compression, and minification is now seen as the last step in the chain, the job of the application/website developer and no longer something that every JS library under the sun needs to do bespoke. A big reason for this is developer experience (it's easier to debug when everything is a large collection of modules), but it also makes for a better user experience (the final application knows more precisely what modules it needs to operate [tree shaking], and in what order [optimizing bundles for specific use cases/first impressions]).
Overall, frontend web development is shifting to share modules with more backend operations and the node ecosystem's npm (and relatives that piggyback on it like jspm and yarn) has become the package/module management ecosystem of choice. This is great for developer experience, whether or not you are doing your backend development in Node, because you have an easier access to a larger ecosystem of libraries, an increasing number of which are "universal"/"isomorphic" running just as well in the browser as on a server running Node (or app running in Electron or an app running in Cordova).
Furthermore, for some of these platforms bundling+compression+minification isn't even necessary, such as an app or server loaded directly from the filesystem instead of shipped to a browser via HTTP. Even that is changing best practices because HTTP/2 mitigates a lot of the reasons that you even need to bundle in the first place (HTTP/1.x often can only handle a single file per connection while HTTP/2 was designed from the start to bulk download a collection of files in a single connection; the connection overhead being the biggest reason to bundle in HTTP/1.x).
There's a lot of great stuff to learn about the current state of the art with respect to modules and ES2015 and modern frontend development ecosystem if you thought to give it a try instead of sticking to old practices now long considered harmful (not just in JS, even, but in about any language: giant global stateful singletons like JQuery are a miserable antipattern any time they show up in any language in the history of programming).
I'm using Google's minification:
https://developers.google.com/closure/compiler/
There's a lot of scattered Medium articles and "Awesome Lists" I've seen from time to time, but I don't seem to have any organized bookmarks on the matter.
I did not say bundling+compression+minification is "obsolete", I said things had shifted in how front-end development uses those things as tools in their toolbelt. It's a very different world these days in JS and lot of "must" best practices became "when needed" best practices. All I was trying to point out was that you don't start with bundling+minification+compression, you pull in those tools as and if you need them.
As for JQuery, I'm not the only engineer that sees it as a problem. It's the second sentence of the Wikipedia article summary on the Singleton pattern, for instance:
« There are some who are critical of the singleton pattern and consider it to be an anti-pattern in that it is frequently used in scenarios where it is not beneficial, introduces unnecessary restrictions in situations where a sole instance of a class is not actually required, and introduces global state into an application. »
-- https://en.wikipedia.org/wiki/Singleton_pattern
Google for "Singleton anti pattern" for many pages of articles on the subject, if you care.
2) JQuery: Any JS library is better when it adds only a single variable to global scope. JQuery uses $. My app uses 'm64'. Anyway what is the better DOM-manipulation API I should be using? Do tell. It has to be an actual competitor to JQuery (i.e. lightweight pure JS library with same DOM capabilities.)
3) Singletons: I use Spring Beans on the server side to be sure I have singletons 'correct', and I use TypeScript namespaces on the browser side. The most common way people screw up singletons is related to the instantiation issues, and circular-references. By using Spring @Component and TypeScript namespaces, I avoid the exact issues you are concerned about. Based on my architecture it's literally IMPOSSIBLE to encounter those issues even if I tried.
> Any JS library is better when it adds only a single variable to global scope.
No, this is how you use jQuery in 2017:
Simple as that. Just stop using global variables.What are your thoughts on for example React, Vue or Angular? Any reason you didn't go that route instead?
I have been waiting for React to emerge as a clear winner before using it. I only use libraries, APIs, languages, databases, etc. that I've concluded will still be around 10yrs into the future. I don't like Angular, and don't know Vue. If React doesn't replicate some future plans Google has for Polymer ecosystem i might try it.
> Based on your definition of "giant global variable" the $ in JQuery is also precisely that.
jQuery’s `$` and `jQuery` are giant global variables when you include jQuery as a <script> or by concatenation. When you load it as a module, it adds no globals, which is rather nice. Look at https://www.npmjs.com/package/jquery’s “Babel” section, which is also how you use it with TypeScript.
You also seem to be under the impression that polluting the global namespace is the only bad thing about globals. It’s one factor, but it’s also just generally an indicator of bad design. Organizing global mutable state into singletons and namespaces doesn’t help with this. You’re also crowing about TypeScript but throwing away many of its benefits by not using modules or typings for the libraries you depend on.
> You may not even know this, but packaging an app into a single downloadable JS file (modules not even necessary) is actually the modern approach. It lends itself to better compression+minification.
(ES6) modules actually quite necessary if you want your minifier/bundler to be able to remove unused code reliably. Closure Compiler can only do so much.
I think if an SPA can achieve all of these http://rauchg.com/2014/7-principles-of-rich-web-applications...
with minimal additional payload, then it's good.
2) Javascript bloat. This is increasingly important as mobile gradually overtakes desktop, and cheap Android phones proliferate. The more client-side code, the more your performance is limited by your users' own crappy devices.
3) It's not trendy anymore. There was only one truly exemplary, popular SPA, and that was Gmail. Isomorphic/universal is the new hotness, and that requires server-side rendering.
4) The Stack Overflow Effect[1] didn't take. Every client-side framework is so painful that people can't wait to migrate their production apps to something different. So now there are dozens, probably hundreds of frameworks in production, and each community lacks the stability and size to allow newcomers to bootstrap quickly.
5) It's just bad UX. People still manage to get SPAs wrong in 2017. My bank just rolled out a new SPA interface to their suite of consumer budgeting tools, and didn't bother with the History API. Every time I navigate back, it takes me out of the suite entirely and I have to find my way back manually to where I was. I think this problem is a consequence of #1 and #4, though.
-------------------------
[1]: http://svdictionary.com/words/stack-overflow-effect