Brief history of web dev
What we’ve done with Node so far:
Generally the browser is a “thin client”.
It just displays what the server gives it and handles no logic.
Client-side JS used to add some interactivity (modals etc).
The heavy lifting is done server-side.
HTML is the source of truth.
Browsers handle a ton of stuff automatically.
Imagine implementing links using JS…
<a>
and <form>
handle a lot of complexity.
You can write server code in any language.
The majority of servers don’t use Node (PHP, Ruby, Python, Java etc)
Web servers usually run on powerful dedicated hardware.
Client-side code runs on the user’s device
(often an underpowered mobile phone).
Server code is hidden from the user. No access to secrets, DB etc.
Can’t trust any code executed in the browser.
Everything requires a request to the server and back.
Full page loads can make interactions feel slower.
Hard to build dynamic interactive “apps” that compete with native.
Deploying, securing and maintaining a server can be difficult
(and expensive for popular sites)
Historically backend has been a separate skill-set.
It’s expensive/inefficient to have two teams writing two languages.
All the logic runs in the browser.
Only one HTML page requested: index.html
.
After that all templating/routing happens in JS in the browser.
Database access is still on server (for security).
But logic and templating is in the browser.
fetch
JSON data from servers (either 3rd party or your own).
Use that data to render dynamic DOM.
JSON is the source of truth.
Don’t have to reload entire page to remove one element.
fetch
ing some JSON and updating a small section can feel faster.
More dynamic interactions are possible.
E.g. filtering lists, deleting elements, animated transitions are easier.
If you’re using 3rd party APIs then you don’t need your own server.
Can use free static hosts like Netlify for your HTML/CSS/JS files.
Avoids the complexity/expense of managing a server.
You can have the same devs write your frontend/backend in JS.
This may or may not be a good thing 🙃.
Building a non-trivial app in client-side JS is tough.
Managing ongoing “state” as the user browses is complex.
Server rendering can be simpler for sites that aren’t very interactive
You can’t run any language but JS in the browser.
JS isn’t necessarily the best language for some stuff
(e.g. precision currency calculations)
0.1 + 0.2 === 0.30000000000000004; // true
All your code executes on the user’s device.
This is often a £100 Android phone with a CPU from 5 years ago.
JS is slow to parse and execute, especially on old devices.
If all your code executes in the browser you can’t hide API keys.
You also can’t trust any user input.
So you probably need a server to hide secrets/validate submissions.
The page is blank until your JS loads, parses & executes.
JS is the “slowest resource per byte”:
i.e. it takes longer to run 1KB of JS than render 1KB of HTML.
SPAs force every user to download all your app’s code.
There’s an ethical trade-off to consider:
offloading processing from company servers to users’ devices.
Help manage complexity building big apps.
Give everyone a shared structure to work from.
Often handle lower-level DOM updates for you.
A framework provides patterns for structuring code within a team.
Frameworks usually have good google-able docs.
Good frameworks make it easier to build cool stuff.
They can empower newer developers.
Can make it nicer to build complex apps.
They often come with stuff developers want built-in.
Frameworks are code you must load before you own.
They can also encourage bad habits that lead to bloated apps.
E.g. just npm install do-the-thing
React apps usually can’t use libraries written for Angular.
An organisation tends to have to go all in on one technology.
React is built by Facebook for Facebook.
They might add features you don’t need,
or refuse to add features you want.
Instead of telling the browser each step to render an element you describe it
(just like in HTML!)
function Box() {
const div = document.createElement("div");
div.classList.add("box");
div.append("Hello world");
return div;
}
function Box() {
return <div className="box">Hello world</div>;
}
Even event listeners are declarative
return <button onClick={() => console.log("Clicked!")}>Click me</button>;
function DateInput() {
return h("input", { id: "dob", type: "date", placeholder: "dd/mm/yyyy" });
}
vs
function DateInput() {
return <input id="dob" type="date" placeholder="dd/mm/yyyy" />;
}
Dividing your app into pieces is a nice mental model.
You can group markup, styling & behaviour in a re-usable thing.
function App() {
return (
<Form>
<Field>
<Label>Name</Label>
<TextInput />
<Error />
</Field>
<Field>
<Label>Date of birth</Label>
<DateInput />
<Error />
</Field>
</Form>
);
}
No special templating language.
You use JS conditionals, variables and loops to render markup.
const posts = ["blah", "other post", "..."];
return <div>There are {posts.length || 0} blog posts.</div>;
For any given state the rendered DOM should always be the same.
You can just update state and React will keep the UI in sync.
let x = add(1, 2);
If add
is “pure” we can be sure what x
is every time we call it.
We also know nothing else will be affected.
let state = { name: "oli", basket: [...] }
let dom = app(state);
// OR `ui = fn(state)`
We can know exactly what the DOM is for any given app state.
return <button>The count is: {count}</button>;
Instead of:
When this thing happens, find this element and change this property to that value...
we have:
Given the current state, this is what the UI should be
and React just makes it happen.