Knockout (Anti-)Patterns

knockout!

by @timplourde

Introduction

This resource is inspired by this great talk by Ryan Niemeyer and my own experience in building non-trivial apps with Knockout. It's purpose is to identify common anti-paterns in Knockout-heavy apps and suggest ways to avoid them.

You should have a basic understanding of Knockout before proceeding.

Before we begin it's very important to emphasize the importance of unit testing. If you are not unit-testing your Knockout ViewModels with something like QUnit or Jasmine, just stop reading. Nothing is more vital to the long-term health of a JavaScript app than solid unit test coverage.

1. Separation of Concerns

Knockout is great because it does one thing very well: data binding. After just a few hours learning Knockout, you be building complex apps and impressing your friends in no time.

The key to making sure those fancy apps are sustainable is to stick to the MVVM pattern. Don't worry if you don't have a PhD in MVVM. The main thing you need to remember is the ViewModel should not know about the View (HTML, CSS, DOM). This simple rule will help you build testable and maintainable apps.

1.1 DOM Manipulation in Knockout ViewModels

Once your VM has any knowledge of the DOM then it knows about your View. This breaks MVVM, makes unit testing more cumbersome (because you need to set up / tear down DOM nodes) and yields brittle code. With Knockout binding handlers there is really no reason to have any DOM-related code in your ViewModels.

1.2: (Non-semantic) CSS in ViewModels

This is contraversial since it appears in the Knockout documentation. Avoid generating CSS class names from ViewModels if those class names have no semantic meaning and only exist for visual styling.

1.3: calling ko.applyBindings() inside ViewModel code

ko.applyBindings() binds the ViewModel to the DOM. You shouldn't call it from inside a ViewModel constructor function since it directly affects the DOM and should really only be run once if all your HTML is available at startup time.

2. Going Overboard

Be prudent in your use of knockout's powerful observables. Too many subscriptions can lead to performance issues which can be tough to refactor in large code bases.

These may be more performance optimizations than patterns but if it results in better code then mission accomolished!

2.1: Making everything observable

Every observable has a cost. If you're dealing with large objects or collections, make sure you are only observing things that will change.

2.2: Using subscriptions for input sanitization

Subscribing to changes, intercepting the value and setting the value again triggers unnecessary dependency tracking. Use extenders instead.

2.3: Too much data-binding in large collections

Be careful not to bind DOM events for every element in an array if it's not necessary.

2.4: Over-evaluating computed's

Throttle computed's for easy performance wins.

3. Brittle HTML

HTML can't (practically) be unit tested, so reduce the amount of logic in the HTML as much as possible.

3.1: Inline functions in data-bind attributes

Move logic into ViewModels so it can be unit-tested.

3.2: Too much logic in HTML comments

This happens as application scope grows over time. Try to minimize the ammount of ko: if statements in your HTML.

3.3: Copy-pasted complex data-bindings

Building rich "controls" can result in a lot of repeated code if you're not careful.