Author's note: this was originally intended to be one in a series, but what with WinJS being terrible, I decided to abandon the platform. Honestly, avoid it if you can. Since this was written, I've decided to avoid using libraries I can't do pull requests on as much as possible.
For the past couple of months, I've been working on/off on a Windows 8 app called Cargo. It's a client for the Pocket service for "read later" bookmarking, similar to Instapaper or Readability.
Being first and foremost a web developer, I was mostly interested in developing Windows Store apps because of the option to develop my app in HTML5 & JavaScript. This was an interesting choice on Microsoft's part - the traditional .NET option of C#, C++, or VB (and XAML for templating) is of course still supported, so my (uninformed) guess is that this was a gambit to attract web developers to a new platform, rather than an honest attempt to bring web standards to desktop and mobile apps.
But, hey, it seems to be working well enough. And while I would never write an HTML5/JS app for a traditional desktop platform, with Microsoft's Metro interface (or "New Design Interface," or "Windows 8 Interface," or whatever they're calling it these days - even MSDN is inconsistent!), it's actually a viable option. They provide a ton of good APIs for integrating with devices, including various views with great touch support.
Metro web apps are primarily based on two libraries: the Windows runtime APIs (universal to all Windows 8 apps), which handle fundamentals like storage and launching other apps, and WinJS, a big hodgepodge of useful JavaScript bits. The former is mostly self-documenting, and all one really needs to use it is reference. On the other hand, WinJS is a massive library with extremely poor documentation.
This series of blog posts are the result of my exploration into creating a WinJS app. Rather than simply go over the various aspects of WinJS, I'll show how to use them to create an MVC structure, similar to an app created using a framework like Backbone.js.
(an aside: there are many people who forego WinJS as much as possible, instead preferring to use libraries like Backbone, Angular, or Knockout. This is not necessarily a bad idea - I've heard Knockout integrates particularly well - but I'll focus purely on the WinJS APIs here.)
Throughout this series, I'm going to have a lot of major issues with WinJS. It's a frustrating library - there's a lot of great ideas there, and some are implemented well, but many are just half-baked. The worst is that the library isn't open source, unlike just about every other JS library you'll ever use, so there's no way to just submit a pull request to fix it!
My first beef with WinJS is the joke of a "module" system. Stephen Walther has a good article discussing it here, but if you're used to module loaders in JavaScript or another language, you probably immediately see the major drawbacks it has:
<script>
block at the top of your entry page(s)Thankfully, there's only one situation where WinJS actually requires you to use their Namespaces - defining template converters - and it works well enough there, regardless of whether you've used them elsewhere in the app.
Personally, I vastly prefer creating JS applications using RequireJS. RequireJS is a fairly simple library for using AMD modules, which are the most common format for JavaScript modules on the web - most popular libraries, including Underscore.js and jQuery, will have RequireJS versions, be they official or a maintained fork.
RequireJS is simple to implement:
default.js
file Visual Studio generates for you when you create a new WinJS project as an entry pointdefault.js
file in your entry point page, after RequireJS itselfdefault.js
, call "main" modules depending on the activation state. For example, if an app is being opened, you would run a "main" module; if it's being restored from suspension; you would run a "restore" module; and if it's being called from a share target, you could run a "share" module.You should probably read up on the RequireJS API before reading further in this tutorial, but it's not necessary. Keep it around for reference, though!
First, add a RequireJS config file to your project:
This file just sets the root of your RequireJS modules to /js
, which makes your paths easier to write, but you can add all sorts of rules (such as shims) to this file if needed.
Update your index.html
file. Rather than have a giant stack of <script>
tags, just include require.js
(with your data-main
attribute being set to your config file) and default.js
:
Finally, update your default.js
. This file is used as an entry point for your app, and essentially will run different modules based on your app's initial state. For example, this file will run different modules based on if it's being launched the first time, restored from suspension, being used as a share target, or being suspended:
Once you have RequireJS set up, dependency management and creating modular applications is a breeze. Want to create a new model class? Just create /js/app/models/SomeModel.js
that returns a SomeModel
prototype, include it in your dependencies, and create it with new SomeModel()
. Of course, we'll get to creating models (and an MVC architecture) in the future.
All of these modules will simply take the form:
Ultimately, you end up writing code similar to the module pattern WinJS encourages, but with the bonus of dependency management and loading JS files only when needed.
Next, I'll cover some of the basics WinJS includes, including Classes, Mixins, and Promises. These will be the basis that we build an MVC structure on in future sections. Look for that post next week-ish.