2. Agenda
• Why?
• Performance as a feature
• Request the minimal data needed for operation
• Minimize the use of observables
• Bindings
• Batch queries
• Knockout tips
• Style
3. Why?
• Lessons learned from R2
• Raise awareness
• Performance as a feature
• Knowledge sharing
• Writing better code
4. Performance as a feature
• Critical rendering path
• Optimizing the Critical Rendering Path – By Ilya Grigorick (Google)
• Youtube - http://www.youtube.com/watch?v=YV1nKLWoARQ
• Perceived performance vs. Actual performance
http://www.mobify.com/blog/beginners-guide-to-perceived-
performance/
5. Request minimal data needed – Why?
• Improve performance
• Promote perceived performance.
• Reduce the network bandwidth required for the request.
• Reduce server time handling the request.
• Simple requests may help optimizing cache control.
• Give better control on data.
• Control data bindings.
• Memory considerations
• Less data received less memory to consume.
• Reduce GC cycles when loading and removing the view.
6. Request minimal data needed - how
• Only get data for the immediate visible view or operation.
• Try to split large query into smaller queries.
• This will enable a more flexible control on data requests.
• For lists limit the request for the amount of possible visible elements plus 50%.
• This will create overflow that enables live scroll to get the next results.
• For extensive calculations:
• Best if server can do it.
• Try to do a special background request for calculation logic.
• Defer the calculation until after applyBinding (after calling loading.resolve())
and run it in a setTimeout(calc, 0) so it will not block any rendering.
• Consider web worker (threading).
7. Request minimal data needed - how
• Any user click data related should be requested when the
user clicks.
• Use projection wherever possible.
• Projection limits the number of properties retrieved for the entity
request.
• It is equivalent to the select clause in SQL. Represent as $select
parameter in OData querystring.
• In JayData use the map function.
8. Projection usage
• JayData map function defines
the object retrieved in the
results.
• Note that the object in the
results will not be JayData
entity.
9. Request minimal needed data - how
• Example: Goal page
• Ux requirement:
• View mode:
• Show expanded goal hierarchy down to initiative
• Only name, short description, description and status is visible
• Investments are collapsed but their count should be visible.
• Some strategy info is needed for authorization
• Investment allocation aggregated calculation is needed
• On user menu selection
• Edit entity form
• Add entity form
• Delete entity
• Entity details popup
11. Example – Autocomplete solution
• R1 autocomplete (filter box)
• Get everything on load
• Enable filtering on the full list
• Practically it was filter box not an
autocomplete.
• R2 autocomplete
• On load get enough data to create the
overflow and scrollbars.
• As a rule of thumb we can use 150% of
visible items.
• Query server on user typing to filter the list.
• Use live scroll to get next paged data.
12. Minimize the use of observables – why?
• Observables are great way to sync between view and view-model,
However, they have costs.
• Each observable is a function that internally can be related to other
observables by subscriptions.
• This is great when we gain the advantages of data synchronization
but its better to avoid it’s overkill when unnecessary.
13. Minimize the use of observables - how
• Use observables only when necessary.
• 2 way binding:
• Edit modes when user input is needed.
• Updated content:
• Show or hide according to user action.
• Updated counter or calculations.
• etc...
• Any static binding use simple js object.
• Do not use it as variable
• komap is evil
• komap.fromJS (fromJD) is a brut force – it maps whole object with observables
• komap on JD entity clone the JD object and creates observables on each property destroying
the JD behavior.
• The process create a block and produce many GC events. This can be illustrated in next slide…
14. komap is evil – Test case
There are 3 lists of
initiatives include strategy
all get the first 200 items.
The only binded property is
the initiative name
1. Left list use projection
on the initiative name.
2. Middle use no projection
and no komap.
3. Right list do komap.
The width of the flame chart
express the time to do the
rendering of the list after
response.
15. komap alternatives
• There is a JayData module for Knockout that integrates
both libraries we name it JDKO.
• JDKO add behavior to JD toArray function that detects an
observableArray passed as parameter and then add knockout
observable behavior to it, BUT keep JD behavior in tact.
• komap have a mapping options object that you can pass
as an optional parameter to the fromJS function.
• Using this mapping you can define what properties should be
observed, ignored, copy or include.
16. Bindings
• Use on demand binding.
• Defer binding of hidden elements to display time.
• Views that are not visible should be fetched and bind at the time they
needed mainly when user click to show.
• Handle data processing before setting binded observable.
• Example: A list is binded to an observableArray. The list needs to
be processed or even filtered.
• In this case do the filter or process the array and then when it is
ready for display set it on the observableArray to be displayed.
• This will do the rendering process in one pass and not force the
browser to parse html, calculate style layout and paint on each item
pushed into the observableArray.
17. Bindings
• Binded observable register to the change event by default. Therefore:
• Avoid binding change events. Use subscribe or computed observables
instead.
• Bad
• totalValueHasChanged is a function – could be subscribe to totalPlanned, which
includes the validateNum logic as well. Alternatively use the writable computed
observable.
• Good
• totalPeriod is a computed observable.
18. Bindings
• The subscribe function can be done on any event. Just
pass the event name as the third parameter to the
subscribe callback.
• Example: The beforeChange event can be used to get the
old value before the change. See how it is done for attach
detach:
19. Bindings
• One last thing… please write maintainable bindings!
• Bad… bad example: Its only 3 <a> tags
20. Batch queries - why
• Batch query aggregate multiple queries into one and send
it to the server as one query. Server execute all queries
and create one response.
• Reduce the amount of requests and all it’s overhead.
• Reduce the risk of server/db deadlocks.
• Reduce the risk of cache synchronization issues.
21. Batch queries - how
• Currently server supports only POST/PUT queries in
batch queries.
• Batch query protocol allows queries in batch to reference
each other and have dependencies between them.
• Batch query
URL:http://localhost:8080/sp/osvc/Batch/$batch?$format=j
son
• JayData create batch query automatically when context
(db) has more than one entity to change/add.
22. Batch queries - how
• Example for JayData query that creates batch query:
23. Knockout tips
• ko.utils.unwrapObservable(x) – get the value of x
whether it is observable or not. No need to check for
function.
• ko.isObservable(x) – check if x is observable.
• ko.isWriteableObservable(x) – check if x is a writable
observable. Non writable observable can be a read only
computed observable.
• ko.dataFor(element) - returns the data that was available
for binding against the element.
• ko.contextFor(element) - returns the context that was
available for binding against the element.
24. Style
• Avoid using selectors
specific to the view’s DOM
• Favor element, classes and
id’s selectors
• Page view should have a
specific id
25. Performance of Style JS changes
• When changing style in a loop, cache the object style
property before the loop and use cached value.
• Bad example, Good Example
Best Practices
Request the minimal data needed for operation.
De-couple view from view-model.
Minimum binding
Code complexity
Knockout
Use observable only on dynamic parts
komap is evil
Minimize bindings
Use on demand binding – don’t bind all data at once.
Minimize the use of observable
Handle data processing before setting observable (before binding)
Avoid binding change events
Use ko.utils.unwrapObservable
JayData
Projection ($select, map)
Batch queries
Use JD behavior of changedProperties
JDKO
ResetChanges
Style
Avoid view’s DOM specific selectors
Favor element, classes and id’s selectors
Page view should have a specific id
Performance
Performance as a feature
Critical rendering path
Time for first pixel paint
Perceived performance vs. Actual performance
Get only displayed data in init method
Split requests
GC issues
DevTools