REAL WORLD SPAA Knockout Case Study
Cory House @housecor
.com
Learn From My Mistakes
Knowledge Survey
Have you built a SPA?
Know Ember, Angular, Backbone?
Worked with Knockout, Durandal, Require, Fiddler, AJAX?
Here’s the Focus1. App walk-through
2. 10 Lessons Learned
3. Does this even make sense?
What monthly payment would you
like?
App Demo
New App: Two Goals
1. Cross Platform
2. Speed
10Lessons Learned
1Decisions are Hard.And we were wrong. A lot.
Sea of Decisions
but…
LightUnopinionatedLibrary
RichOpinionatedFramework
LightUnopinionatedLibrary
RichOpinionatedFramework
Pick a Language?!
Service Layer
WebAPI
And Pick a Promise Library…
Q RSVP
Bluebird jQuery
And a Testing Framework…
Utility Libraries
Data
Network TransportAJAX
Go 2-way:
WebSocket
AJAX Long-polling
Adobe® Flash® Socket
AJAX multipart streaming
Forever Iframe
JSONP Polling
Or punt:
NoSQL?
2Unobtrusive JSDead?
Inline Unobtrusive
Databinding
1999 2007 2013
Unobtrusive JavaScript Movement: Dead?
1999 Inline
2007 Unobtrusive
Knockout
Angular
2013 Databinding
Databinding Advantages
Discoverability
Clarity
Less Code
Consider the Maintenance Programmer
Physically separating concerns that are logically intertwined without an explicit interface obfuscates rather than aids understanding.
3Bundle & MinifyMaybe not?
Heavy Load
93 JS
19 HTML
5 CSS
11 Images
----------------
132 HTTP requests?!
The Full SPA
93 JS files
53 viewmodels
9 libraries
39 Popup windows
56 HTML files
94 RESTful endpoints
Why Minify and Bundle?
1. Reduce HTTP requests to reduce load on server
2. Speed page load
3. Obfuscate code
Why Not Minify and Bundle?
1. Prod no longer matches dev
2. Risk of introducing obscure bugs
3. Debugging in prod is more complex
4. Complexity (and thus risk) must be justified
How’s Performance? It Depends.
Chrome IE8
Empty cache 6 13
Warm cache 3 3
Load first deal 1 2
Load second deal 1 2
Recalculate payment
1 2
To nearest second
4We’re blind to errors!That’s malpractice.
‘Twas Blind, but Now I See…
5Fast in Chrome?Meaningless.
Old IE, The Laggard
10x slower 5x slower
In memory? No problem.
In a table? Uh oh.
1000 rows…
DOM Weight
Traditional
Duplicate elements rendered
Client-side
Single template
Cross Browser Testing
Many options: http://bit.ly/16cXevo
XP-More
6Unix or Windows Mindset?No right answer.
Our Tech Stack
WebAPIORMLiteMSTest
KnockoutJS
Durandal
RequireJS
KendoUIKnockout.Mapping & Knockout-
KendoToastr
jQuery Q.js QUnit
Our Stack
Why Use Knockoutwith Durandal?
14% of our customers :_(
1. Convention
2. Composition
<div data-bind=“compose: ‘viewmodels/vehicle’></div><!-- ko compose: ‘viewmodels/vehicle’ --><!-- /ko -->
3. Routing
A Torrid Love Affair…
WebAPIORMLiteMSTest
KnockoutJS
Durandal
RequireJS
KendoUIKnockout.Mapping & Knockout-
KendoToastr
jQuery Q.js QUnit
Our Stack
WebAPIORMLiteMSTest
AngularJS
KendoUI
If We Used AngularJS instead…
7TypeScript or TestOr refactor at your own risk
My Awesome JS Refactoring Tool
And a Testing Framework…
What I’m testing:Service endpointsBusiness logicThat the data displays (implied)
8Architect Your JS like C#.DAL, BLL, Presentation…
Create a Client-Side “DAL”
JSON.stringify
Centralized service layer
Single wrapper around $.ajax
View
ViewModel
Service
AJAX service
Keep Your Viewmodels Thin
Create Business objects!
Instantiate them in your viewmodels
A 1,000+ line viewmodel is as smelly as a 1,000 class.
Avoid here.
Where Should I Put the Business Logic?
Prefer Here
9Config Object PatternDynamism belongs in JSON
JavaScript Configuration Object PatternJavaScript belongs in static .js files
Not strings in C#, Java, etc.
1 language per file
Inject dynamism via JSON from the server.
JavaScript Configuration Object Pattern
Config Object Pattern: A JustificationSeparation of concerns
Caching
Minimizes string parsing overhead
Code coloring
Syntax checking
Reusable
Reduced payload
Less abstraction
bitnative.com/2013/10/06/javascript-configuration-object-pattern/
1 Manage DependenciesKeep the global namespace clear.0
RequireJS
Utilizes AMD pattern
Dynamically load JS
Inject dependencies
Watch for circular dependencies
1Stay DRYDon’t repeat viewmodels on server & client1
var firstName = ko.observable(user.firstName);
var middleName = ko.observable(user.middleName);
var lastName = ko.observable(user.lastName);
…
Instead:
var user = ko.mapping.fromJS(user);
How Do I Stay DRY?
Use KO mapping plugin
Inject nullos via the Configuration Object Pattern
Ko.utils.
Does This Even Make Sense?
Why not?
Proprietary business logic
Low interactivity
Slower page load
Page is rarely called
Complex – Too many choices!
Debugging pain Runtime errors Cryptic One mistake and nothing loads
Why?
Responsive
Rich Interactivity
Separation of concerns
Efficient
Simple - Less abstraction Debugging No compile wait
Faster page load
SPA Experience Complete
Cory House
bitnative.com
@housecor
spkr8.com/t/35431