Yesterday, I took a quick look at the performance impact of using directive templates in AngularJS. The impact was small and definitely worthwhile considering the benefits of creating reusable code. Today, however, I wanted to look at another approach to reusing code that does have a significant performance impact: using ngRepeat with ngInclude in AngularJS.
For this experiment, as with yesterday's, we're going to render two different lists that use the same content. In order to keep the code DRY (Do not Repeat Yourself), we're going to define the ngRepeat content as template that gets included using the ngInclude directive. I'm not going to bother showing the "control" experiment - the code from yesterday (you can watch the video); so, let's jump right into the ngRepeat-ngInclude demo:
As you can see, each ngRepeat template also uses the ngInclude directive to reference a common template. Notice that the ngRepeat iteration item is "person" in both cases. We have to do this because the included template has no way to differentiate the calling context (ie, friend vs. enemy).
This page works just as you would expect; however, it takes over a second to render (where as the control takes about 100ms). And, when we look at the Chrome dev-tools timeline, we can see why:
The browser spends over a second just parsing HTML. The reason for this is that the ngInclude directive is recompiling the content of the included template in every single linking phase of the ngRepeat. This means that if there are 100 ngRepeat clones, the ngInclude template gets compiled 100 times. This is a striking difference when compared to the use of a directive template which only compiles the template once.
NOTE: After digging through the AngularJS source code, I believe the directive only compiles the template once by queuing up the linking functions. Then, once the template is available, AngularJS clones and links the previously-compiled node using the queued transclusion functions. Or, as best I can tell - this portion of the AngularJS code is particularly cryptic.
I think the lesson learned here is that if you are using ngInclude to reuse code (such as inside an ngRepeat), it's probably better off inside a "component" directive. This will create a more responsive experience for your users. Of course, if you're using ngInclude to render sections of a page in a non-reusable manner, using ngInclude should be totally fine - ngInclude is awesome, I'm not trying to give it a bad name.
Want to use code from this post? Check out the license.