Here are some things that make the new proposal for a new Knockout website pretty darned cool, in our opinion.
- Single-page app
- Auto-reloading Application Cache
- Javascript error checking
- Inline auto-reloading samples
These are a selection of the many really cool features that underly the future online presence of Knockout. The ideas and implementation here will hopefully provide a really great user experience, help demonstrate a little of what Knockout can do, and show some techniques that can be used in many other contexts to improve the web.
Single-page app
The site uses Knockout along with a number of other packages, as you can see in our bower.json and package.json.
The compiled unminified source code is pretty lean, coming in at under a thousand lines. While demonstratign the concept we are relying on over a MB of Javascript libraries. Much of that can and will be minified down when it comes time to deploy more broadly.
Without further ado, let’s get into the details.
Overall page strategy
We create a single instance of an object, Page
that Knockout binds to the <body>
and thereafter determines the state of the entire page. When an anchor is clicked the event is intercepted then, if the browser supports HTML5 history and the user has not disabled the single-page functionality, the page rewrite boils down to Knockout changing the body
template of the Page
instance. If the browser does not support HTML5 or the user turns off the single-page functionality, then the link is followed (though it may have been rewritten).
When the body template changes the template shown changes. As well, body
change triggers a page title update to the data-title
attribute of the template being changed to.
To make things load quickly we concatenate our templates into one file that is loaded asynchronously, likely from the appcache (more on that below).
All our pages are stored as markdown and also compiled into a single file. A couple tricks we’ve used include exposing the gitVersion
as a property of the global
object, that is reloaded (minus debounce) every time it is accessed by e.g. the markdown html template. This becomes important when reporting to our Javascript error reporter, so we know what version has exhibited issues. This trick allows us to get around having to intercept the vinyl streams to inject the git version, which is arguably a more “gulpy” way to do it.
History
I opted for HTML5 history support instead of HTML5-History-API or history.js. Those are excellent plugins, but the idea here is to keep it simple and aim for the future.
As you can see from the anchor click intercept and popstate
handler manipulating the history is pretty trivial when used in conjunction with Knockout.
Multi-page fallback
All our links are rewritten to /a/PAGE.html
. These links are simultaneously generated individually as actual files, and generated for use by Knockout as templates that go into the body
property of our Page
.
Auto-reloading Application Cache
The application cache speeds up loading time and gives offline access. We regenerate our application cache basically whenever anything changes in the compiled files, updating the date of compilation so browsers will know to reload the cache.
When the page is loaded regularly recheck the application cache. This is handy because we can inform users when there is a newer version. It also comes in handy as a livereload substitute during development.
Error reporting
We are using TrackJS to keep us up to date on what might be happening. So far, very few errors have been reported over a few hundred accesses, but it was easy to identify and fix the issues when they were reported. There’s not much to say here other than we do it, and it’s really awesome!
Inline auto-reloading samples
Many of the examples on the website are “live” in the sense that they can be edited directly. They can also be sent off to jsFiddle and CodePen, making it easy to tinker, learn, and demonstrate reproducible issues.
We use the Ace editor to show and edit examples. When a change to the code occurs, the result
is updated.
Our inline examples are decoded as YAML, then converted to JSON and encoded in Base64 to simplify escaping issues.
When a <live-example>
tag is bound by Knockout it is interpreted as a LiveExampleComponent. The base64
param is decoded and passed to an Example constructor in the LiveExampleComponent.
The LiveExample
is a separation of the user and data interface from the instance of an Example
itself; they could be combined but this lets us create Examples
in other contexts from the LiveExample
.
Where to now?
Next up, we have to go through the examples, links, references, etc.. to clean it up. The style definitely needs some work. Some “live” time to give users time to give it a whirl and send feedback.