Handling Page Transitions on Single Page Web Apps
There are 2 common approaches to solve this problem:
- Approach 1: Keep all the pages in the DOM tree, use CSS (for example
display) to transit between pages.
- Approach 2: Only render the current page to the DOM tree, when the route changes, we render the corresponding page element to the DOM tree.
That's how we usually manage the routing and page transitions, not to mention that we still want our app to be well-structured and easy to manage the routes when we have more and more pages.
Approach 1: Keep all the pages in the DOM tree.
- The transition you use will be smoother because your pages' DOM are already in the DOM tree, the browser don't have to do very much work (like manipulate and render the whole page). When route changes, it just trigger some changes on CSS classes and your page will be transited nicely.
- When your app grows, the DOM tree will be huge. Imagine your app has 10+ pages then you have to keep all that pages in the DOM tree, the memory for those constant pages' DOM is not a small number.
- Keeping all those DOMs in the DOM tree all the time give a high potential of memory leak and leaving the Garbage Collector to be executed continuously, this will definitely cause lag/jank to your app.
Let's take a look at one of the example app that use this approach: shop.polymer-project.org.
As seen in the profiler, we can see as the app loads up more and more pages, the memory keep increasing and remain there, when we navigate back to the visited pages, Polymer uses that chunk of memory which contains mostly DOMs and just display them.
Let's see the Timeline record for the page transition:
It takes ~17.5ms of scripting to render the new page, which is pretty good.
Approach 2: Only render the current page to the DOM tree
- The DOM tree only contains the current page's DOM no matter how many pages are there in the application, which is a good thing if we consider the memory problem in Approach 1.
Let's take a look at another famous example, React HN built with ReactJS:
In this example, I navigated to several pages and repeat it continuously, and you can see how the app manage the memory from the Profiler report. No matter how many pages are there, it's only the current displaying page get allocated in the memory.
Took ~79.3ms to render the new page, this is the amount of time ReactJS need to render the new page and modifying the DOM tree. As you can see this is the trade off between memory usage and CPU processing, it uses less memory but have to work more to render the page.
Please aware that these are 2 different applications, built with different frameworks/libraries by different person and the applications are quite simple, so don't take the numbers too serious.
The point of this is to show that no matter what framework/library we are using, just make sure we understand the problems when dealing with multiple pages application, and how we can decide when to use what approach or what approach our favorite framework/library is using. That'll give us a better idea why our web application become "laggy" on mobiles or even on Desktop.