1 | horse
|
2 | =====
|
3 |
|
4 | horse is a couple of helper classes that can be used to help you build isomorphic
|
5 | applications for io.js / node. It abstracts routing and rendering helpers so
|
6 | that you can plug in a rendering system, bind links, and have an application
|
7 | that works anywhere.
|
8 |
|
9 | A Brief Overview
|
10 | ----------------
|
11 |
|
12 | ```
|
13 | ======================================
|
14 | Your App
|
15 |
|
16 | +---------+ +---------------+
|
17 | | koa | | html5 history |
|
18 | +---------+ | api |
|
19 | | +---------------+
|
20 | req req
|
21 | | | render and
|
22 | \ / wait for new route
|
23 | ------------------------ event
|
24 | | ^
|
25 | v |
|
26 | ====================================== |
|
27 | +--------------+ |
|
28 | | horse/App.js | |
|
29 | +--------------+ |
|
30 | | |
|
31 | v |
|
32 | ==================================== |
|
33 | Your App's Routes |
|
34 | |
|
35 | +---------------+ |
|
36 | | route handler | -> yield { body: reactElement }
|
37 | | | -> throw MissingAuthenticationError();
|
38 | +---------------+
|
39 | ```
|
40 |
|
41 | The App has an instance of an Express-like request router that it uses to map
|
42 | requests to the appropriate handling function, and is run on both the client-
|
43 | and server- side. It's meant to abstract just enough boilerplate out of the
|
44 | way so that you can do your own custom stuff.
|
45 |
|
46 | An example usage might be like: (es6 incoming)
|
47 |
|
48 | `routes.jsx`
|
49 |
|
50 | ```javascript
|
51 | // This is used both client- and server- side, and simply sets up an app with
|
52 | // routes; in this case, returning React elements.
|
53 |
|
54 | import Layout from '../layouts/layout.jsx';
|
55 | import Index from '../pages/index.jsx';
|
56 |
|
57 | function setupRoutes(app) {
|
58 | app.router.get('/', function *() {
|
59 | this.layout = Layout;
|
60 |
|
61 | var user = yield db.getUser(1);
|
62 | this.props = { user };
|
63 |
|
64 | this.body = <Index {...this.props} />;
|
65 | });
|
66 | }
|
67 |
|
68 | export default setupRoutes;
|
69 | ```
|
70 |
|
71 |
|
72 | `server.es6.js`
|
73 |
|
74 | ```javascript
|
75 | import koa from 'koa';
|
76 | import React from 'react';
|
77 |
|
78 | import {App} from 'horse';
|
79 | import setupRoutes from './setupRoutes';
|
80 |
|
81 | var server = koa();
|
82 |
|
83 | var app = new App();
|
84 | setupRoutes(app);
|
85 |
|
86 | server.use(function *(next) {
|
87 | yield app.route(this, function () {
|
88 | var Layout = this.layout;
|
89 |
|
90 | this.body = react.renderToStaticMarkup(
|
91 | <Layout>{this.body}</Layout>
|
92 | );
|
93 | });
|
94 | }
|
95 | ```
|
96 |
|
97 | `client.es6.js`
|
98 |
|
99 | You'll want to add push state too, but that's outside the scope of our
|
100 | example.
|
101 |
|
102 | ```javascript
|
103 | import React from 'react';
|
104 | import {ClientApp} from 'horse';
|
105 |
|
106 | import setupRoutes from './setupRoutes';
|
107 |
|
108 | import jQuery as $ from 'jquery';
|
109 |
|
110 | var app = new ClientApp();
|
111 | setupRoutes(app);
|
112 |
|
113 | var $mountPoint = document.getElementById('app-container');
|
114 |
|
115 | $(function() {
|
116 | $('body').on('click', 'a', function(e) {
|
117 | var $link = $(this);
|
118 |
|
119 | var ctx = app.buildContext($link.attr('href'));
|
120 | yield app.route(ctx);
|
121 |
|
122 | React.render(ctx.body, $mountPoint);
|
123 | });
|
124 | });
|
125 |
|
126 | ```
|
127 |
|
128 |
|
129 | Final Notes
|
130 | -----------
|
131 |
|
132 | * This is all ES6, so you'll want to use a transpiler; I like
|
133 | [babel](http://babeljs.io). To get babel to work with npm modules, you'll
|
134 | need to turn off `ignore npm` and add `.es6.js` to the transpiled files, like
|
135 | so:
|
136 | ```
|
137 | require('babel/register')({
|
138 | ignore: false,
|
139 | only: /.+(?:(?:\.es6\.js)|(?:.jsx))$/,
|
140 | extensions: ['.js', '.es6.js', '.jsx' ],
|
141 | sourceMap: true,
|
142 | });
|
143 | ```
|
144 | * Tested with iojs 1.0.0 and later and node 0.10.30 and later.
|