static sites with react
TRANSCRIPT
14
1. define structure of pages
2. create HTML files from rendered
react
3. bundle client-side JS and provide
instant page linking
16
import React from 'react';
const Example = () => <main role="main"> <header> <h1>Example</h1> </header> </main>
Example.meta = { title: 'Example', description: 'This is the example' };
export default Example;
Example.react.js
17
import Root from './Root.react'; import Index from './Index.react'; import Example from './Example.react'; import NestedExample from './NestedExample.react';
const routes = { IndexRoute: { component: Index }, ExampleRoute: { path: 'example.html', component: Example }, NestedExampleRoute: { path: 'this/is/a/ridiculously/nested/example.html', component: NestedExample } }; // ...
routes.js
18
// ... const getChildRoutes = () => { let childRoutes = []; for (let key in routes) { if (key !== 'IndexRoute') { childRoutes.push(routes[key]); } } return childRoutes; };
export default { path: '/', component: Root, indexRoute: routes.IndexRoute, childRoutes: getChildRoutes() };
routes.js
19
1. define structure of pages
2. create HTML files from rendered
react
3. bundle client-side JS and provide
instant page linking
21
import { match, RoutingContext } from ‘react-router'; import routes from '../src/components/routes';
/* * For the `indexRoute` plus all * `childRoutes`, do the following */
const location = '/'.concat(path || '');
match( { routes, location }, async function handleMatch(err, redirectLocation, renderProps) { /* * Render component markup, create output directories * and write markup to a file whose filename is * extracted from the route's path. * Provide fallback of `index.html` for IndexRoute. */ } );
match each location
22
import { exec } from 'child_process'; import React from 'react'; import { renderToString } from ‘react-dom/server'; import fsp from ‘fs-promise';
const html = renderToString(<RoutingContext {...renderProps} />), directory = determineDirectory(path), filePath = path || 'index.html';
await exec(`mkdir -p _site/${directory}`, execCallback);
await fsp.writeFile( `_site/${filePath}`, `<!DOCTYPE html>${componentHTML}`, 'utf8', fspCallback );
render react and write files
23
1. define structure of pages
2. create HTML files from rendered
react
3. bundle client-side JS and provide
instant page linking
25
import gulp from 'gulp'; import browserify from 'browserify'; import babelify from 'babelify'; import source from 'vinyl-source-stream';
const client = async () => { return browserify({ entries: 'src/browser.js', debug: true }) .transform(babelify) .bundle() .pipe(source('app.js')) .pipe(gulp.dest('_site')); }
export default client;
28
export const path = (route) => { return '/'.concat(routes[route].path || ''); }
<Link to="/">Home</Link> <Link to={ path('ExampleRoute') }>Example</Link> <Link to={ path('NestedExampleRoute') }>Nested Example</Link>
routes.js
react component
29
import React from 'react'; import { render } from 'react-dom'; import Router from 'react-router'; import history from 'history/lib/createBrowserHistory'; import routes from './components/routes';
render(( <Router history={ history() }> { routes } </Router> ), document);
browser.js
32
3. dev server that watches and reloads
2. copy file structure defaults to consumer
1. find CLI tool
33
1. find CLI tool
3. dev server that watches and reloads
2. copy file structure defaults to consumer
37
#!/usr/bin/env node
import program from ‘commander';
program .command('new [path]') .description('create a new react-static project') .action(function handleNew(path, options) { if (path) { // copy over defaults } else { // handle no path } });
$ react-static new portfolio/
40
program .command('serve') .description('run the dev server and have it watch for changes') .action(function handleServe() { execSync('node_modules/.bin/nodemon', { stdio: [0,1,2] }); });
$ react-static serve
// build HTML & JS & then...
const app = express();
app.use('/', express.static('_site')); app.listen(port);
console.log(`=> A development server is running at http://localhost:${port}`);