Client-side framework selection criteria for progressive decoupling
This document’s goal is to aid the Drupal community in choosing a JavaScript framework best-suited for progressive decoupling, where only parts of the page are rendered through the framework while key integration capabilities with Drupal configuration, modules, and themes are maintained. In short, Drupal-rendered components ought to coexist gracefully with framework-rendered components. Further reading: “Selecting a client-side framework for Drupal” — “Should we decouple Drupal?” — “The future of decoupled Drupal” | Importance legend M (must have) S (should have) N (nice to have) |
Criteria | Most promising | Promising | Least promising | |||||
Criterion (importance) | Why it matters | Angular 2 (MVC, 2.0.0-beta.0) | Ember (MVC, 2.2.0) | React (V, 0.14.3) | Elm (lang, compile-to-JS) | Backbone (MV, 1.2.3; in core) | Angular 1 (MVC, 1.4.8) | Knockout (MVVM, 3.4.0) |
Server-side rendering of templates (M) | Server-side rendering of framework templates is important for SEO and performance. Over time, it allows us to migrate to unified templates across client and server and a server-side JavaScript-based render, whether via in-PHP JS execution or via a thin layer of Node.js. | Built-in | Add fastboot | Built-in | Add rendr | Add -server | Add prerendered | |
Rehydration / seamless state transfer (M) | Does the client-side code discover and reuse HTML rendered during server-side framework execution without incurring an additional rerender? | In progress | Yes (add fluxible) | No (DIY) | No (DIY) | In progress | Yes (add prerendered) | |
Server-side rendering of app itself (M) | Can an app written to run in the client be rendered in the server without code changes? Doing this requires ecosystem-wide coordination around e.g. data-fetching and build tools. | Yes (built-in) | Yes (add fastboot) | No (add Flux impl and DI tool) | Yes (add rendr) | Yes (add -server) | Yes (add prerendered) | |
Small payload size: TodoMVC JS as of 12/29 (M) | Large frameworks incur an initialization cost and deplete mobile batteries faster. Smaller file sizes mean less JS to download, interpret and execute, whether on the client or the server. All frameworks are deeply concerned with this (esp. globally-focused Google and Facebook), so this may be a non-issue in the medium term. | 168KB (min+gzip@level=9); code size is p1 before end of beta | 217KB (min+gzip@level=9) | No data (React needs add-ons) | 37KB (min+gzip@level=9) | 45KB (min+gzip@level-9) | 49KB (min+gzip@level=9) | 25KB (min+gzip@level=9) |
Execution performance: TodoMVC (M) | An ideal framework should execute common application tasks (such as those found in TodoMVC) quickly. Leo Horie (Mithril) benchmarked some of these against a single TodoMVC app, but this relies on outdated releases. Read more about benchmark reliability. | No data | 780ms (1.4.0 with Handlebars 1.3.0) | 308ms (0.10.0) | 266ms (0.16.0) | 204ms (1.1.2) | 344ms (1.2.14) | 131ms (3.1.0) |
Interoperability (M) | Frameworks that encompass the entire page rather than only encompassing portions of the page are less well-suited to a progressively decoupled approach, as Drupal would need to cede all control over renders and would be unable to render parts of the page in PHP/Twig. | Entire page or parts of page | Entire page or parts of page | Entire page or parts of page | Entire page or parts of page | Entire page or parts of page | Entire page or parts of page | Entire page or parts of page |
Template engine friendliness for Drupal themers (M) | A declarative approach could be beneficial to progressively decouple UIs that are still migrating, while string-based templates are ideal for larger page components. How friendly is the templating system for Drupal themers? Does it work well for interpolation into existing Twig files? | Declarative within DOM (ng-) and string templates | Declarative within DOM (data- ember) and Handlebars (string templates) | JSX (string templates); JSX is optional but strongly rec’d | Declarative syntax through elm-html | Choose your own (string templates) | Declarative within DOM (ng- in flat HTML) | Declarative within DOM or string templates |
Code structure unopinionated- ness (M) | A framework’s opinionatedness about application structure means easy optimization, but it may be overly restrictive for an approach that favors “pick and choose” (library) over “the whole nine yards” (framework). | More opinionated (framework approach) | Most opinionated (framework approach) | Less opinionated (library approach) | Not opinionated (language). Suggested project structure. | Less opinionated (library approach) | More opinionated (framework approach) | Less opinionated (library approach) |
Software licensing (M) | Drupal is free software using a GPLv2+ license. An ideal framework would be compatible with this licensing, insofar as it can be distributed singly with Drupal. | BSD (free forks not required) | BSD3 (free forks not required) | |||||
Patent rights (M) | An ideal framework should lack a restrictive patent clause that prevents its use under unrelated conditions. | None | None | None | None | None | None | |
Client-side routing (M) | Client-side routing provides full URL support for SEO, back button functionality, and bookmarking. The lack of this makes navigation less easily introspected and breaks a fundamental aspect of the web. | Built-in | Built-in | react-router (3rd-party library) | Built-in | Built-in | DIY (various 3rd-party libraries) | |
Nestable components (M) | Nestable components are important for elegant decomposition of complex UIs into manageable hierarchies of smaller portable, encapsulated pieces. Also see “Future readiness” below for discussion of Web Components support. | Yes | Yes | Yes | Yes | No | Yes (.component method) | |
Robust state management (M) | The framework’s state system should not trigger a full DOM rerender, which is bad for performance. Instead, it should perform partial rerenders (only those components that have changed). Using DOM diffs off the page instead of model diffs may be a performance bottleneck. | Model diffing (with JIT compilation) | Value diffing (Handle- bars) | DOM diffing (Virtual DOM) | DOM diffing (virtual-dom) | Manual rerendering | Model diffing | View model diffing (with observables) |
Robust REST support (M) | Frameworks either have built-in syntax for REST calls or enforce the use of jQuery (dependency) to fetch data from a service. Some eschew optimistic feedback by saving client-side data only once the server request is sent, not ideal for apps in disconnected environments (mobile or offline-first). | Built-in (in progress, better after beta) | Built-in | Add fetch or isomorphic- | Through elm-http (maintained by author of Elm). | Built-in but syncs with server (no optimistic feedback; no offline; overridable) | Built-in ($http for broad AJAX, $resource for RESTful APIs) | Manual AJAX (knockout. mapping or $.ajax) |
Testability (M) | Can we test our code using small, fast unit tests, using standard off-the-shelf tools, without excessive mocking? How well does it work with Drupal testing? | Good (also unexpected-react) | Poor (DIY) | Poor (DIY) | ||||
Data binding (M) | Two-way data binding allows for data updated from either the view or the model to be reflected in the view, but it often has a detrimental impact on performance. One-way data binding allows for a solely unidirectional flow and is usually adequate for most apps. | One-way data binding | Two-way data binding (one-way data binding will be default in 2.6) | One-way data binding (ReactLink for two-way) | One-way | None (getters and setters; DIY) | Declarative two-way data binding | Two-way data binding |
Large community, ecosystem (S) | Corporate sponsorship or a large backing community help maintain a robust framework. A large ecosystem entails extensions, plugins, and other incidental projects that aid developers. | Large (Google) | Large | Large (Facebook) | Small | Medium | Large (Google) | Medium |
Maturity (S) | Has the framework seen substantial adoption from many large enterprises? Also, does it have a long history of effective use in production? | Least mature | Mature | Most mature | Least mature | Most mature | Most mature | Mature |
API docs and learnability (S) | Not only does the framework need to have an accessible learning curve for Drupal developers; front-end developers need to be able to use the framework efficiently and to integrate easily into the Drupal community. | Average (better by end of Q1 2016) | Good | Good | Good | Good | Good | |
Debugging experience (S) | Developers desire a pleasant debugging experience such as a tool that aids not only in isolating errors and warnings but also a comprehensive inspector for the structure and execution of the application. | chromeextensions-knockoutjs (low usage) | ||||||
Error handling and reporting (S) | Developers require robust error reporting (e.g. compiler errors, runtime errors) to aid their debugging process. An ideal framework would provide exhaustive and helpful error reporting that minimizes blocked tasks. | TypeScript (statically typed): both compile-time and runtime errors | Built-in .onerror method (also -cli-honeybadger) | DIY (3rd-party libraries) | Strongly and statically typed, excellent compile-time errors, practically no runtime errors | DIY (no error handling OOTB) | Built-in .onError method | |
Native app support (N) | Frameworks increasingly have as part of their ecosystem the capability of compiling single-page JavaScript applications into native mobile applications written in Java and Objective-C. While this does not affect progressive decoupling (unless there is server-side JS), it is useful for full decoupling. | -cli-cordova (HTML5 to native) | None (DIY) | None (DIY) | ||||
Future readiness (N) | An ideal framework should have a plan in place to either provide a polyfill for or directly support Web Components, Shadow DOM, and upcoming versions of JavaScript (ES6, ES7). | Excellent (ES6 support, WC-like syntax) | Excellent (ES6 support, WC-like syntax) | Excellent | Poor | Poor | Good (WC-like syntax) | |
Backwards compatibility (N) | Ideally, a framework should be backwards-compatible with all previous versions in order to avoid incurring significant development costs later. In addition, an ideal framework should be using semantic versioning (semver). | First version is current; full semver | Fully backwards compatible; full semver | No backwards compatibility (0.x.x); full semver | No backwards compatibility (0.x.x); full semver | Full backwards compatible; full semver | No backwards compatibility; semver only recently | Full backwards compatibility; full semver |
Release cadence (N) | Less frequent releases will alleviate the need for modules like jQuery Upgrader, which addressed the inability to retain easy dependency management in core. However, faster releases that are backwards-compatible are good for the framework; it simply creates friction in Drupal’s management of dependencies. | First version in beta 2 | Minor every ~6w (long-term support every ~6m) | Minor every ~6m | Minor every ~6m | Minor every ~6-12m | Minor every ~2w | Minor every ~6m |
Totals | 17 green 4 yellow 1 red 3 n/a | 16 green 7 yellow 2 red 0 n/a | 14 green 5 yellow 5 red 1 n/a | 15 green 6 yellow 4 red 0 n/a | 11 green 6 yellow 8 red 0 n/a | 16 green 6 yellow 3 red 0 n/a | 13 green 9 yellow 3 red 0 n/a |
Other considerations for standardization:
Special thanks to the following experts who provided review and input: