Overview of the Differences
TKO is a fundamental change in the foundation of the code behind Knockout, representing the reverse engineering, compartmentalization, and reintegration of the original code that dates back to 2010.
With this reengineering of Knockout into discrete pieces, it will be easier to update, build, and test the features of Knockout, as well as put the individual packages that make up Knockout to use in situations outside Knockout itself.
To see the differences, have a look at the CHANGELOG.md.
Version Comparison
Version | tko.js | Knockout 4 | Knockout 3.x |
---|---|---|---|
Browser compatibility | Modern browsers (i.e. Proxy support) |
Internet Explorer 9+ | Internet Explorer 6+ |
ko.BindingHandler |
Yes | Yes | No |
observableArray.length |
Yes | Browsers with Array subclass support More info | No |
iterable observableArray |
Yes | Browsers with Symbol support | No |
event throttle / debounce |
Yes | Yes | No |
ko.Component |
Yes | Yes | No |
applyBindings returns Promise |
Yes | Yes | No |
Polyfills included | None | Up to ES6 | Object.prototype.bind |
ko.proxy |
Yes | No | No |
Build System
TKO has a complete build system rewrite and a conversion of the code from a concatenation of source files to discrete ES6 modules with import
and export
statements. This process has been complex and taken years, with the result being a Knockout with a strong foundation of discrete reusable components.
Knockout 4 and TKO will be maintained at the new monorepo knockout/tko on GitHub. Knockout 3.x will continue to be maintained at knockout/knockout.
TKO now uses standard tools like lerna
and yarn
, and employs StandardJS for code style — though all the old code is not yet updated to StandardJS yet.
Test System
Knockout 3 used Jasmine 1 to perform tests. Some packages in TKO continue to use Jasmine 1, while others now use Mocha.
Backwards compatibility and 💥 breaking changes
We’ve strived to have backwards compatibility, and the numerous test cases we’ve run it through have been successful. A key value of Knockout has been and remains the comprehensive test cases that enforce performance to a predictable standard, and TKO meets all the tests that were enforced on Knockout 3.x, notwithstanding a few issues:
- The
function
no longer works indata-bind
, having been replaced by lambdas, sodata-bind='click: function () { x++ }'
will have to be replaced by e.g.data-bind='click: => x++'
- The
data-bind
is now interpreted by a LL compiler (based on knockout-secure-binding), so some edge case parsing issues may crop up
Browser support
Knockout 4 and TKO and the underlying packages will start support from IE9 forward. We aim to test Knockout across all browsers from this point forward.
If you need IE6, 7, and 8 support you may prefer to keep using the Knockout 3.x line, or be prepared for workarounds or missing functionality.
Options
- (options) Allow importing
ko
in node - various new
options
Utilities
-
(utils) Several array utilities use native functions now (
arrayPushAll
,arrayFilter
,arrayGetDistinctValues
,arrayFirst
,arrayIndexOf
) -
The
throttle
anddebounce
utilities now pass arguments to the target functions -
utils
- utils.domNodeDisposal is now exposed as domNodeDisposal
- arguments to setHtml that are functions are called (not just observables)
- cleanExternalData now exposed in domNodeDisposal.otherNodeCleanerFunctions
-
error handling
- onError no longer throws if overloaded; default function is to re-throw.
- error is thrown when an extender is not found
Life Cycles
- (internal) Add the ES6 LifeCycle class (see tko.lifecycle)
Observables
- (observable) When supported,
observable.length
will now be undefined (was0
before), andobservableArray.length
will now be the length of the wrapped array - (observableArray)
observableArray
is now iterable (has aSymbol.iterator
property) - (subscribable) Add the
once
,then
,when
,yet
, andnext
functions
Components
- (components) Warn with custom-element names that cannot be used with custom elements re. #43 & knockout/knockout#1603
- (components) Add
ko.Component
, an abstract base class that simplifies the Component creation and registration API (seetko.utils.component/src/ComponentABC.js
) - (components) Add
getBindingHandler(key)
to use binding handlers local to a component
Components can control their binding handlers.
ko.Component example
See the Pen Commented ko.Component by Knockout (@Knockout) on CodePen.
Bindings
-
(binding)
ko.applyBindings
now returns a Promise that resolves when bindings are completed -
(binding handlers) Add new-style ES6 Binding Handler class (see custom-bindings documentation and tko.bind/src/BindingHandler.js), descended from the LifeCycle class
-
(bind) String errors on binding are now propagated
Binding Handlers and ko.BindingHandler
In TKO, binding handlers can be either an object
, a function
or a descendant of ko.BindingHandler
. The following are all valid binding handlers:
class NewHandler extends ko.BindingHandler {
constructor ({$element, valueAccessor, allBindings, $context, onError}) {
/*
this.value is valueAccessor()
this.value(x) is valueAccessor(x);
if valueAccessor() is observable or a property, it will be set properly.
*/
}
get controlsDescendants () { return true|false }
get bindingComplete () {
/* overload where binding is asynchronous. See e.g. Component binding*/
}
static get allowVirtualElements () { return true|false }
}
handler_fn = (element, valueAccessor, allBindings, viewModel, bindingContext) => { /* ... */ }
the_knockout_3_way = {
init (element, valueAccessor, allBindings, viewModel, bindingContext) {
/* ... */
},
update (element, valueAccessor, allBindings, viewModel, bindingContext) {
/* ... */
},
allowVirtualElements: true|false
}
// Virtual Elements may be permitted for a binding explicitly.
virtualElements.allowedBindings[handler_name] = true|false
Binding handlers can be registered in the following ways:
// Globally, where NewHandler is a BindingHandler descendant.
NewHandler.registerAs('handler')
// Globally, for any handler type.
ko.bindingHandlers.set(handler_name: handler)
// Knockout 3.x style
ko.bindingHandlers[handler_name] = handler
// For a component
class Component {
getBindingHandler(bindingKey) {
if (bindingKey === handler_name) { return handler }
// Alternatively:
// return { bindingKey[bindingKey]: handler }[bindingKey]
}
}
Parsing
- (parser) Fix interpretation of unicode characters as identifiers / variables
==
and===
use===
for comparison (same for!=
and!==
); fuzzy equality ~== / ~!= for the evil twins- Knockout 4 will work with a Content Security Policy that prohibits unsafe-eval.
- add “naked”
=>
lambdas (even in legacy browsers e.g.data-bind='click: => was_clicked(true)'
- inline functions are no longer supported (e.g.
data-bind='click: function (){...}'
will fail) - No longer uses
with
statements - No longer uses
eval
/new Function
- add “naked”
Namespacing
- incorporate punches namespacing i.e.
data-bind='event.click: => thing(true)'
is equivalent todata-bind='event: {click: => thing(true)}'
Attribute Interpolation
Text Interpolation
- incorporate punches
{{ }}
and{{{}}}
text and attribute interpolation
Event
- (
event
binding) Add object-based event handler e.g.event.click: { handler: fn, once: true, capture: true, bubble: false, passive: false}
. Also, bubbling can be prevented withevent.click: {bubble: false }
re #32 - (
event
binding) Addthrottle
anddebounce
parameters
With
- (with binding) Fix dependency count for function-arguments [knockout/knockout#2285]
Attr
- (attr) Support namespaced attributes with
attr
binding #27
Foreach
- Use tko.binding.foreach for the
foreach
binding (based on brianmhunt/knockout-fast-foreach) - Add
each
as an alias offoreach
- Rewritten as O(1)
- (foreach binding) When using the
as
parameter, the$data
remains unchanged (i.e. the context inside aforeach
is no longer a “child” context, but an extension of the current context); this deprecates thenoContext
parameter - (foreach binding) Expose the
conditional
on thedomData
for use by theelse
binding (when the array is empty, theelse
binding will be rendered) - (foreach binding) Expose
$list
inside the foreach - (foreach binding) Allow
noIndex
as a peer binding parameter (e.g.foreach: items, noIndex: true
) - (foreach) Preserve focus when items are deleted and re-added (i.e. moved) in the same animation frame.
Template
- Make the
template
binding expose a conditional for else-binding - support template literals (``) in bindings (even in legacy browsers)
- add the
@
prefix operator that calls/unwrap functions (i.e.obs()()
is the same as@obs
)
If / Else
- add
<!-- else -->
inside theif
binding, and add anelse
binding (following the brianmhunt/knockout-else plugin) - Add
else
binding - Add
unless
binding
Using
Hidden
- add
hidden
binding (knockout/knockut#2103)
HTML
The HTML binding now works in virtual elements. Based on ko.punches.
Filtering
Filters can be applied to modify values in bindings. Based on ko.punches.
Deprecated
- Template binding options are deprecated
- expressionWriting (twoWayBinding)
- ‘.’ in binding handler names
- jsonExpressionRewriting (expressionRewriting)
- form parsing
bind
shim- ko.utils.parseJson
- getFormFields
- fieldsIncludedWithJsonPost
- postJson