Upload
luciano-mammino
View
21
Download
1
Embed Size (px)
Citation preview
Rome 24-25 MARCH 2017
{ Universal JS Web Applications with React
Luciano Mammino
loige.link/codemotion-rome-20171
WHO ISLUCIANO
lmammino
loige
loige.co
2
-20% eBook
NJDPEB20
-15% Print
NJDPPB15
loige.link/node-book
3
AGENDA
1. The term "Universal" JS
2. Who & Why
3. Common problems and technologies
4. Building a frontend only Single Page App
5. Making it universal
5
NOT ONLYFOR THE WEB...
Desktop applications
Mobile applications
Hardware!
7
ADVANTAGESOF UNIVERSAL JAVASCRIPT
"JavaScript-only" development
Maintainability
Better SEO
Faster "perceived" load time8
ADVANTAGES...MORE
Keep using React/JS paradigms also to
generate "static" websites
Speed up content loading with
linkprefetch
loige.link/universal-react-made-easy-talk9
IN THE WILD
10
IT LOOKS GREAT BUT...
11
MODULE SHARING
Use Node.js modules in the browser
UMD
12
UNIVERSAL RENDERING
Render the views of the application from
the server (first request) and then in the
browser (next requests)
13
UNIVERSAL ROUTING
Recognise the view associated to the
current route from both the server and the
browser.
14
UNIVERSAL DATA RETRIEVAL
Access data (and APIs) from both the server
and the browser.
AXIOS UNIVERSALFETCH
15
UNIVERSAL STATEMANAGEMENT
Manage changes on the state tree both on
the server and the client...
16
FUTURISTIC/ALTERNATIVE JS?!
17
18
OK...LET'S STOP COMPLAININGAND BUILD SOMETHING!
19
WHAT ARE WEGOING TO BUILD?
loige.link/judo-heroes-app
loige.link/judo-heroes-tutorial
v 2.0
20
21
22
23
curl -sS "https://judo-heroes.herokuapp.com/athlete/teddy-riner"
24
WHAT TOOLS AREWE GOING TO USE?
v2 v15.4
v4v5-alpha
25
Dependencies 😺😺
yarn add \ [email protected] \ [email protected] \ [email protected] \ [email protected] \ [email protected] \ [email protected] \ [email protected] \ [email protected] \ [email protected] \ [email protected]
26
27
The data set// src/data/athletes.js
const athletes = [ { id: 'driulis-gonzalez', name: 'Driulis González', country: { id: 'cu', name: 'Cuba', icon: 'flag-cu.png', }, birth: '1973', image: 'driulis-gonzalez.jpg', cover: 'driulis-gonzalez-cover.jpg', link: 'https://en.wikipedia.org/wiki/Driulis_González', medals: [ { id: 1, year: '1992', type: 'B', city: 'Barcelona', event: 'Olympic Games', category: { id: 2, year: '1993', type: 'B', city: 'Hamilton', event: 'World Championships', category: { id: 3, year: '1995', type: 'G', city: 'Chiba', event: 'World Championships', category: { id: 4, year: '1995', type: 'G', city: 'Mar del Plata', event: 'Pan American Games', { id: 5, year: '1996', type: 'G', city: 'Atlanta', event: 'Olympic Games', category: // ... ], }, // ...];
export default athletes;
28
REACTCOMPONENTS
29
Layout component
30
IndexPage component
31
AthletePage component
32
NotFoundPage component
33
AthletePreview component
34
AthletesMenu component
35
Flag component
36
Medal component
37
// src/components/Layout.js
import React from 'react';import { Link } from 'react-router-dom';
export const Layout = props => ( <div className="app-container"> <header> <Link to="/"> <img className="logo" src="/img/logo-judo-heroes.png" /> </Link> </header> <div className="app-content">{props.children}</div> <footer> <p> This is a demo app to showcase <strong>universal Javascript</strong> with <strong>React</strong> and <strong>Express</strong>. </p> </footer> </div>);
export default Layout;
38
// src/components/IndexPage.js
import React from 'react';import { AthletePreview } from './AthletePreview';
export const IndexPage = ({ athletes }) => ( <div className="home"> <div className="athletes-selector"> { athletes.map( athleteData => <AthletePreview key={athleteData.id} {...athleteData} /> ) } </div> </div>);
export default IndexPage;39
// src/components/AthletePreview.js
import React from 'react';import { Link } from 'react-router';
export const AthletePreview = (props) => ( <Link to={`/athlete/${props.id}`}> <div className="athlete-preview"> <img src={`img/${props.image}`}/> <h2 className="name">{props.name}</h2> <span className="medals-count"> <img src="/img/medal.png"/> {props.medals.length} </span> </div> </Link>);
export default AthletePreview;
40
ROUTING
41
2 ROUTES
Index Page: /
Athlete Page: /athlete/:id
42
// src/components/App.js
import React from 'react';import { Route, Switch } from 'react-router-dom';import { Layout } from './Layout';import { IndexPage } from './IndexPage';import { AthletePage } from './AthletePage';import { NotFoundPage } from './NotFoundPage';import athletes from '../data/athletes';
// ...
export const App = () => ( <Layout> <Switch> <Route exact path="/" render={renderIndex} /> <Route exact path="/athlete/:id" render={renderAthlete} /> <Route component={NotFoundPage} /> </Switch> </Layout>);
export default App;
43
// src/components/App.js
// ...
const renderIndex = () => <IndexPage athletes={athletes} />;
const renderAthlete = ({ match, staticContext }) => { const id = match.params.id; const athlete = athletes.find(current => current.id === id); if (!athlete) { return <NotFoundPage staticContext={staticContext} />; }
return <AthletePage athlete={athlete} athletes={athletes} />;};
44
CLIENT APP
45
// src/app-client.js
import React from 'react';import { render } from 'react-dom';import { BrowserRouter as Router } from 'react-router-dom'import { App } from './components/App';
const AppClient = () => ( <Router> <App /> </Router>);
window.onload = () => { render( <AppClient />, document.getElementById('main') );};
46
HTML TEMPLATE
47
// src/views/index.ejs
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> Judo Heroes - A Universal JavaScript demo application with React </title> <link rel="stylesheet" href="/css/style.css"> </head> <body> <div id="main"><%- markup -%></div> <script src="/js/bundle.js"></script> </body></html>
48
BUILD CONFIG(BABEL + WEBPACK)
49
.babelrc
import path from 'path';
const config = { entry: { js: './src/app-client.js', }, output: { path: path.join(__dirname, 'src', 'static', 'js'), filename: 'bundle.js', }, module: { rules: [ { test: path.join(__dirname, 'src'), use: { loader: 'babel-loader' }, }, ], },};
.webpack.config.babel.js
{ "presets": ["react", "es2015"]}
50
LET'S BUILD IT!
51
// src/server.js
import path from 'path';import { Server } from 'http';import Express from 'express';
const app = new Express();const server = new Server(app);
// use ejs templatesapp.set('view engine', 'ejs');app.set('views', path.join(__dirname, 'views'));
// define the folder that will be used for static assetsapp.use(Express.static(path.join(__dirname, 'static')));
// render the index for every non-matched routeapp.get('*', (req, res) => { let markup = ''; let status = 200; return res.status(status).render('index', { markup });});
// start the serverconst port = process.env.PORT || 3000;const env = process.env.NODE_ENV || 'production';server.listen(port);
"Static" Express server
52
READY... LET'S TEST IT
53
RECAPWhat we learned so far
1. Define views combining React components
2. Add routing using React Router
3. Compile the client bundle with Babel and
Webpack
4. Run the app with a static Express server54
SERVER SIDERENDERING AND
ROUTING
55
Updating the server app// ...import { renderToString } from 'react-dom/server';import { StaticRouter as Router } from 'react-router-dom';import { App } from './components/App';
// ...app.get('*', (req, res) => { let markup = ''; let status = 200;
const context = {}; markup = renderToString( <Router location={req.url} context={context}> <App /> </Router>, );
// context.url will contain the URL to // redirect to if a <Redirect> was used if (context.url) { return res.redirect(302, context.url); }
if (context.is404) { status = 404; }
return res.status(status).render('index', { markup });});
56
THAT'S IT!
LET'S TEST AGAIN
57
RECAPWhat we learned so far
1. Create a Single Page Application with
React and React Router
2. Add server side routing and rendering
using React and React Router libraries in the
Express app58
UNIVERSAL DATA RETRIEVAL
api-proxy & async-props(COMPLETE CHAPTER in )
UNIVERSAL STATE MANAGEMENT
Redux
Node.js Design Patterns
WHERE DO WE GOfrom here...
Code: loige.link/judo-heroes-2 59
THANKS!
loige loige.colmammino
(Special thanks to , , Aleksandar Čambas & )@cirpo @andreaman87 @quasi_modal
loige.link/codemotion-rome-2017
60