Angular 2, React, and Knockout apps on ASP.NET Core
One of our goals for ASP.NET Core is to make it a truly first-rate platform for building modern rich JavaScript apps (sometimes called single-page apps). This is the feature area I’m personally focused on, so in this post I want to describe what we’re up to and ask for your feedback.
Update: Since .NET Core RC2 has now shipped, this post is updated to cover usage on RC2 only.
The challenge with building 2016-style clientside-centric web apps is that there’s just so much going on, such as:
- Frameworks (Angular 1 or 2, React, Knockout, Aurelia, …)
- Architectures (plain old MV*, Flux/Redux, …)
- Languages (ES2016/TypeScript/…, LESS/SASS/…)
- Build systems (Webpack/Gulp/…), and all the ensuing choices:
- Development middleware
- Hot module replacement
- Bundling strategies
- Lazy loading of modules
- Server-client integration
- Server-side prerendering of Angular/React/etc components
- Server-side prepopulation of client-side data caches
- Plain old fetching and synchronising data
- Combining server-side and client-side routing
- Combining server-side and client-side validation
With ASP.NET Core, we don’t want to shy away from all this and just say it’s your job to set up whatever you want. Creating from scratch a TypeScript-based React+Redux app that does server-side prerendering and supports Hot Module Replacement without needing manual compilation is not trivial, but it’s amazingly effective when you’ve got it!
So, we want to offer:
- Helper packages - a collection of NPM and NuGet packages that cover most of the plumbing work needed to make all the above work seamlessly on ASP.NET Core
- Project templates (a.k.a. starter kits) that give you a fully-working setup immediately. Naturally these will evolve continually as client-side frameworks and patterns change.
The templates
Right now we’re building templates for Angular 2, Knockout, React, and React with Redux. They all give you a Bootstrap-based dashboard-style site with client-side navigation, and each demonstrates a pattern for fetching data and implementing UI components with your chosen framework/architecture.
Here’s a more detailed breakdown:
Angular 2 | Knockout | React | React + Redux | |
---|---|---|---|---|
Language | TypeScript | TypeScript | TypeScript | TypeScript |
Build/loader [1] | Webpack | Webpack | Webpack | Webpack |
Client-side navigation | Yes | Yes | Yes | Yes |
Dev middleware [2] | Yes | Yes | Yes | Yes |
Hot module replacement [3] | Yes, limited | Yes, limited | Yes, awesome | Yes, awesome |
Server-side prerendering [4] | Yes | No | No | Yes |
Lazy-loading [5] | No | Yes | No | No |
Efficient prod builds [6] | Yes | Yes | Yes | Yes |
[1] Why Webpack? Because it’s quickly becoming dominant and offers such advantages for convenience, productivity, and runtime performance. Obviously you can change to Gulp/Grunt/nothing or whatever else you prefer if you want, but Webpack is the basis for features [2] and [3] below.
[2] Dev middleware makes your development-time experience more fluid and keeps you mentally in the zone. Instead of having to wait for client-side resources to rebuild (e.g., TypeScript -> JavaScript) each time you change anything, or even having to have built resources somewhere on disk, this middleware transparently intercepts requests for Webpack-built resources and evaluates them in memory, ensuring the browser always receives the latest compiled artifacts. This stays running so it can update built artifacts almost instantly even in large apps.
[3] Hot module replacement takes [2] a step further. You no longer need to ‘refresh’ the browser after each change - your updated code/CSS/etc is automatically pushed into your running client-side app as soon as you save changes to any source file. This is better than a live-refresh type technique: because it doesn’t reload the page, you don’t lose the state of your app in the browser’s memory (or destroy your debugging session, etc.). In the case of the React templates, it can even retain the complete state of your React components across code changes. When you get used to this, it’s hard to imagine putting up with not having it. Combine this with Redux’s time-travel debugging for crazy-futuristic results.
[4] Server-side prerendering is the basis for isomorphic or universal apps. We can also run your Angular 2 / React components on the server, so the server can render the initial HTML and send that to the browser. The browser then shows the initial app UI instantly while still loading client-side resources. When combined with ASP.NET Core’s <Cache>
tag helper, your perceived page load time can be reduced to milliseconds. In the React+Redux case, all application state is transferred seamlessly from server to client, so the browser can continue execution where the server left off. To a limited extent, this allows an Angular 2 / React+Redux app to run even with JS disabled in the browser, though that’s more of a party trick than a realistic use case.
[5] Lazy-loading - instead of transferring the entire client-side app to the browser, you can organise components into bundles that are fetched dynamically on demand. This is seamless to the user and to developers building components. Currently this feature is exclusive to the Knockout template, but if anyone can suggest sufficiently clean and simple ways to achieve it with Angular 2 / React, we’ll add the option to those template too.
[6] Efficient dev vs production builds - at development time, you want unminified builds containing source maps for easy debugging, whereas in production you want the leanest, most stripped-down bundles you can get. We’ve set up a pattern where vendor resources (third-party JS/CSS libraries) go into a separate bundle so that at dev time, your builds are far faster, and in production, browsers can cache the vendor resources and not re-fetch them even as you update your own application code.
I want it now! How do I start?
Great news - it’s easy. The instructions are the same no matter whether you’re on Windows, Mac, or Linux.
- Make sure you have the prerequisites
- ASP.NET Core RC2
- Node.js. If you’re on Windows, make sure your installation is new enough to have NPM version 3+ (run
npm -v
to find out) otherwise you’ll have a bad time.
-
Install yeoman and the
aspnet-spa
generatornpm install -g yo generator-aspnetcore-spa
Short-term extra dependency on Webpack
Because of an outstanding implementation quirk, you’ll also need Webpack installed globally:
npm install -g webpack
This explicit dependency on the
webpack
tool will go away soon. -
Create your new project
cd some-empty-directory yo aspnetcore-spa
You’ll be asked what kind of project you want to create (Angular 2, React, etc.), then it will lay out the initial files. Wait a moment (or go for a 7-course dinner if you’re on a 3G network) while NPM downloads approximately one bajillion files.
-
Run it
dotnet run
Now open http://localhost:5000/ and marvel at the glory of it all. If you’re on one of the templates that supports server-side prerendering (see above), then try disabling JS in your browser and see it still works :)
-
Try it in development mode. Set the environment variable and then restart
dotnet run
. On Windows, that’s:set ASPNETCORE_ENVIRONMENT=Development dotnet run
… or on Mac/Linux:
export ASPNETCORE_ENVIRONMENT=Development dotnet run
Now you can modify client-side resources in a text editor (e.g., some
ts
file, or atsx
file in a React app, or an.html
file in a Knockout/Angular app), and see it auto-update immediately in the browser. This is the combination of Webpack dev middleware and Hot Module Replacement (HMR). -
Make a production build. Set the
ASPNETCORE_ENVIRONMENT
variable toProduction
and then run:webpack --config webpack.config.vendor.js webpack
…to regenerate both the vendor and app bundles. Now when you run the app (
dotnet run
again), the browser will receive fully minified resources.
Hopefully it should be clear in the source folders where you can start to add new components to your app and make it your own.
What editor / IDE is preferred?
Obviously it’s up to you. ASP.NET Core projects don’t require any special non-human-editable source files. The .xproj
file is a convenience for Visual Studio users, but if that’s not you, you can delete or ignore it. Any text editor is fine.
I’m mostly using Visual Studio Code, which is an excellent free cross-platform editor for ASP.NET Core apps and C#/TypeScript (among other languages). Use it to open the directory containing your new single-page app, and you’ll get full intellisense on your C# and TypeScript. It supports .tsx
syntax for React apps as well.
Visual Studio users can open the generated .xproj
file. You’ll get full support for building, launching, debugging, etc.
Is this production-ready or what?
Well, ASP.NET Core itself is at release-candidate-2 stage, and if you’re using Angular 2, that’s at release-candidate-1 stage. For the templates and packages described here, I’d say ‘beta’ is a fair description. If you find this stuff is all working for you - and I hope you do - then stability should be really quite good.
What about ${my preferred JS framework}?
Obviously we couldn’t do a great job of maintaining an unlimited number of packages and templates for every possible JS framework. So in most cases, for other JS frameworks/languages/etc., you can use our templates as a possible starting point for adapting them to some other framework, and then you can distribute your templates/packages if you want.
In the specific case of Aurelia, I’m already discussing this with Rob Eisenberg and his team, and we do expect to have something for you!
Your feedback wanted
Clearly there’s a lot going on here in terms of the range of frameworks and libraries we’re trying to support, and the number of design decisions we’ve had to make around project layout and architecture.
It would be great to know if this is the sort of thing you’d use, and if you have any suggestions for improvement! You can either post here or in the Github repo.