Classic Web Site vs. Single-page Application (Part II: Performance Analysis)

I created two identical web applications: Long Live RSS! – Classic Web Edition (CWE) and Long Live RSS! – SPA Edition (SPAE). Both are virtually identical applications, except for the client-side implementation. CWE delivers the page with a simple HTML and CSS combo. SPAE employs Angular to present the application as a single-page application.

In this article, I’m comparing the performance of the two applications as observed from a user’s end.

Note: By the time you’re reading this article, I may have updated the applications and the result of the runs may have been affected.

For both applications, I did the following series of actions as my test cases.

  1. Go to the homepage (https://longliverss.com/classic or https://longliverss.com/spa)
  2. Go to Login page
  3. Enter a valid login info and click “Login” button
  4. Click the feed “Digital Photography School”
  5. Click the first article
  6. Click “Next Article”
  7. Click “Next Article”
  8. Click “Keep unread”
  9. Click feed “UX Movement”
  10. Click the first article
  11. Click “Save to Favorites”
  12. Click “Next Article”

The following are measurement readings  (the average of five runs, rounded, for each application) from Chrome’s Developer Tools,  taken after each aforementioned action.

CWE SPAE
Action # Response Size (kb) Response Time (ms) Response Size (kb) Response Time (ms)
1 328 476 739 2,608
2 6 121 0 0
3 86 356 60 236
4 17 78 7 61
5 1,120 6,538 1,113 6,166
6 596 3,917 589 3,916
7 1,049 3,780 1,043 3,877
8 15 221 5 159
9 10 63 2 61
10 33 1,370 26 1,317
11 14 152 0 98
12 23 408 17 502
Table 1. Response size and download time for CWE and SPAE

Action 1 is the first time the application is accessed. SPAE reports response size that’s at least 2X, and response time 5X as that of CWE. (Note that responses may be delivered in parallel.)

Requests generated by SPAE on Step 1

Requests generated by SPAE for Action #1

For the rest of Actions (except Action #7 and #12), SPAE outperforms CWE. Cumulatively, though, CWE outperforms SPAE by a narrow margin.

CWE SPAE Difference (%)
Requests Count 96 70 -27.08
Cached Responses 32 1 -96.88
Response Size (kb) 3,297 3,601 9.22
Response Time (ms) 17,480 18,999 8.69
Table 2. Cumulative response size and download time for CWE and SPAE

Table 2 indicates that, cumulatively, SPAE has lower number of requests. CEW has a higher number but browser cache prevents CWE from hitting the server for every single request. When considering cached responses, CWE hits the server only 64 times–less frequent than SPAE (69 times). Table 2 also shows that CWE transfers slightly fewer bytes and in a slightly shorter time.

The following are some details about the test runs.

  • The two applications are hosted at a2hosting.com.
  • The two applications shared one instance of database.
  • I did the following before each run
    • Refresh data
    • Warm up the server
    • Clear browser cache
    • Open a new instant of Chrome’s incognito window
  • Content displayed came from my database, except images in the articles
  • Images in the articles are hosted by the original web sites.

Classic Web Site vs. Single-page Application (Part I: Development Cost)

Developing a web application using a framework such as Angular or React has been the norm nowadays. When you use one of those aforementioned frameworks, you typically create a so-called single-page application (SPA).

Typically, developing a SPA is more expensive because it involves more client-side development (using JavaScript or TypeScript) and still yet similar amount of development on server side. In this article, I’m looking closer at the cost of developing a SPA vs a classic web site.

I created two web applications: LongLiveRss! – Classic Web Edition (CWE) and LongLiveRss! – SPA Edition (SPAE). Both applications are of the same functionality: a feed reader with identical UI and features such as reading feeds, saving an article to your favorites, etc. The two applications differ mainly in the client side implementation. One utilizes SPA technology (Angular), another is a classic web site rendered with HTML and CSS (no JavaScript).

Long Live RSS! - SPA Edition

Long Live RSS! – SPA Edition

The following is the technology stack employed to build the two applications.

CWE SPAE
ASP.NET Core MVC 2 ASP.NET Core API 2
MS SQL Server 2017 MS SQL Server 2017
Bootstrap 4 Bootstrap 4
Angular 7

The two applications share (either the code or the instance) several libraries: Services and Repositories libraries. Both also share the same instance of database. Diagram 1 shows how the data flows from the database to the view rendered by a browser. The components with grey shading indicate those with shared instance/code.

Diagram 1. Data flow in a classic web site vs. single-page application

Diagram 1. Data flow in CWE vs. SPAE

The cost to develop an ASP.NET MVC Controller is similar to that of an ASP.NET API Controller. A lot of code (such as ViewModel definition) can potentially be shared between the two. A Razor View looks a lot different than an Angular View, but the complexity is about the same.

The diagram shows that both applications employ similar components except that SPAE includes a component which has no counterpart in a classic web site: an Angular Component. The extra cost of developing a single-page application lies on this part.

Two Applications in One

I own a 1,017-page Pro ASP.NET Core MVC 2 book. I also own a 788-page Pro Angular book. A hasty estimation suggests that building a SPA costs 177% more than building a classic web site. More accurate estimate will require analyzing the complexity of server- and client-side code. Angular packs a lot of features that you can’t master overnight. It’s an opinionated framework which also requires somewhat advanced understanding of software design.

When you build an Angular-based SPA, you will write a lot of Angular code, so much that it feels like developing two applications. The Angular code is a big part of the application. Angular framework usage in a web application is rarely a (progressive) enhancement to a classic web site unlike the usage of jQuery in recent past.  When a browser doesn’t support JavaScript, a SPA will become unusable, while a classic web site with jQuery will (should) degrade gracefully.

Do Everything (Not Really, but Many Things) Twice

In SPAE, many application infrastructures implemented on the server are done as well on the client side. Some examples are routing, authentication, and error handling.

Routing

When a visitor visits, say, http://example.com/favorites, you need to program the Angular routing module to route that request to the correct Angular Component. And when an Angular service sends a request to the server at http://example.com/api/favorites, you need to map an ASP.NET API method to handle that request.

Authentication

Your Angular code needs to determine if a visitor has access to a certain view and handle it accordingly. Similarly, certain methods of your ASP.NET API needs to be protected from an unauthorized access.

Error handling

Inevitably you will write some sort of global error handler for your ASP.NET API. The Angular code will also need to handle error returned by the ASP.NET API or raised by some other Angular code.

Extra Work With Angular

Handling the back button used to be a lot of effort in a SPA. Nowadays, Angular handles that for you behind the scene. Plenty of things, though, come free with a classic web site, but still needs to be (re)implemented in a SPA.

Loading Progress Indicator

When a SPA sends a request to the server, there is no progress indicator shown by the browser while waiting for the response. You have to write your own implementation or incorporate many available packages out there.

Data Consistency

SPAE consists of different areas on the view. When data is updated in one subview, you have to program certain routine so that different subviews which reference the same data reflect the latest value.

Client-side Code Payload Size

When accessing SPAE, the browser has to download a lot more client-side code. (I analyze more of this in part 2.) Hence, optimizing the size of all those static JavaScript files is a complexity that a classic web site doesn’t have to deal with.

Angular vs. React vs. Vue (Part VI: Payload Size)

I took the three “To Do” apps built with Angular (version 6.0.3), React (version 16.4.1), and Vue (version 2.5.16), applied some form validation, and generated a production build for each app. In this article, I’m comparing the build size and the payload size of each app.

Framework Files Generated Number of Requests Payload Size (KB)
Angular 7 files (290 KB) 5 284
React 9 files (1.98 MB) 4 406
Vue 8 files (962 KB) 4 180
Table 1. Build artifacts and payload summary

Files Generated are the files produced by the build process and the ones to be deployed to the production server. React and Vue have a lot larger total file size because the build process of the two frameworks produce a .map file for every minified JavaScript file and/or CSS file.

Once the app is deployed to a production server, accessing the app on its URL triggers requests to several static resources (JavaScript, CSS file, etc); these requests are Number of Requests on Table 1 above, excluding requests to remote API.

The Payload Size is the total size of all those static resources. JavaScript files (the framework and application code) makes up the most of payload. The React app has the most payload, and Vue app the least.

Table 2. Build artifacts
Framework Filename Size (KB)
Angular 3rdpartylicenses.txt 2.12
favicon.ico 5.30
index.html 0.61
main.3584cb656b4d762e14c3.js 222.00
polyfills.2f4a59095805af02bd79.js 58.10
runtime.a66f828dca56eeb90e02.js 1.02
styles.34c57ab7888ec1573f9c.css 0.02
React asset-manifest.json 0.20
favicon.ico 3.77
index.html 0.64
main.65027555.css 0.09
main.65027555.css.map 0.28
main.8a73369a.js 400.00
main.8a73369a.js.map 1580.00
manifest.json 0.32
service-worker.js 3.18
Vue app.c9980c65.css 0.02
app.c4cbf4c9.js 4.15
app.c4cbf4c9.js.map 19.30
chunk-vendors.d8813c49.js 173.00
chunk-vendors.d8813c49.js.map 762.00
favicon.ico 1.12
index.html 0.87