Few years ago I built a shopping site MPA this way and the page transitions were almost not noticable.
Stale-while-revalidate: see https://web.dev/articles/stale-while-revalidate & https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ca...
Immutable: https://datatracker.ietf.org/doc/html/rfc8246 & https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ca...
And if using a CDN, `s-maxage` (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ca...) is quite useful. Set it to a long time, and purge the CDN cache on deploy.
Immutable
is what you need. Crazy enough, Chrome has not implemented it. Bug open since 2016: https://issues.chromium.org/issues/41253661------------------
EDIT: appears that Chrome suddenly had decided in 2017 to not validate at all on reload anymore, after Facebook had complained to Chrome devs about Chrome being more a drag on their servers compared to other browsers.
Quoting https://engineering.fb.com/2017/01/26/web/this-browser-tweak...:
> We began to discuss changing the behavior of the reload button with the Chrome team. […] we proposed a compromise where resources with a long max-age would never get revalidated, but that for resources with a shorter max-age the old behavior would apply. The Chrome team thought about this and decided to apply the change for all cached resources, not just the long-lived ones.
> Firefox was quick in implementing the cache-control: immutable change and rolled it out just around when Chrome was fully launching their final fixes to reload.
> Chrome and Firefox’s measures have effectively eliminated revalidation requests to us from modern version of those browsers.
;-)
> There are only two hard things in computer science: Cache invalidation, naming things and off-by-one errors
I would say that concurrency is a caching issue once proper locking has been set.
Control over Service Workers cache lifetime is more explicit
I’d still specify ‘good’ cache lifetimes though
If one of the most popular sites on the web couldn’t keep a resource in cache for long then most other sites have no hope, and that’s before we consider that more people are on mobile these days and so have smaller browser caches than on desktop
# Cache static files
location ~* \.(ico|css|js|gif|jpe?g|png|svg|woff|woff2)$ {
expires 365d;
add_header Pragma public;
add_header Cache-Control "public";
proxy_pass http://127.0.0.1:9000;
}
Service Workers offer more controls over cache lifetime, and in Chromium (at least) JS gets stored in an intermediate form (which reduces the re-compile overhead on reuse)
—— Original comment:
With browser cache, the browser stills need to send a HEAD request to see if the content was modified. These requests are noticeable when networks are spotty (train, weak mobile signals…)
Service Worker could cache the request and skip the head requests all together
I've wondered if going back to that paradigm would be more productive or not than using React et al.
Plenty of big sites like Amazon or Steam still are made this way. Not exactly PHP + jQuery but rendering HTML on the server and sprinkling some JS on top of it.
Has anyone gone back to working like that?
There are some pages that require more local state. Alpine.js has been great. It’s like Vuejs that you can initialize on your page and doesn’t require a build step.
I had to ask myself if it was worth the hassle to update to 3.x and risk the same thing happening again. The answer was no.
The new stack is Django (which the backend was already written in). Will it stop receiving updates? Extremely unlikely, conserving they have been preserving upgrade paths for the last 20 years and has a solid foundation supporting it.
The supporting ui libraries like htmx and alpine could conceivably become abandoned. The big difference is that they can be vendored easily.
I checked the vue project and it has 1500 transitive dependencies. The new “stack” has a whopping total of 7.
On top of that there is no build step to maintain. Also it’s straight up way faster.
i am hoping my oldest son gets interested in computer programming and can take over as the lead eventually
Same can’t be said for the vue app and its 1500 dependencies + web pack build chain. At least not as easily.
Sacrificing the first born as god intended.
I came across htmx a while back and have kept it in the back of my mind as something to potentially use if I ever had to build something. I'm glad this article came up on HN, and your comment here... makes me really want to build something with htmx!
You will need a Latin motto on your coat of arms. Something like "Simplex sigillum veri".
I algo got burned with Vue 2 at one point. Pretty amazing considering Vue is quite popular. Recently went over 1M daily downloads.
https://npm-stat.com/charts.html?package=vue
I don't think even jQuery had so many downloads back in its heyday but it has maintained its API and methodology after all these years.
Even today's LLM-assisted programming doesn't give me that fluidity. If I use LLMs to assist me in writing a big framework, it'll autocomplete stuff I haven't learned yet, so I need to retroactively research it to understand it.
JS soup on a webpage is a mess, but it's all using the same tools that I know well, and that to me is productive.
They're all Django applications, and the limited dynamic elements are just simple jquery. We have some bootstrap stuff and elements like form elements in javascript, but that's about it.
We are extremely productive, especially compared to our official apps which follow the .NET/Angular stack, that run into all kinds of API versioning issues and errors, it's not even a faster user experience. The problem with such a stack is that you need a few highly skilled architects/system designers. We just have regular programmers piecing it all together, most of them learned these frameworks on the job and come from a regular app dev background, not web.
Granted, we only serve something like 20-30 concurrent users for each of tthe Django apps (as in, page requests/second), but still...
I used to assume React solved genuine problems for FB but given ways in which the UX+performance has gotten worse, I’m not even sure about that.
Meanwhile html plus progressive enhancement works fine for the majority of projects I’ve been involved in. Componentization can still be a win but there’s other ways to do it.
Working with react has a higher barrier of entry (something which is becoming less true over time given that many templates etc exist for it), but if you want to be producing pages and functionality, a good react dev will run leagues around even an a exceptionally good jquery dev.
I’m solid at both and we are talking a dev cycle that is magnitudes faster once you are set up reasonably well. There’s a reason it’s popular. Typescript makes a lot of testing unnecessary and gives many guarantees on robustness. React makes it a breeze to create complex state full components. Nextjs makes it super easy to deploy all this to a web app and quickly create new pages.
The maintenance demands for using a heavy, evolving framework and a large graph of churning dependencies are tremendous and perpetual. While you can make meaningful wins on delivery time for a new feature, once "you are set up reasonably well", you've impliclty introduced new costs that don't apply to "vanilla" code that stays closer to established standards and only includes code it actually uses.
That's not to argue that vanilla is strictly better (both have a place), but just to acknowledge a latent but significant tradeoff that your comment seems to ignore.
Counterpoint: If you replace a frontend framework with backend components, as has been suggested a few times in this thread, those frameworks are even larger, and a lot of the basic functionality provided by React (such as components) is often provided by third-party packages.
Sure, if you're using something like jQuery, then it's smaller and more stable, but the functionality is limited compared to both backend and frontend frameworks. Which of course might be 100% appropriate depending on the use case.
Absolutely. This the reason I'm moving on from JS in the backend. You're constantly stitching up dependencies which might be abandoned and/or become incompatible at any moment.
It probably depends on the use case, no?
If you only need links, forms, and data tables what advantage does React have over SSR + jQuery?
React makes all of that easy to compose. I tell it how to render my state, and I write code that updates state.
Also possible without react with far less complexity.
If you only need links, forms, and data tables
Is putting in a couple of lines of JS that has no knowledge of the backend-managed good enough for small one-off pages? Maybe. But it's definitely not composable and abstractable, which was the original claim.
Or if the forms have to sync with the backend then there are other React-less options, for example:
Some forms are simple enough that you don't need composition and abstraction, yes, no-one's denying that.
> Or if the forms have to sync with the backend then there are other React-less options, for example:
> https://htmx.org/examples/value-select/
Ok, now try composing that. What happens when you want to nest one of those inside another (i.e. have 3 levels of selection)?
Here's the most dynamic form I can think of: https://www.globalgolf.com/golf-clubs/1064148-titleist-tsr2-...
Any input change triggers any/all other inputs to change.
Looks like they send the entire page each action, no React, yet the user experience is much better than the typical competitor site where they've overengineered the whole front end.
Their faceted search is nice too: https://www.globalgolf.com/golf-clubs/used/titleist/fairway-...
Notice how when you add a filter it updates the URL. Breath of fresh air.
And this is way beyond what I had in mind when I read links, forms, and data tables.
Welp guess I'll have to take my business elsewhere. Thanks for the heads up.
Now I'm off to find a golf site that uses React for guaranteed perfection.
This stubbornly stupid mindset is why we have wars.
Edit: I checked the site myself and was able to reproduce the issue mentioned in GP. This is a terrible website.
Reusable components that can be tested in isolation. Type support. That leads to easier evolution and refactoring.
With good architecture you can go mobile with react-native.
Laravel, Dotnet, Rails, etc.
See Laravel, Dotnet, Rails, etc.
The backend is an API for the contact forms and the feedback collection forms.
I was referring to the store. Just checked it and there's tons of jQuery and vanilla stuff (at least on the homepage).
A shopping site or similar, then sure - the "one thing at a time " workflow works.
For internal line of business apps? I'm not sure sure anymore. From a comment of mine a few days ago: https://news.ycombinator.com/item?id=42148627
> Which is a pity; I was watching a client do some crud work in a webapp.
> Web - 1 form per page:
> Click "back". Copy some text. Click forward. Paste it. Repeat for 3 different fields. Click submit.
> Native apps (VB/Delphi/etc) used to be:
> Open both forms. Copy and paste from one to the other. Open another one on the side to lookup some information, etc.
> Webapps, even moreso with MPA, force a wizard-style interface - you only go forward through the form. This is not how people used to work; they would frequently have multiple forms in the same app open at the same time.
> With SPA and html "windows" made out of movable divs you can probably support multiple forms open at the same time, but who does that?
But the folks using the company's line-of-business apps mostly aren't even aware that the browser can open a particular part of the app in a new tab, and even when they are, they aren't aware that the tabs can be torn off into separate windows, and of those that do, there are still some who wouldn't figure out that both browser windows can be tiled side-by-side.
And even when you pass all those hurdles, it's still disruptive enough to the normal workflow that most people who can do that won't do it at all anyway.
You can potentially use http2 push to send down files from the server early but I've seen the browser drop the files if they're unneeded for that page load.
Yes, there are other hacks you could do to make the browser download the resources early, like invisible images or preload audio files, but if you're going down that path why not put in a service worker instead and have it download in the background.
I think some pages still use them for running background stuff. My browser is setup to clear all of them upon closing the tab.
This whole direction is being silently discontinued anyway. Running browser apps has become harder, not easier.
I'm outta the loop, can you expand on how this is the case?
So, there is some piece of infrastructure for this future here and there. Service Workers is one of those pieces. But the apps only achieved some success in closed markets (extension stores). It never became a standard (visit a page, pin it, becomes a fully fledged app).
Instead, the web moved to mobile and desktop apps through other means (super-Cordoba/Electron-like apps, little JS/HTML insertions in traditional apps, other inventive web ways that do not involve a collaborative standard).
The leftovers of this imagined distribution mechanism are being pushed aside (hidden in weird menus or options). Tech is still there because it is a standard, but the counterpoint UI and market decisions are pointing in other directions.
For example, both in Chrome and Firefox, the ability to invoke the browser "chromeless" that was a part of this whole thing has been removed or muted in some way. It was never a standard, so it was removed as soon as possible (probably few people working on it).
Does that make sense?
I imagine ideally we want user choice of where the computation is happening. if on a mobile device I'd save battery in exchange for network latency
but in a desktop computer I'd rather do as most local computation as I can
Omission of trailing part changes the meaning and makes it clickbaity.
You obviously shouldn't build interactive pure-HTML apps, but that’s a talk for another day ;)
This is a distinction between computation that is performed on a server, using arbitrary tools, and computation run on a user's machine locally. The former is much more flexible, but comes at a cost of latency; all user input and output must be serialized, and transmitted a long distance. Front end code can result in much lower latency to the user due to ommitting this transmission.
It makes sense to apply a suitable mix of these two tools that varies based on an application's details. For example, an immediate UI response that can be performed using only information already present on the client makes sense to be handled exclusively there, as computation time is minimal compared to transmission and serializtion.
I believe that JS being able to be used on a server is not relevant to the core distinction.
A lot of the trouble is how that data is "initialized". State management and state ownership are old complications in every client/server application back to the dawn of time. In the old "Progressive Enhancement" days all of the state was owned by the HTML DOM and JS would pick that up and reflect that. In a lot of the modern SPAs the state is owned by the JS code and the DOM updated to reflect it. The DOM never has a full picture of the "state". So state has to be passed in some other channel (often referred to as "hydration" by data/state as water analogy).
Also, in most of the SPA frameworks, state management is deeply entangled with component management and the internals of how everything is built. It may not be deterministic that the server running the same code gets the same data, produces the same results. It may not be easily statically analyzable which components in the system have which data, which outputs given which data are repeatable enough to ship across the wire.
(I just released a low level server-side rendering tool for Butterfloat, and one of the things that kept it easy to build was that Butterfloat was built with an architecture that more clearly separates static DOM from dynamic updating state bindings.)
So it’s not just easy to take code that runs on the server and run it on the client. Anytime the client needs to do more than one round trip, it would have been faster to render the data completely on the server, html included.
Additionally, with SPA’s there’s a lot of nuance around back/forward handling, page transitions, etc. that make a page based application awkward to turn into a purely client side one.
OData batch, GraphQL, and similar technologies exist to reinvent this wheel:
"What if instead of a hundred tiny calls we made one high-level request that returned a composite formatted response that includes everything the client needs to show the next state."
Also known has... server-side rendered HTML like we did since the 1990s.
You can do it, but you might paint yourself into a corner. For example, your manager might at some point say: please load X, while Y is animating; that will not work if the entire page is reloading. A SPA will give you more control and will also reduce the probability of having to rework entire parts of the code.
You Can't Build Interactive Web Apps Except as Single Page Applications... And Other Myths
This is an essay contributed by Tony Alaribe, based on his talk at BigSkyDevCon this past year, discussing techniques to make a non-SPA based web application feel fast and slick. He mentioned some techniques that were new to me and I thought it was the best talk at the conference.
What we have is the natural way to do things with web stack (the way it's is mean to be used), and the "hacky way" (the way that let us do what we want to do, even when the web stack doesn't support it yet).
SPA is the hacky way today, but before it we had CGI, Java applets, Flash... And the web purists were always very vocal against the hacky way.
But the hacky way is what pushs the envelope of what the natural way can do. Every feature cited in the article that makes an MPA competitive with an SPA today only exists because of SPAs.
I'm on the side of preferencially use the web the way it's meant to use whenever it's possible, but I love to see what can be done when we are a little hacky, and it's awesome to see the web stack adapting to do these things in a less hacky way.
as i already wrote here:
https://news.ycombinator.com/item?id=42165046
i find SPAs a much cleaner way to write web applications.
When choosing a tech stack, normally I’d also look for which one rots the slowest. Writing a SPA will typically mean that in the case of the libraries/frameworks there becoming untenable, at least you have an API that you can use when writing a new client.
I have this PrimeFaces/JSF project at work - it’s unpleasant to work with and feels brittle (especially when you have to update components, in addition to nested table row updates being pretty difficult). I’ve also helped migrate an AngularJS project to Vue, the former was less pleasant to use than the latter but the migration itself was also unpleasant, especially when you wanted to get close to 1:1 the functionality. I like how Angular feels more batteries included than the other options, but I’ve seen some overabstracted codebases. React seems like it has the largest ecosystem but the codebases seem to rot pretty quickly (lots of separate libraries for the typical project, lots of updates, things sometimes break). The likes of Laravel and Rails let you be pretty productive but also have pretty tight coupling to the rest of the codebase, same as with PrimeFaces/JSF. I’ve also seen attempts at putting as much logic in the DB as possible and using the back end as more or less only the view layer, it was blazing fast but debugging was a nightmare.
Honestly, just pick whatever technology you think will make the people working with the project in 5 years the least miserable. For me, often that is something for a SPA, a RESTful web API, some boring back end technology that connects to a relational database (sometimes SQLite, sometimes PostgreSQL, sometimes MariaDB; hopefully not Oracle). Whatever you do, try not to end up in circumstances where you can only run a front end with Node.js 10 or where you can't update your Spring or ASP.NET version due to there being breaking changes in your coupled front end technology.
It's probably all good for something... But I would love to just make my web app out of what the browser itself can do, without a tech stack as high as a skyscraper for me to handle.
I know, this way the ecosystem can develop more rapidly (compared to waiting for improvements in the official web standards), and it's also fun to play with toys, and everyone can raise his/her value by learning more and more of these tools.
On the other hand, the web was imho in a better shape before all that began. From user perspective and from developer perspective.
I could be wrong... I'm not primarily a web developer at all...
I wouldn't change it for the world, but I've been told multiple times I'm very much in the minority.
I made the switch and the community it stronger than ever for vuejs and react
sure, i could do the same thing with a traditional fullstack framework. with discipline i would be able to keep frontend and backend code separate. but i have yet to work on a project where that is the case.
i don't build SPAs because the industry demands it. i build SPAs since before the industry even heard about it. and i build them because they make for a cleaner architecture and give me more flexibility than any fullstack framework would.
WebAssembly is pretty great. Here's an example: https://bandysc.github.io/AvaloniaVisualBasic6/
https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blaz...
There might be some technical advantages you can argue but there’s an undeniable economic advantage to not needing to make a bunch of disparate choices and wire things up in a way that you hope will not bit you in the ass later.
The real gradient is more like:
1) a SPA is needed because it might have to run offline
2) A SPA is extremely beneficial because the state of complex interactive are difficult to maintain on the server
3) A SPA helps because we have complex interactions and want to keep the server stateless
4) A SPA is used because we only have developers who haven't done it any other way and won't be fast enough trying something new
5) A SPA is strictly detrimental and ought not be used because there aren't any complex interactions and the added weight to processing / network traffic / etc overwhelm whatever justification we had.
This is not really novel, newsworthy or even worth yet another rambling blog post.