DoneJS 3.0 is out! 🎆
If you're unfamiliar with DoneJS you should definitely check out our homepage and run through the getting started guide (it doesn't take very long). Let me give you the quick spiel: DoneJS is a JavaScript framework that provides you everything you need to build performant real-time applications.)!
So settle in while we go over as much of what the new DoneJS version provides as we can cram into one article.
Incremental rendering
About a year ago we started thinking about how we could incorporate streaming and HTTP/2 into DoneJS. DoneJS has long had the easiest to use server rendering solution of any framework. With streaming we asked; how can we also make it the fastest?
Our answer to this question is incremental rendering. Incremental rendering splits your app into two parts: the static content that can be sent right away, and the dynamic content that needs to be waited on.
By sending the static content immediately, it allows an app to start booting up earlier than in traditional SSR. Meanwhile the server continues to render that dynamic content and send deltas back to the client.
The result is the fastest server rendering solution on the market. See below, the app on the left is a traditional SSR app (using DoneJS 2) and the app on the right is using incremental rendering in DoneJS 3.
CanJS 5
You can learn about everything that went into CanJS 5 in its release article. The following are some of the things that DoneJS 3 highlights in its generators and guides.
can-debug
can-debug provides several methods for debugging the ways in which observable affect each other. The CanJS Debugging Guide gives you a primer on debugging CanJS applications.
Additional, can-debug enables the devtools that allow you to inspect the state of your ViewModel graphically. The CanJS DevTools allow you to
View and edit your ViewModels.
Visualize the dependency graph of your components.
Debug changes to observables using the CanJS queues system.
Separating Route data from the Rest of the Application ViewModel
Sometimes there are things you want in your global state (The Application ViewModel) that you do not want bound to the route. Previously this required defining those properties as
serialize: false.
In DoneJS 3 we no longer bind the Application ViewModel to the route. Instead, only a property on the ViewModel gets bound.
By default this is the
routeData property. In your index.stache this gets declared like so:
<can-import
The pageComponent pattern
One thing we've always noticed about DoneJS apps is that the switch statement in index.stache never quite felt right. Even in a small app like place-my-order it becomes difficult to read.
In more complex apps it has been common to dynamically generic a stache template with can-imports to progressively load the current page.
Thanks to constructible can-components and can-value you can now do this easier in JavaScript.
Check out the place-my-order Application ViewModel to see how this works. Here's a small snippet:
get pageComponent() { switch(this.routeData.page) { case 'home': { return steal.import('~/pages/home.component') .then(({default: Home}) => { return new Home(); }); } ... }
Which allows you to write this little stache:
{{ #if ( this.pageComponent.isResolved ) }} {{ this.pageComponent.value }} {{ else }} {{# if ( this.pageComponent.isRejected ) }} <div>Error: {{ this.pageComponent.reason }}</div> {{ else }} <div class="loading"></div> {{/ if }} {{/ if }}
What this does is:
Showing a loading animation while the page component is being progressively loaded.
Shows the page component once it has been loaded.
{{this.pageComponent.value}}inserts the component instance.
Shows an error message if there was an error with loading the component.
for/of
In stache you can now render items in a list using a
for/of loop that works similar to that of JavaScript.
This continues efforts to make scope more readable in stache templates. If you stick to for/of loops you can always reference your component’s ViewModel with
this.
{{# for( item of restaurant.menu.dinner ) }} <li class="list-group-item"> <label> <input type="checkbox" on:${{ item.price }}</span> </label> </li> {{/ for }}
StealJS 2
Additionally DoneJS comes with StealJS 2 which gives you:
Native Promises
By default steal.js doesn’t include the Promise polyfill (it's available as steal-with-promises.js if you need it). This makes for a much better debugging experience in asynchronous code:
Tree Shaking
Tree-shaking is a form of dead-code elimination that examines
import statements to determine what code is used. Any code that's not used can be eliminated, or just never loaded.
StealJS is likely the first client-side module loader to provide tree-shaking. This allows DoneJS apps to import everything from can, rather than individual packages.
Your code goes from:
import DefineMap from 'can-define/map/map'; import DefineList from 'can-define/list/list'; import superModel from 'can-super-model'; import QueryLogic from 'can-query-logic';
To a more simple:
import { DefineMap, DefineList, superModel, QueryLogic } from 'can';
Thanks to tree-shaking we can include all of CanJS' ecosystem packages as well.
This means if you have a use case for streams you can just include
defineStreamKefir in your import statement from can. No extra packages to install.
Coming Up
DoneJS 3 brings a lot to the table but we aren’t slowing down. In 2019 we’ll put even more focus on improving the developer experience in DoneJS apps. First thing: improving build times with steal-tools build.
And of course, what is voted on in the DoneJS Community Survey. Please sign up to let us know what you want to see prioritized in the coming months.
Hire us
DoneJS’s main sponsor is Bitovi. We provide web and mobile application consulting services. We can build your app, work with you to build it, or teach you how to build it. To learn more visit bitovi.com or @bitovi on Twitter.