I've been reading a lot about SPA vs SSR and maybe I understand its real idea or maybe not. I'd really appreciate someone experienced who can tell my if my assumptions are right or mean something.
Observation 1)
SPA - client requests www.example.com, this goes from browser to server. Server returns index.html which has just <div id="app"></div> and the script source for javascript file. Browser again makes another request for that bundled js file, server returns it. After that returned js file kicks in and starts executing. When compiled and done, the page is shown to the user.
SSR - client requests www.example.com from browser to server. Server does all the things, making any api calls or other things, puts everything in html and returns html. if these html has some styles or other js sources, browser will make requests for these too.
What I think - Why is SSR faster? Is it because in the SPA case, it had to download the whole js file of the whole website ? and in the SSR case, only the content of that specific page user is entering gets returned ?
Observation 2)
SPA - if the page got loaded and user clicks on one of other routes, it won't make any request to the server to get the html to show it to the user. all the route's js are already downloaded so no need to make request to the server unless there's an Ajax call for some dynamic data from database.
SSR - this would again make the request to the server to get the html file for the new page.
What I think SPA is faster in this case, even though SPA will still need to make the ajax request for some data. Ajax request for some data seem to be faster than the request to download the newly rendered html which would also need that ajax call on the server.
I know SSR is good for SEO, but i am only interested about performance. What do you think? Everything correct about what I said?
SPA
Load once, navigate freely. Sounds great, in theory at least. The initial load time can be a burden. If you have 6 different screens in your application, each taking .5s to load. Would you rather wait .5s everytime you get to a new one, or the initial 3s ? For most user any long loading time is unacceptable, so it's often better to load things incrementally.
On top of that, you often have the burden of actually running the framework to create pages inside the client browser for most modern JS framework (angular, vuejs, reactjs, etc...). That can create performance issues in some cases.
SSR
Generate everything server side, serve content dynamically. Sounds better for code splitting and letting the user load only what he needs. On top of that you can run your framework on a powerful server rather than the client computer/phone/fridge.
However, as you stated, you need to request more content more often. To avoid that, most modern framework are able to create partial routes, dynamically loading page fragments inside a fixed layout if only part of the page need to update when routing.
But that's not all, introducing
Service worker
Service workers and SSR are working very well together. If you establish a cache first strategy, that means that every time your user goes from page A to B to A again, you'll load applications fragment for A and B only once. You service worker will recognize that you are using the same fragment again and pull it from the user cache rather than making a new network request.
It makes things feel lightning fast.
On top of that you can also preload content. A user if hovering a link ? Start loading the app fragments used in this route. It might only save a few hundred ms, but when you are loading small apps fragments it can feel instantaneous for your user.
What's the catch then ? Well first of all it can actually be worse if you have a static application. Cache exists also for SPA, and if your app is small enough you might not care about the few ms saved during the initial loading time. Even with a larger application, it's mostly dependent on your user base.
But mostly, SSR requires an actual server to run the app. A lot of hosting services, like firebase or aws, allow you to host static files like you could use for a PWA, as well as handle a database easily from the client side, for a really cheap, if not free, cost. SSR will require a server, so you will have an increased running cost as those services.
In regards to your second observation, an SPA doesn't have to download everything up front . Through dynamic import an SPA can download assets on demand when a user switches route. This is an optimization to prevent the overhead of downloading everything up front.
As far as SSR vs SPA, SSR generally gives a better user experience. The server can send ready to be rendered HTML through SSR, so the application is mostly making dispatches and rendering the results with minimal client-side logic. If an SPA is designed with a framework, most assets downloaded will have to be processed prior to rendering. However, an SPA can be designed such that the minimal amount of code is provided on initial load and then all remaining assets are downloaded in the background.
You can do an awful lot with an SPA, but having the ability to offload processing to a dedicated server is almost always a win. However, if you are worried about user bandwidth or if you are developing a mobile application then an SPA might be worth considering. An SSR app can still do well here, but if you can get the assets onto the user device up front then you should have less latency-related issues.
Again everything comes down to design decisions, but from my experience an SSR application is more performant and scalable.
SSR provides the end user with a perceived performance improvement. If you are using SSR and SPA together, then SSR will give the end user something to look at quickly until the SPA is booted and the page is ready to be interacted with.
Related
In the documentation for Next 13, it says like this:
Unlike the pages directory which uses client-side routing, the new router in the app directory uses server-centric routing to align with Server Components and data fetching on the server. With server-centric routing, the client does not have to download a route map and the same request for Server Components can be used to look up routes. This optimization is useful for all applications but has a larger impact on applications with many routes.
Although routing is server-centric, the router uses client-side navigation with the Link Component - resembling the behavior of a Single-Page Application.
Please can someone explain the difference between client-side and server-centric routing in a bit more detail? It seems to me that in both cases components are rendered on the server, the client gets HTML and gets hydrated with JS. I'm not sure what the difference is except how the data is fetched.
In server-rendered components, the entire component gets built on the server and then sent to the client. the server does not send any javascript bundle, any dependency, and client will not parse anything. server will complete and sent it to the browser and the browser will just display it to the user.
That is why when you decide which components should be server-side rendered or client-side rendered components, you need to look at the component to see if the component has some sort of user interactivity, for example, button click. if there is a clickable component, that component should be rendered by the browser because the browser has to know the logic of how to handle the click once the click event occurs. The server will send the client related js code.
Since your component is rendered on the server it will be easier to fetch the data from the database. Because your server will not send any heavy library to the client, (browser has to parse javascript, and parsing always take s time) this increases the performance and initial loads will be significantly faster.
In app directory you can still use client components with use client directive on top of the page, but overall goal should be using as small amount of client components. Our goal is to minimize user wait time.
I'm trying to upgrade an old mobile application written in vanilla Javascript+React hosted on Cordova. This application leverages a simple api on the server-side:
when the app requires a page, sends a request to the server
the server elaborate the request, fetch the resources, and then reply with a complete HTML+JS. The Javascript is a ReactJs view compiled with Gulp/Browserify
the app takes the reply and stores it in a local Sqlite DB, then mount the received code and the view become reactive.
if the user requests a view but has no connectivity, the app search though the Sqlite db wether there is a cached view and uses it instead of requesting a fresh copy from the server.
When developing, the React JSX code is immediately compiled to a vanilla JS so, when in production, the api only needs to merge the vanilla JS with the HTML template. Plus, adding new features and fixing bugs is quite easy, because each user essentially download any updated view, each time he enters in it.
The problems with this approach are:
developing is painful because of the continuous compilation
a "base" part of the application resides on Cordova assets (basically the utilities to fetch from server, caching etc) and fixing this parts needs a new app release or ugly override patches
the caching feature often causes problems to the Sqlite database (which is used also for other stuff on the app); as a result, the DB sometimes corrupts and the user must clear the app data.
we would like to get rid of React
We already used NuxtJs for generating static sites, and it's great, but in this case I cannot leverage the SSG because the app should be almost completely served from the api, so we can keep the easy-feature-and-fix stuff.
I never used NustJs SSR and wonder if could be suitable for my use case, for example, could I spin up a NuxtJs instance on server-side which generates the html output and hydrate a barely empty Javascript client on Cordova? Is there a better way to accomplish this task? Should I use only Vue instead?
Thanks
I am building a website with separated front end and back end via restful api. When I develop the front end UI, I bought a template that uses webpack to generate the bundled production files.
Currently I am using pure javascript to handle the routing. For example, to show different view for logged in users compare to other users, I did the following:
if (window.location.pathname === "/") {
const token = window.localStorage.getItem("token")
// if token exists and it has not expired, show the logged in homepage.
if (token && !jwt_expired(token)) {
window.location.replace("/index-loggedIn.html")
} else {
window.location.replace("/index.html")
}
Here are my questions:
I notice window.location.replace could be pretty slow and it would take half a second to route from index.html to index-loggedIn.html. Is this the right way to do it?
I am planning to deploy the front end static files on Nginx as a web server. I guess this workflow will be like:
User sends a request to the "/" route -> Nginx responds with index.html -> The above javascript code gets ran -> Another request to "/index-loggedIn.html" gets sent to Nginx -> Nginx responds with index-loggedIn.html
Is that what actually will happen? It sounds very inefficient to me since I would like to be able to check user's log in status before serving the home page.
Is there any other frontend server/framework I can use to make the routing easier? Then I can use Nginx simply as a reverse proxy. I checked out Angular but I think it will take too much effort to convert the whole template into Angular's format. Can I use express just for front end routing?
Thanks in advance for your suggestions!
You are asking too many things with too many assumptions. Let us try to uncover them step by step.
Type of Application
Basically, you can build two types of applications: SPA (Single Page Application) or Multiple independent pages application; or the hybrid of two Multi-page SPA application. Typically, we use React/Vue/Angular to build a SPA application.
Routing in SPA application
In a SPA application, the routing is completely done on the client-side. The server plays very little role in it. We use HTML History API to achieve this (This is what you are doing by hand using replace method).
With this setup, you need a UI Server (typically build with Node.js) or a Nginx like Reverse proxy. Your server won't be aware of the routes that client-side code has defined. The server/reverse-proxy needs to do two things. First, when the request is for asset (request ending with .html, .css, .js, .png, etc.), then it should try to serve that asset. When the request is for non asset (request without any file extension https://example.com/users/), then you can safely assume that it is a request that must be handled by the client (your client-side code has defined the routing) and thus you must always and always send index.html page which then will bootstrap your SPA application.
In the initial boot sequence of the SPA, you would read the location and determine if this is a valid route. If this is not a valid route, you can redirect user (client-side only) to some default view or 404 view.
Why you would need to do this? Because initially, your user may just say https://example.com and then navigate to /users route by some action. But, if user does full page refresh after navigation, you back-end server will see the route as https://example.com/users. There you need to handle the fallback route to always serve index.html. In this case, 404 must be handled by using client-side code. All the major UI frameworks provide routing solution. You don't need to do it by hand.
Authentication
Now if you using using index-loggedIn.html for authenticated views and index.html, then from SPA perspective, you have a anti-pattern or code-smell at least. You must have a single index.html view which takes care of both authenticated and un-authenticated views. In routing world, we typically use guards to protect the views from unauthorized access.
What you are doing would currently work but as you mentioned that you see the lag of half seconds before redirect. And, that would happen on every page refresh. But that exactly is the problem with full client-side SPA apps in general. However with proper architecture, you should be able to address this.
Routing in a Multi-page SPA or multiple pages application
If you are building an application like this, then your server needs to be more involved and must handle more routing responsibility. In this case, you should probably use cookie instead of localstorage for managing your token. Browser automatically sends the cookies to the server and your server then can make the decision if it wants to serve index.html or index-loggedIn.html page.
To answer your question:
Is this the right way to do it? - It would work but it is an anti-pattern.
Is that what actually will happen? It sounds very inefficient! - It is not inefficient as long as you are properly loading the views with spinner and doesn't cause awkward UX for the users.
Can I use express just for front end routing? - It is an invalid question. Literally, you cannot use express for front-end routing. But then if you plan to implement UI server using express, then you can use it to define proper routing architecture eventually making it part of your front-end stack.
In the end, note that there are few more consideration that you need to take into account. What happens when user is doing hard refresh when he is on some route that server doesn't recognize? How to properly support bookmarking capability for your users? Also, are you building SPA or traditional server rendered application? If SPA, use client-side framework. If server rendered, then keep all the logic inside Express or equivalent code. If it is hybrid, determine the architectural boundary.
Finally, if this sounds too complex consider using hash-based routing for your SPA. That simplifies many things at the cost of not-so clean URLs.
Sometimes when I create basic web tools, I will start with a nodeJS backend, typically creating an API server with ExpressJS. When certain routes are hit, the server responds by rendering the HTML from EJS using the live state of the connection and then sends it over to the browser.
This app will typically expose a directory for the public static resources and will serve those as well. I imagine this creates a lot of overhead for this form of web app, but I'm not sure.
Other times I will start with an API (which could be the exact same nodeJS structure, with no HTML rendering, just state management and API exposure) and I will build an Angular2 or other HTML web page that will connect to the API, load in information on load, and populate the data in the page.
These pages tend to rely on a lot of AJAX calls and jQuery in order to refresh angular components after a bunch of async callbacks get triggered. In this structure, I'll use a web server like Apache to serve all the files and define the routes, and the JS in the web pages will do the rest.
What are the overall strengths and weaknesses of both? And why should I use one strategy versus the other? Are they both viable and dependent upon scale and use? I imagine horizontal scaling with load balancers could work in both situations.
There is no good or bad approach you could choose. Each of the approaches you described above have some advantages and you need to decide which one suits best to your project.
Some points that you might consider:
Server-side processing
Security - You dont have to expose sensitive information (API tokens, logins etc).
More control - You will have more control over what you do with your resources
"Better" client support - Some clients (IE) do not support same things as the others. Rendering HTML on the server rather than manipulating it on client will give you more support for clients.
It can be simpler to pre-render your resources on server rather than dealing with asynchronous approach on client.
SEO, social sharing etc. - How your server sends resources, thats how bots see them. If you pre-render everything on the server bot will be able to scrape your site, tag it etc. If you do it on the client, it will just see non-processed page. That being said, there are ways to work around that.
Client-side processing
Waiting times. Doing stuff on the client-side will improve your load times. But be careful not to do too many things since JS is single-threaded and heavy stuff will block your UI.
CDN - you can serve static resources (HTML, CSS, JS etc) from CDN which will be much faster than serving them from your server app directly
Testing - It is easy to mock backend server when testing your UI.
Client is a front-end for particular application/device etc. The more logic you put into client, the more code you will have to replicate across different clients. Therefore if you plan to have mobile app, it will be better to have collection of APIs to call rather than including your logic in the client.
Security - Whatever runs on the client can be fully read by the client. No matter how much you minify, compress, encrypt everything a resourceful person will always be able to do whatever he wants with your code
I did not mark pro/con on each point on purpose because it is up to you to decide which it is.
This list could go on and on, I didn't want to think about more points because it is very subjective, and in the end it depends on the developer and the application.
I personally tend to choose "client making ajax requests" approach or blend of both - pre-render something on the server and client takes care of rest. Be careful with the latter though as it will break your automated tests, IDE integration etc. if not implemented correctly.
Last note - You should always do crucial validations on the server. Never rely on data from client.
Suppose you were to build a highly functional single-page client-side application that listens to URL changes in order to navigate around the application.
Suppose then, that when a user (or search engine bot) loads a page by its url, instead of delivering the static javascript file and hits the api as normal, we'd like to precompute everything server-side and delivery the DOM along with the js state.
I am wondering if there are existing tools or techniques for persisting such an execution of state to the client.
I know that I could execute the script in something like phantom JS and output the DOM elements, but then event handlers, controllers and the js memory state would not be attached properly. I could sniff our user agent and only send the precomputed content to bots, but I am afraid google would punish for this, and we also lose the speed benefits of having sent everything precomputed in the first place.
So you want to compile, server-side and send to the client the results of requesting a resource at a specific URL? What is your backend written in?
We have an API running on GAE in Java. Our app is a single-page app, and we use the HTML5 history object so we have to have "real responses" for actual URLs on the front-end.
To handle this we use JSP to pre-cache the data in the page as it's loaded from the server and sent to the client.
On the front end we use Backbone, so we modified Backbone.sync to look for a copy of the data it's looking for locally on the page and if it's not there, only then to request it from the server as an AJAX call.
So, yes, this is pretty much what every site did before you had ajax. The trick is writing your app so that the data can be local in the page (or in localStorage even) and if not only then to request the data. Then make sure your page is "built" on the server end (so we actually populate the data in the HTML elements on the server end so the page doesn't require JS on the client end).
If you go somewhere else the data is dynamic and the page doesn't reload.