Back in the days, there was no such thing as a front-end framework. You’d click a link — it took you to a new page. The server built the HTML, and the page rendered from blank. The CSS? It’s just a link in the <head>
. Yes, it’s downloaded again each time. Well, not really, because the built-in browser cache was there, but it’s still read and parsed.
Doesn't sound efficient, does it?
And it wasn’t — especially back then.
Because the network was slower, the browsers weren’t that advanced, and even the computers themselves — everything just lagged behind what we have now.
Somewhere around 2005 web devs started to address that by what was called AJAX (which stands for Asynchronous JavaScript and XML). The concept was not as scary as its name, though.
Imagine you’re about to leave a comment on a blog post. In the “classic” setup, that means submitting a form via a POST request (yes, a full reload) and getting redirected back to the initial page with all the HTML rebuilt by the server from scratch. This time, with your new comment.
Inside your server, you’d typically have multiple layers of caching of different pieces of data — or you’d have to go to the database for everything visible on the page. Imagine having to load not just the post and all the comments but also user names and related posts, maybe latest trends and most popular tags, on each user action.🤯
What AJAX proposed was: ask the server just for the dynamic part (the list of comments or, better, just the new comment), then have Javascript swapping the old with the new.
Sounds simple and effective — and it is. Even today, some frameworks like Rails still use this approach, and it’s actively promoted by the folks behind it. See Hotwire.
Many devs feel nostalgic about those days — and for a good reason — the typical server code looked like that:
def comments
@post = Post.find params[:post_id]
@comments = @post.comments.order(:created_at).last(10)
render partial: 'comments/list'
end
<ol id="comments">
<% @comments.each do |comment| %>
<li id="comment_<%= comment.id %>">
<strong><%= comment.author_name %></strong>:
<%= comment.body %>
</li>
<% end %>
</ol>
And the front-end code? None — it was generated by the framework.
Wait… wait… here comes the but:
But this approach also had its downsides.
Like, noticed the comment counter? Right — stuck with the old number.
For those small details you either needed to go an extra mile and do a manual update or restructure your markup so that it's included into the partial response.
The solution could look like this:
$('#comments_counter').text(parseInt($('#comments_counter').text()) + 1);
A tiny line — but multiply that by every little detail — and suddenly the whole thing turns into unmanageable spaghetti.
That's when front-end frameworks started to kick-in, with Backbone, Knockout and Spine landing in 2010. That started a paradigm shift, putting more control into the hands of front-end code.
That was also the moment we started calling websites apps — because that's what they became — self-aware pieces of code running in the browser, intercepting user actions, handling requests to the server and managing UI updates.
Now you'd have a declarative model that knows how to update itself and a template that's fully rendered on the client:
const Comment = Backbone.Model.extend({});
const Comments = Backbone.Collection.extend({
model: Comment,
url: '/comments'
});
const CommentView = Backbone.View.extend({
tagName: 'li',
initialize() {
this.listenTo(this.model, 'change', this.render);
},
template(data) {
return `
<strong>${data.author}</strong>: ${data.body}
`;
},
render() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
comments.on('add', function(comment) {
const view = new CommentView({ model: comment });
$('#comments').append(view.render().el);
});
Sweet, isn't it?
A little boilerplate-ish, but you can always extend()
and bake in render()
and those listenTo()
; once done, throughout the rest of your app just define templates and add some touches here and there.
All the cool stuff you’d expect from some brand new killer framework to replace React — was already sitting there back in 2010.
And should I mention it didn't need a transpiler? It's just a few lines of JS that render normal HTML.
And on the server, life became so much easier. You no longer had to load and assemble everything like related posts or recent trends to build one bulky page, in a single endpoint. Each section on the page has its own endpoint now — thinner functions and happier server guys. 🥳
A fair question is — what made people start looking for a new approach in the first place?
Coming soon in Pt.2