Knockout.js and Angular.js are great at many things, but rendering large lists of data is not one of them. Below I show you why, and how to fix it.
A friend of mine was recently trying to use Knockout.js, a mvvm model-binding framework, to create a table with 1000 rows in HTML, and it was failing miserably. Rendering took several seconds on an average PC, during which the browser page was hard locked. A completely unacceptable solution by any standard, and yet completely avoidable.
Here is an example plunkr of what he was trying to accomplish: http://plnkr.co/edit/xWpRm72qz2mV8hxbgOai?p=preview
Let’s look at a couple of things going on in the performance timeline. First thing you’ll notice is on my 2 year old Intel i7, its takes 853ms to render this start to finish, and uses 61MB to do so.
It calls the garbage collector 6 times:
With almost 300ms of that time to render:
Model binding frameworks are great at abstracting out complicated rendering and event wiring, but are terrible at rendering large amounts of HTML. They simply were not built to do this, and trying to make it work is a fool’s errand.
You might be thinking, “But hey, I bet Angular.js is faster!”. Nope, sorry, wrong again: http://plnkr.co/edit/Bkd2jgjHb1F0sagjF5Ke?p=preview. For any model binding framework, a lot of events and handlers need to be wired up for every binding context, with a context being a viewmodel in Knockout or a $scope in Angular. Most pages will typically have a low number of contexts, typically somewhere between 1 and 50, so even though the high amount of overhead per item is high, the actual total time ends up being low. Unfortunately that context wiring scales linearly, so when you have hundreds of contexts on a single page, one for each item in a list, that overhead becomes very apparent.
Use the right tool for the job
This problem has already been asked and solved — years ago. My current favorite tool for solving this problem is SlickGrid. The way it handles rendering is amazing, only actually showing the data you need to see on the screen, and dynamically adding/removing the rows you don’t need to see. A perfect solution.
Here is an example: http://plnkr.co/edit/hGCklkYvhgYq7mnrpRnw?p=preview
It takes 80 ms to render, uses 58MB, and 13ms of that time is rendering:
As you can see, using the right tool for the right job makes all the difference in the world.