can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
1,016 lines (742 loc) • 50.1 kB
HTML
<!DOCTYPE html>
<!--####################################################################
THIS IS A GENERATED FILE -- ANY CHANGES MADE WILL BE OVERWRITTEN
INSTEAD CHANGE:
source: [object Object]
@page guides/comparison
######################################################################## -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS - Comparison to Other Frameworks</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="../static/bundles/bit-docs-site/static.css">
<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" href="/docs/images/canjs_favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="../../docs/images/canjs_favicon_57x57.png">
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="../../docs/images/canjs_favicon_57x57.png">
<link rel="apple-touch-icon" sizes="72x72" href="../../docs/images/canjs_favicon_72x72.png">
<link rel="apple-touch-icon" sizes="114x114" href="../../docs/images/canjs_favicon_114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="../../docs/images/canjs_favicon_128x128.png">
<link rel="apple-touch-icon" sizes="144x144" href="../../docs/images/canjs_favicon_144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="../../docs/images/canjs_favicon_152x152.png">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta name="apple-mobile-web-app-status-bar-style" content="white-translucent">
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-2302003-11', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger"/>
<label for="nav-trigger">Menu</label>
<div id="everything">
<div id="left" class="column">
<div class="top-left">
<div class="brand">
<div class="logo">
<a href="../../index.html" alt="CanJS"></a>
<div class="dropdown project-dropdown">
<a href="https://donejs.com/">DoneJS</a>
<a href="http://stealjs.com/">StealJS</a>
<a href="http://jquerypp.com/">jQuery ++</a>
<a href="https://funcunit.com/">FuncUnit</a>
<a href="http://documentjs.com/">DocumentJS</a>
</div>
</div>
<div class="version">
<div class="version-number">
3.0.0
</div>
<div class="dropdown version-dropdown">
<a href="https://v2.canjs.com">2.3.27</a>
</div>
</div>
</div>
<div class="search-bar">
<p>
</p>
</div>
</div>
<div class="bottom-left">
<div class="social-side-container">
<ul class="social-side">
<li>
<a class="header-mobile github" href="https://github.com/canjs/canjs" target="_blank"><img class="social-icon-small" src="../../docs/images/github.png">Github</a>
</li>
<li>
<a class="header-mobile twitter" href="https://twitter.com/canjs" target="_blank"><img class="social-icon-small" src="../../docs/images/twitter.png">Twitter</a>
</li>
</ul>
<ul class="social-side">
<li>
<a class="header-mobile" href="https://gitter.im/canjs/canjs" target="_blank">Chat</a>
</li>
<li>
<a class="header-mobile" href="http://forums.donejs.com/c/canjs" target="_blank">Forum</a>
</li>
</ul>
</div>
<ul>
<li class="
parent
expanded">
<a class="page"
href="../guides.html"
title="Welcome to CanJS! These guides are here to help you develop and improve your relationship with CanJS. After all, picking a JavaScript framework is a commitment. We want CanJS to be the
framework you marry. This page helps you know how advance through the different stages of this
relationship:">
Guides
</a>
<ul>
<li>
<span>introduction</span>
<ul>
<li class="
">
<a class="page"
href="mission.html"
title="Learn about CanJS's mission, why it matters, and how we've worked (and will keep working) to accomplish it.">
Mission
</a>
</li>
<li class="
">
<a class="page"
href="technical.html"
title="">
Technical Highlights
</a>
</li>
<li class="
">
<a class="page"
href="who-uses-canjs.html"
title="">
Who uses CanJS?
</a>
</li>
</ul>
</li>
<li>
<span>experiment</span>
<ul>
<li class="
">
<a class="page"
href="chat.html"
title="This guide walks through building real time chat application with CanJS's Core libraries. It takes about 30 minutes to complete.">
Chat Guide
</a>
</li>
<li class="
">
<a class="page"
href="todomvc.html"
title="This guide walks through building a slightly modified version of TodoMVC with CanJS's Core libraries and can-fixture. It takes about 1 hour to complete.">
TodoMVC Guide
</a>
</li>
<li class="
">
<a class="page"
href="atm.html"
title="This guide walks through building and testing an ATM application with CanJS's
Core libraries. It teaches how to do test driven development (TDD)
and manage complex state. It takes about 2 hours to complete.">
ATM Guide
</a>
</li>
<li class="
">
<a class="page"
href="setup.html"
title="CanJS is packaged in multiple ways so that it can fit into any development workflow. Learn how to setup CanJS in different environments.">
Setting up CanJS
</a>
</li>
</ul>
</li>
<li>
<span>commitment</span>
<ul>
<li class="
">
<a class="page"
href="api.html"
title="This page walks through how to use and understand CanJS's API documentation.">
API Guide
</a>
</li>
<li class="
">
<a class="page"
href="examples.html"
title="">
Examples
</a>
</li>
<li class="
">
<a class="page"
href="../roadmap.html"
title="Learn about CanJS's future plans and how we make them, and how you can influence them.">
Roadmap
</a>
</li>
<li class="
">
<a class="page"
href="../migrate-3.html"
title="">
Migrating to 3.0
</a>
</li>
</ul>
</li>
<li>
<span>contribute</span>
<ul>
<li class="
">
<a class="page"
href="contributing/bug-report.html"
title="Learn how to submit a bug report.">
Bug Report
</a>
</li>
<li class="
">
<a class="page"
href="contributing/code.html"
title="Learn how contribute a code change to CanJS.">
Code
</a>
</li>
<li class="
">
<a class="page"
href="contributing/documentation.html"
title="Learn how to improve CanJS's site and documentation.">
Documentation
</a>
</li>
<li class="
">
<a class="page"
href="contributing/evangelism.html"
title="Learn about resources that can help you spread the word about CanJS.">
Evangelism
</a>
</li>
<li class="
">
<a class="page"
href="contributing/feature-suggestion.html"
title="Learn how to suggest a feature.">
Feature Suggestion
</a>
</li>
<li class="
">
<a class="page"
href="contributing/releases.html"
title="Release and hosting information for CanJS maintainers.">
Releases
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="
">
<a class="page"
href="../can-core.html"
title="The best, most hardened and generally useful libraries in CanJS.">
Core
</a>
</li>
<li class="
">
<a class="page"
href="../can-ecosystem.html"
title="Useful libraries that extend or add important features to the core collection.">
Ecosystem
</a>
</li>
<li class="
">
<a class="page"
href="../can-infrastructure.html"
title="Utility libraries that power the core and ecosystem collection.">
Infrastructure
</a>
</li>
<li class="
">
<a class="page"
href="../can-legacy.html"
title="Former libraries that we still accept patches for, but are not under active development.">
Legacy
</a>
</li>
</ul>
</div>
</div>
<div id="right" class="column">
<div class="top-right">
<div class="top-right-top">
<ul class="top-right-bitovi">
<li class="dropdown">
<a href="http://bitovi.com" class="bitovi icon-bits">Bitovi</a>
<ul class="dropdown-menu">
<li><a href="http://bitovi.com">Bitovi.com</a></li>
<li><a href="http://bitovi.com/blog/">Blog</a></li>
<li><a href="http://bitovi.com/consulting/">Consulting</a></li>
<li><a href="http://bitovi.com/training/">Training</a></li>
<li><a href="http://bitovi.com/open-source/">Open Source</a></li>
</ul>
</li>
</ul>
<div class="brand">
<div class="logo">
<a href="../../index.html" alt="CanJS"></a>
</div>
</div>
<ul class="top-right-links">
<li>
<a href="https://gitter.im/canjs/canjs">Chat</a>
</li>
<li>
<a href="http://forums.donejs.com/c/canjs">Forum</a>
</li>
<li>
<a class="github-button nav-social" href="https://github.com/canjs/canjs" data-count-href="/canjs/canjs/stargazers" data-count-api="/repos/canjs/canjs#stargazers_count">Star</a>
</li>
<li>
<a href="https://twitter.com/canjs" class="twitter-follow-button nav-social" data-show-count="true" data-show-screen-name="false">Follow @canjs</a><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
</li>
</ul>
</div>
<div class="breadcrumb">
<li><a href="../../index.html">CanJS</a></li> /
<li><a href="../guides.html">Guides</a></li> /
<li><a href="comparison.html">Comparison to Other Frameworks</a></li>
<li class="breadcrumb-dropdown">/ <a> On this page</a>
<ul class="on-this-page"></ul>
</li>
<div class="nav-toggle" title="Back to top"></div>
</div>
</div>
<div class="bottom-right">
<article>
<section class="title">
<div class="page-type">
<h1>Comparison to Other Frameworks</h1>
<div>page</div>
</div>
<section class="description">
</section>
</section>
<section class="on-this-page-table">
</section>
<section class="title-footer">
<ul class="title-links">
<!-- <li><a href="#">docco</a></li> -->
<li><a href="//github.com/canjs/canjs/tree/v3.0.0/docs/can-guides/introduction/comparison.md">source</a></li>
<!-- <li><a href="#">download</a></li> -->
<!-- <li><a href="#">tests</a></li> -->
</ul>
</section>
<section class="body">
<p><strong>CanJS</strong> is a full <strong>MVVM</strong> library for creating fantastic web applications, but let’s face it, there are a lot of other frameworks that solve the same problem. These days, most of the <strong>MV</strong>* frameworks and libraries have some mechanism for creating <strong>Custom Elements</strong> for the browser and binding those elements to some sort of <strong>observable state or data-store</strong>, along with features like <strong>routing</strong> and connecting data to a <strong>web service API</strong>. The modern <strong>MV</strong>* frameworks are starting to look more and more similar, as concepts and features are shared between them, and the general needs of developers start to converge.</p>
<p>You should evaluate if a framework fits your mental model of how applications should be built, and to do that you’ll need to know the subtle but important differences that may not seem obvious at first glance.</p>
<p>Two of the most popular frameworks/libraries for building web applications are <strong><a href="#React">React</a></strong> and <strong><a href="#Angular2">Angular 2</a></strong>. So here are some comparisons you should consider when evaluating which framework to use to build your non-trivial application.</p>
<h2>React</h2>
<p><strong>React</strong> is really just the "V in MVC", while <strong>CanJS</strong> is a full <strong>MVVM</strong> library, so it may feel like we’re comparing apples to bananas, but there are certain tradeoffs that can be explored when comparing <strong>CanJS</strong> to <strong>React</strong></p>
<p>Because <strong>React</strong> is only a view layer, there has been a deluge of competing libraries, each trying to establish itself as the de facto state management library for your <strong>React</strong> app. Some of the more popular libraries right now include <strong>Redux</strong>, <strong>MobX</strong>, <strong>Alt</strong>, <strong>Reflux</strong> and, of course, Facebook’s own <strong>Flux</strong> library (an implementation of their Flux architecture). Even frameworks like <strong>Angular 2</strong> and <strong>CanJS</strong> are joining in and making "React friendly" implementations.</p>
<p>But with all these choices and change, who is to say the state management library you pick for your <strong>React</strong> app will be around next year? The year after that? Things are moving so fast around the <strong>React</strong> ecosystem, it’s hard to see what it will look like in the coming years.</p>
<p><strong>CanJS</strong> has a historical track record providing a stable platform in a way <strong>React</strong> cannot, because <strong>React</strong> is too new and has a small focus, inviting an unstable ecosystem of tooling to form around it. Read more about our approach balancing stability and innovation <a href="business-advantages.html">here</a>.</p>
<p>To make a fair comparison, at times we will be referencing and comparing <strong>CanJS</strong> to <strong>React-Redux</strong>, arguably the most popular <strong>Flux</strong> implementation and state management library for <strong>React</strong> at this time</p>
<h3>Observables</h3>
<p><strong>CanJS</strong> has <strong>observable</strong> constructs that are powerful but still very easy to use.</p>
<p><strong>React</strong> has no real equivalent of observable state, and manages state changes through explicit calls to <code>render()</code> or <code>setState()</code>.</p>
<p><strong>React-Redux</strong> has a pattern for updating application state that is synchronous and uses pure functions, but it can be difficult to work with.</p>
<p>The synchronous data flow provided by the <strong>Redux</strong> store is generally insufficient for real web apps. The web is very async by nature, with it's ajax calls, web-workers, web-sockets and all sorts of common APIs resolving asynchronously using events and callbacks. So you find yourself either writing a whole lot of code to manage your asynchronous action creation, or more likely you pull in asynchronous middleware like <strong>redux-thunk</strong>, <strong>redux-promise</strong> or <strong>redux-rx</strong>.</p>
<p>Suddenly, the strict unidirectional data flow, is no longer that easy to follow, and you end up having to write a non-trivial amount of code to support the Redux pattern of dispatch -> action -> reducer -> state.</p>
<pre><code class="language-javascript">const ViewModel = DefineMap.extend({
subreddit: 'string',
posts: {
get(lastSetValue, resolve) {
this.postPromise
.then(response => resolve( response.json() ));
}
},
postsPromise() {
get() {
return fetch(`http://www.reddit.com/r/${this.subreddit}.json`)
}
}
});
</code></pre>
<p>VS</p>
<pre><code class="language-javascript">function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
subreddit
}
}
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
function receivePosts(subreddit, json) {
return {
type: RECEIVE_POSTS,
subreddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
}
// assumes thunkMiddleware is in use
export function fetchPosts(subreddit) {
return function (dispatch) {
dispatch(requestPosts(subreddit))
return fetch(`http://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json =>
dispatch(receivePosts(subreddit, json))
);
}
}
const mapStateToProps = (state) => {
return {
posts: state.posts,
subredit: state.subredit
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onTodoClick: (id) => {
this.props.dispatch(fetchPosts(ownProps.subreddit))
}
}
}
const VisiblePostsList = connect(
mapStateToProps,
mapDispatchToProps
)(PostsList)
export default VisiblePostsList
</code></pre>
<p>The functional aspects of <strong>Redux</strong> do have benefits to simplicity but there is obviously a trade-off, and you have to decide which way you prefer to attack simplifying the asynchronous and stateful demands of client side web UIs.</p>
<p>Using <strong>MobX</strong> observables with <strong>React</strong> is fairly popular, though not as popular as <strong>Redux</strong>, and could just as easily be compared to using <strong>CanJS</strong> observables with <strong>React</strong>, which is very possible and easy to do, because of <strong>CanJS</strong>’ modularity.</p>
<h3>Data Fetching and Real-time Data</h3>
<p><strong><a href="../../can-connect.html">Can-Connect</a></strong> allows for <strong>CanJS</strong> <strong>observables</strong> to be connected to a data source such as an RESTful API or a real-time data stream, with advanced features like fall through caching and batched minimal requests, so that your components can just request the data they need while can-connect figures out how to fetch that data in the most efficient way possible.</p>
<p><strong>React</strong>, being just a view layer, has no concept of this.</p>
<p><strong>Relay</strong>, a JavaScript framework for building data-driven <strong>React</strong> applications, has some very promising features, similar to can-connect, but requires you to implement a GraphQL server while <strong>Can-Connect</strong> can work with whatever data source or API you have.</p>
<p>Being tied directly to one particular type of back end service may end up with <strong>Relay</strong> being slightly easier to use, but <strong>Can-Connect</strong>'s flexibility, and it’s configurable integration points, will be valuable if you have different types of back-end services, like traditional REST APIs or you need to work with unique kinds of data stores.</p>
<h3>One-Way Data Flow</h3>
<p>One-Way data flow is touted as a benefit, that it simplifies the complexity of your UI state management. But if your problem is that state is changing unexpectedly or not changing when you think it should, maybe the solution is is to simplify your structure, not limit your tools.</p>
<p>One-Way data flow may be simpler to follow, but you end up writing a lot more code as a trade off. The reason 2-way binding became popular was that it removed a lot of boilerplate code: binding to a change, updating the state, updating any other dependants with the new state…etc.</p>
<p><strong>CanJS</strong> supports many types of data-bindings in our views, including one-directional, two-way binding, sibling-to-sibling value binding, and binding directly to DOM events. <strong>CanJS</strong> makes it easy to save writing a lot of code and keep your app simple to follow at the same time.</p>
<pre><code class="language-javascript">// Binding to a Todo models completed property
const Todo = DefineMap.extend({
name: 'string',
completed: 'boolean'
});
// In stache
{{#each todos}}
<label><input type="checkbox" {($checked)}="completed" />{{ name }}</label>
{{/each}}
</code></pre>
<p><strong>React</strong> has a dedication to the one-way data flow. This leads to a lot of boilerplate, hooking up event handlers just to change the state, over and over everywhere. <strong>Flux</strong> and <strong>React-Redux</strong> follow this same principle and ends up with even more code: <code>actions</code>, which get sent to a <code>dispatcher</code>, which pass the <code>action</code> to a <code>store</code>, to finally set the <code>state</code>. More code to write, more places for bugs to hide.</p>
<pre><code class="language-javascript">// minimal reducer
const todo = ( state, action ) => {
switch (action.type) {
case 'TOGGLE_TODO':
return Object.assign({}, state, {
completed: !state.completed
});
default:
return state;
}
}
// minimal mapStateToProps and mapDispatchToProps functions for container
const mapStateToProps = ( state ) => {
return {
todos: state.todos
};
};
const mapDispatchToProps = ( dispatch, ownProps ) => {
return {
onChange(id) {
dispatch({
type: 'TOGGLE_TODO',
id
});
}
};
};
const TodoListContainer = connect( mapStateToProps, mapDispatchToProps )( TodoList ) );
// in JSX
Todo = (props) => {
checkboxProps = {
checked: props.completed,
name = props.name,
onChange(event) {
props.onChange(props.name)
}
}
return <label><input type="checkbox" ...checkboxProps />{ props.name }}</label>
}
</code></pre>
<p>If the problem is losing track of what is changing the state, is the problem solved by adding more code, or is it better solved with a simpler abstraction and more succinct code?</p>
<h3>DOM Libraries and Memory Leaks</h3>
<p>Sometimes you are not starting a brand new project, and you'd rather incrementally add your new framework to your existing app, rather than do a whole re-write. Maybe you are using <a href="http://getbootstrap.com/javascript/">Bootstrap</a>, or <a href="https://jquery.com/">jQuery</a> plugins, and you'd rather not re-implement everything you have at once.</p>
<p><strong>CanJS</strong> makes working with other libraries seamless. You can just use <a href="../../can-stache.html">can-stache</a> to add <a href="../../can-components.html">can-components</a> custom elements into your page as needed, and since <strong>CanJS</strong> works with real DOM events and attributes, everything just works as expected.</p>
<p>If you are using <a href="https://jquery.com/">jQuery</a>, <a href="https://plugins.jquery.com/">jQuery plugins</a> or <a href="http://getbootstrap.com/javascript/">Bootstrap</a>, CanJS has a library, <a href="../../can-jquery.html">can-jquery</a>, which was specifically created for apps using CanJS with jQuery, and allows all jQuery events or DOM manipulations to "just work" without any special code needed.</p>
<pre><code class="language-handlebars"><!-- A Bootstrap Modal -->
<div id="my-modal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<script type="text/stache" can-autorender>
<!-- A CanJS user-form component -->
<user-form />
</script>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary save">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<button class="btn btn-primary newuser">New User</button>
<script>
$('.newuser').on('click', ev => { $("#my-modal").modal('show'); });
$("#my-modal").on('show.bs.modal', ev => {
let modal = $(ev.currentTarget);
modal.find('button.save').on('click', ev => {
modal.find("user-form").trigger('submit');
modal.modal('hide');
});
});
</script>
</code></pre>
<p><strong>React</strong> replaces certain core aspects of the DOM, namely the event system and attributes, with it’s own “React version” of events and props. Because of this, <strong>React</strong> can have some frustrating “gotchas” when you try and integrate it with jQuery plugins or any other library that queries or manipulates the DOM.</p>
<p>Using <code>ReactDOM.render()</code> to insert <strong>React</strong> components in to your app, may even cause a <a href="http://www.ibm.com/developerworks/web/library/wa-memleak/">memory leak</a>. To prevent memory leaks when using <strong>ReactDOM</strong> this way, you need to be aware of and use <code>ReactDOM.unmountComponentAtNode()</code> every time a <strong>React</strong> node get's deleted. This is important and often forgotten. Forgetting to call <code>unmountComponentAtNode</code> will cause your app to leak memory, so you'll have to hook up this call, into your current app lifecycle.</p>
<pre><code class="language-handlebars">// DO NOT DO THIS, THIS CAUSES A MEMORY LEAK
<div id="dialog-form" title="Create new user">
<div class="react-user-form-component"></div>
</div>
<button id="create-user">Create new user</button>
<script>
// This will leak memory
ReactDOM.render( $('.react-user-form-component').get(0), UserFormComponent );
dialog = $( "#dialog-form" ).dialog({
autoOpen: false,
modal: true,
buttons: {
"Create an account": dialog.find( "user-form" ).submit(),
Cancel: () => {
dialog.dialog( "close" );
}
}
});
$( "#create-user" ).button().on( "click", function() {
dialog.dialog( "open" );
});
</script>
</code></pre>
<p>Conversely <strong>CanJS</strong> is aware of it's own DOM removal, and will clean up any event handlers or bindings <em>automatically</em>.</p>
<p>If you have existing legacy code, you'll probably want integrate your new framework progressively, a piece at a time, and it's important that things “just work”, without any surprises or frustrations.</p>
<h3>Encapsulation of Components</h3>
<p>There is a trend in JavaScript these days towards small reusable modules, tiny components, and little composable functions. But as with everything in programming there are tradeoffs, and if your modules are all too small, too isolated, the effort of wiring them together to become useful becomes greater than the benefit of reusability.</p>
<p>Encapsulation is one way to simplify your applications. By encapsulating modules and only exposing an api for input and output, your app becomes easier to reason as you can think about it in larger chunks that have their input and output defined. Having well encapsulated modules or components means you don’t have to hold all the details of the implementation in your head, just the parts that matter at the level you are using that module.</p>
<p><strong>React-Redux</strong> has chosen to model their encapsulation in layers.</p>
<ul>
<li><p>There is the <strong>View</strong> layer which holds all the <strong>React</strong> components, action creators and the dispatcher</p></li>
<li><p>There is the <strong>Store</strong> layer which holds all the reducers and redux middleware.</p></li>
</ul>
<p>These layers have a simple and inverse API, the Store layer takes actions and returns state, the View layer takes in state and returns actions (as the user takes action on the UI).</p>
<p><img src="../../docs/can-guides/images/introduction/redux-flow.png" style="width:100%;max-width:750px"/></p>
<p>...and there are layers <em>within those layers</em>, like <strong>action creators</strong> and <strong>redux-middleware</strong>.</p>
<p>This architecture is nice and simple, with discrete lines of interaction and well defined purpose and interface. The <strong>tradeoff</strong> however, is that to add a feature you need to add <strong><em>all</em></strong> the individual pieces to each of these layers.</p>
<p>To illustrate the idea with an example you wanted to to add a "live video chat" feature to your app. Let’s pretend you have already implemented this sort of thing in some other app, so you are just going to reuse the shared portions of it for this app.</p>
<p>Let’s say the video chat feature assumes there is a users model that hold the URL to the video stream for each user. Features of the Video-Chat include:</p>
<ul>
<li><p>Adding and removing users to your video chat</p></li>
<li><p>Muting the microphone</p></li>
<li><p>Turning the camera on and off</p></li>
</ul>
<p>So in your <strong>React-Redux</strong> app, you would at minimum need to import:</p>
<ul>
<li><p>A video chat component</p></li>
<li><p>8 action creators</p></li>
<li><p>9 reducers</p></li>
</ul>
<p><img src="../../docs/can-guides/images/introduction/video-chat-react.png" style="width:100%;max-width:750px"/></p>
<p>...and wire them all together with code. The wiring up is not free, and though simple, may produce bugs and there is no guarantee the individual bits will work well together.</p>
<p><strong>CanJS</strong> has a more cross-cutting encapsulation strategy. A can-component knows what data it accepts through <strong>attrs</strong>, and can fetch its own data with models as dependencies, and also handles the user interaction, state-changes and display all within the component.</p>
<p>The individual parts of a component follow the MVVM pattern, and so are decoupled and easily testable themselves, but act as a cohesive unit of functionality for sharing and reuse across the app.</p>
<p>Can-Components can then be used in parent can-component’s views, who can pass the child components attrs through the template bindings, and the whole app can be composed as a tree of components and HTML elements, each acting as little apps themselves.</p>
<p><img src="../../docs/can-guides/images/introduction/canjs-flow.png" style="width:100%;max-width:750px"/></p>
<p>To continue with the <strong>Video-Chat</strong> example above, sharing a video-chat component from one <strong>CanJS</strong> app to another would be far more straightforward. You would only have to import the 1 video chat component, and the only wiring necessary would be to pass in a list of Users to the video-component attrs.</p>
<p><img src="../../docs/can-guides/images/introduction/video-chat-canjs.png" style="width:100%;max-width:750px"/></p>
<p>Encapsulated modules, like <strong>can-components</strong>, have many advantages over monolithic layers, like the layers provided by <strong>react-redux</strong>.</p>
<p>The encapsulated components are easier to test. In the <strong>react-redux</strong> example, to run some black-box functional tests, you would have to pull in all the individual pieces and assemble them like a mini version of the app. The can-component on the other hand could just be imported and placed on the page, and have the tests run, because it encompasses all the behaviour it needs in one uni</p>
<p>Using encapsulated components makes it easier to find and fix bugs. Working across layers means understanding the different parts and how they work together, if something is broken you’ll have to check in several spots to see where the problem is coming from. When you fix bugs in layered code, you end up having to touch the code in many places, as opposed to the encapsulated module where all the related code is in one place. And touching different places in code increases the chances of having some unintended effect on downstream code, making it more fragile.</p>
<p>When you are working in a team, the layered approach can be more risky, as you’ll all be working in the same code a lot, since layer fixes and features will always span multiple layers, and you’ll have a lot more opportunity for overlap, or having a negative effect on another person’s feature, leading to merge conflicts and potential bugs.</p>
<p><strong>CanJS</strong> chooses a pattern of encapsulated models over a layered architecture because they are easier to share, easier to test, easier to find and fix bugs in and easier to work together on as a team.</p>
<h3>Summary</h3>
<p><strong>React</strong> is not really a full solution for building apps with web technology, and the <strong>React</strong> ecosystem is still the wild west when it comes to state management and supporting technologies.</p>
<p><strong>CanJS</strong> simplifies your code with it's observables and component architecture.</p>
<p><strong>CanJS</strong> let's you integrate with your existing project easier and lets you be confident you won’t be re-writing your app in a new JavaScript framework next year.</p>
<h2>Angular 2</h2>
<p><strong>Angular 2 (ng2)</strong> is actually very similar to <strong>CanJS</strong>, if you look at these two "Hello-World" components, you see just how aligned the concepts behind <strong>Angular 2</strong> and <strong>CanJS</strong> really are.</p>
<pre><code class="language-javascript">import Component from 'can-component';
import stache from 'can-stache';
export default Component.extend({
tag: "hello-world",
template: stache(`<h1 ($click)="emphasize()">{{ message }}</h1>`),
viewModel: {
message: "Hello world!",
emphasize() {
this.message += '!';
}
}
});
</code></pre>
<p>AND</p>
<pre><code class="language-javascript">import { Component } from '@angular/core';
@Component({
selector: 'hello-world',
template: '<h1 (click)="emphasize()">{{ message }}</h1>'
})
export class HelloWorld {
message = 'Hello World!';
emphasize() {
this.message += '!';
}
};
</code></pre>
<p>Because it’s backed by Google, <strong>Angular</strong> is sometimes the "go-to" choice for teams developing web apps or web pages, but often it’s way more than what the project requires.</p>
<p>The steep learning curve, esoteric terminology, tricky syntax and concepts can end up slowing a team down far more than any benefit it provides. So it’s worth the effort to evaluate the framework and see if <strong>Angular 2</strong> is really what your team needs.</p>
<p>That being said, <strong>Angular 2</strong> also encompasses a lot more than just the <strong>MV</strong>* portion of the library: things like code generators, a dev-server, an SSR solution, and a system for script loading and dependency management. This makes <strong>Angular 2</strong> a lot more comparable to <strong>CanJS</strong>’s parent project <a href="https://donejs.com/">DoneJS</a>, but we can still evaluate the parts that <em>do</em> line up, to find the notable differences you need to make a proper comparison.</p>
<h3>Longevity and Stability</h3>
<p>When evaluating <strong>Angular 2</strong> as an option, it is unwise not to consider the proverbial elephant in the room: "Is this version going to stick around this time"?</p>
<p>Google and the Angular team released <strong>Angular 2</strong> as a complete re-write with no backwards compatibility, and no real plan for upgrading your application beyond running both Angular 1 and 2 on the same page until you can get all your directives and services migrated over to the new one. Google has a <a href="http://www.wordstream.com/articles/google-failures-google-flops">history of abandoning projects</a>, so you’ve got to ask: How long will it be until you’re rebuilding your app in the new, incompatible <strong>ng3</strong>?</p>
<p>With <strong>CanJS</strong> we have a guiding principle: You shouldn't have to rewrite your application to keep pace with technology. We’re in it for the long haul, and dedicating to providing you stability and easy upgrades balanced with new features and progressive techniques in web development.</p>
<h3>Modularity</h3>
<p>Sometimes, you’re not building the next Gmail, you only have a few interactive forms on your site that could use some pizzaz. Sometimes, you already have a working app, and you want to move progressively into a new framework, not do a whole re-write of what is already working.</p>
<p>The key in these times, is to pick something you can use a piece at a time, as you need it, and pick the parts you need, without bringing in the kitchen sink. You’ll want modularity,</p>
<p><strong>CanJS</strong> is very modular. In <strong>CanJS 3.0</strong>, a lot of time was spent breaking out the useful parts of the system into modules and packages that can be used independently of each other but still work harmoniously together.</p>
<p>For example you could:</p>
<ul>
<li><p>just use <strong>can-compute</strong> to quickly bind HTML inputs to observable values, giving you a nicer abstraction without a lot of event listeners and callback coordination</p></li>
<li><p>use <strong>can-fixture</strong> by itself to to mock out ajax and websocket requests for your tests</p></li>
<li><p>use only <strong>can-connect</strong> and <strong>can-define</strong> to create awesome observable models that are connected in realtime to your existing API</p></li>
<li><p>use just <strong>can-route</strong> to take advantage of state-based observable routes, for the easy url updating and decoupling the pattern provides</p></li>
</ul>
<p>Any individual or combination of modules will provide you a great tools for streamlining your web development, and together they become our own little "Megazord” <strong>CanJS</strong>, greater than the sum of it’s parts</p>
<p><strong>Angular 2</strong> is more of an "everything out of the box" framework, where you get everything up front, and it all works together to help you develop more efficiently. The pieces and modules of Angular’s libraries are not really meant to be used on their own, and it would be hard to just extract the tiny bits you need without buying into the whole Angular framework.</p>
<p>There’s a <a href="https://news.ycombinator.com/item?id=12692595">funny story</a> on Hacker News about JavaScript Expert whose performance improvements involved removing almost all the JavaScript on the site. The point is, you should know what you need, and include only the tools needed to get the job done, everything else is bloat.</p>
<h3>Typescript</h3>
<p><strong>TypeScript</strong> is a typed superset of JavaScript that compiles to plain JavaScript that offers optional static typing and type inference. But if you are not already using TypeScript, should your choice of framework decide that for you? Do you even want it?</p>
<p><strong>CanJS</strong> was created before ES6 and transpiling were prevalent in front-end JavaScript, so you don’t need TypeScript, though of course it would still would work with TypeScript if that was your preference.</p>
<p>Though the <strong>Angular 2</strong> people have been assuring the general masses that "you don’t <strong>need</strong> typescript to write <strong>Angular 2</strong> apps", the fact is the framework was designed around embracing the syntactic allowances, like decorators and class fields, and if the framework to be presented without them it would look clunky and pretty verbose.</p>
<pre><code class="language-javascript">const HelloWorldComponent = ng.core.Component({
selector: 'hello-world',
template: '<h1>Hello {{name}}!</h1>' + '<input [(ngModel)]="name">',
viewProviders: [GreetingService]
}).Class({
constructor: [GreetingService, function(greetingService) {
this.greetingService = greetingService;
this.name = "world";
}
});
var AppComponent = ng.core.Component({
selector: 'app',
template: '<hello-world></hello-world>',
directives: [HelloWorldComponent]
}).Class({
constructor: function() {}
});
document.addEventListener("DOMContentLoaded", function() {
ng.platform.browser.bootstrap(AppComponent);
});
</code></pre>
<p>TypeScript may offer all sort of benefits, especially for large apps with multiple teams working on them, but it’s more unnecessary baggage if you weren’t using typescript already.</p>
<h3>Dependency Injection</h3>
<p>Dependency Injection (<strong>DI</strong>) <em>as a pattern</em> can provide decoupling from your dependencies, and allow a flexibility of configuration for your modules, but <strong>DI</strong> <em>as a framework</em> adds additional verbosity and configuration all over your application. In JavaScript, a <strong>DI framework</strong> is useful in only rare cases, due to the flexibility of the language so, in the end, is it really worth it to have all the extra code and complexity?</p>
<p><strong>CanJS</strong> chooses not to include a Dependency Injection framework, opting instead for the developer to decide if they need any more DI than JavaScript can already provide. JavaScript’s prototypal inheritance and mutable runtime instances can serve 90% of the uses for Dependency Injection, and if the developer decided they need a DI library they are free to add their own.</p>
<p><strong>Angular 2</strong> has doubled down on Dependency Injection, making every part of it’s design incorporate providers, services, and an injection syntax. This creates an odd pattern in <strong>Angular 2</strong> where not only do you have to import a component or service and use it, you also have to declare the provider for it, often just passing the class as a shorthand.</p>
<p>Note in the example below <code>FriendService</code> is imported, used <em>and also</em> needs to be declared in the <code>providers</code> metadata:</p>
<pre><code class="language-javascript">import { Component } from '@angular/core';
import { FriendService } from 'app/friend.service';
@Component({
selector: 'my-friends',
providers : [FriendService],
template: `
<div *ngFor="#f of friends">
<h4> Name : {{f.name}} </h4> <h4>Age: {{f.age}}</h4>
</div>
`
})
export class FriendComponent {
constructor(_friendService: FriendService) {
this.friends = _friendService.getFriends();
}
}
</code></pre>
<p>A Dependency Injection system may have a place in other languages, like Java, but JavaScript can handle the pattern easily already. The DI System just ends up being extra syntax, and a there is a confusing loss of clarity when the classes and types imported into the class definition may not actually be the classes used to produce the dependency instance.</p>
<p><em>See the above code example, and realize that <code>FriendService</code>, may in some instances not actually be used in the <code>FriendComponent</code>, if some parent module defines a different provider for <code>FriendComponent</code></em></p>
<h3>Steep Learning curve</h3>
<p>Angular is known for it's steep learning curve, and Angular 2 doesn't really break that reputation.</p>
<p>There are a whole lot of new things a developer must learn to become effective with
Angular 2. You've got to learn the TypeScript syntax, all the examples are written
in it so You're going to have trouble if you skip this step. The Dependency Injection
system is so ingrained, you must learn early how it works, and the syntax involved
just to get going. There is the templating system, the decorators/annotations, a
<a href="https://angular.io/docs/js/latest/api/">wealth of API's and modules</a>, and the kicker
of it all is, because of the "all-or-nothing" nature of Angular 2, you have to learn a
whole lot of it, just to get started.</p>
<p>CanJS also has many different modules and API's to learn, but because of CanJS's modular and individually packaged libraries, you only have to learn what you need, when you need it. There is no new "transpile-to-js" language to learn and no Dependency Injection framework to understand before you can start creating your components and composing your apps.</p>
<p>If your team needs to get started quickly, and hit the ground running, Angular 2 may not be the best choice, but CanJS just might be the right fit for your team.</p>
<h3>Data Fetching and Real-time Data</h3>
<p><strong><a href="../../can-connect.html">Can-Connect</a></strong> allows for <strong>CanJS</strong> <strong>observables</strong> to be connected to a data source such as an RESTful API or a real-time data stream, with advanced features like fall through caching and batched minimal requests, so that your components can just request the data they need while can-connect figures out how to fetch that data in the most efficient way possible.</p>
<p><strong>Angular 2</strong>’s HTTP Service doesn’t have the advanced features like minimal requests and fall-through caches, and any real-time data-source would require you to write a custom service.</p>
<h3>Summary</h3>
<p><strong>Angular 2</strong> is not the safe bet you may be hoping it is. Google wouldn't hesitate to drop it entirely when it no longer serves their purpose, it’s got a steep learning curve, and lot of features that you may not want or need.</p>
<p><strong>CanJS</strong> has a mission to be the stable platform for you to develop your web apps on for years to come. It’s modular enough to take only the parts you need as you need them. It has all the features you need and doesn’t force you into patterns or technologies you don’t.</p>
<h2>Non-technical comparison</h2>
<p>If none of the technical arguments convince you to give CanJS a try, consider this: all the modern frameworks have more or less converged in 2016. You can build a very similar application, with routing, observables, models, and server side rendering (with some exceptions) with Angular 2, several of the React frameworks, Ember, or CanJS. Therefore, the MOST important factor, even beyond small technical differences, is the stability and stability of the framework. You want to bet on a horse that you can ride into the future, not the horse that looks prettiest today.</p>
<p>CanJS's mission is to <strong>minimize the cost of building and maintaining
JavaScript applications by balancing innovation and stability, helping developers transcend a changing technology landscape</strong>. We've spent the past 9 years focused on balancing these two oppoising goals, and have the track record to prove it. Read in more detail on our <a href="mission.html">mission page</a>.</p>
<p>There’s no advantage to choosing projects maintained by a big company, since big companies often kill platforms that many developers have invested in.</p>
<p><img src="../../docs/can-guides/images/introduction/framework-death.png" style="width:100%;max-width:650px"/></p>
<p>There’s no advantage to choosing a framework with a large community, since communities are fickle and change yearly.</p>
<p>There’s no advantage to choosing a framework based on ability to hire devs that list X on their resume, since any competent JavaScript developer can be equally effective in any modern framework with a couple days of learning.</p>
<p>This is why CanJS has a stellar reputation with <a href="who-uses.html">enterprise companies</a>. Stability and longevity are critical factors to your application’s success.</p>
</section>
<script type="text/javascript">
var docObject = {"src":{"path":"docs/can-guides/introduction/comparison.md"},"description":"\n","name":"guides/comparison","title":"Comparison to Other Frameworks","type":"page","parent":"guides/introduction","order":5,"hide":true,"comment":" ","pathToRoot":"../.."};
</script>
</article>
<footer><p>CanJS is part of <a href="http://donejs.com" target="_blank">DoneJS</a>. Created and maintained by the core <a href="https://donejs.com/About.html#section=section_Team" target="_blank">DoneJS team</a> and <a href="http://bitovi.com" target="_blank">Bitovi</a>. <strong>Currently 3.0.0.</strong></p></footer>
</div>
</div>
</div>
<script>
steal = {
instantiated: {
"bundles/bit-docs-site/static.css!$css" : null
}
};
</script>
<script type='text/javascript' data-main="bit-docs-site/static" src="../static/node_modules/steal/steal.production.js"></script>
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>