Azat Mardan
Azat Mardan
Twitter: @azat_co Blog: webapplog.com
- Work: Technology Fellow at Capital One
- Experience: FDIC, NIH, DocuSign, HackReactor and Storify
- Books: Practical Node.js, Pro Express.js, Express.js API and 8 others
- Teach: NodeProgram.com
- SEO
- Loading...
- Code re-use
- Performance
- Full stack
- Isomorphic
- Universal
Code re-use, i.e., templates between server and browser
https://www.npmjs.com/package/jade-browser
Make SPA load data faster
SPA<->node+cache<->API
- Backbone
- Node
- Express
- CoffeeScript
- Grunt
- Browserify
Prefetch data from API, save in cache for SPA.
# home page - dashboard
mapping['/home'] =
name: 'dashboard'
bodyClass: 'dashboard'
pageTitle: 'Home'
helpLink: 'http://www.docusign.com/DocuSignHelp/DocuSignHelp.htm'
clientAssets:
js: [ '/js/bundle-home.js', '/js/bundle-send.js', '/js/bundle-documents-page.js'
'/js/tutorials.js'
]
layoutView: 'HomeLayoutView'
data:
signatures:
type: 'SignatureList'
views: [ ]
order: 1
profile:
type: 'Profile'
views: [ 'ProfileView' ]
reuse: false
attachRefs: [ 'signatures' ]
order: 2
But React makes it easier
React is a UI library
React UIs are simple functions where input generates HTML elements in a predictable way (input->output)
One-way binding: views are simple functions of props
https://github.com/azat-co/react/tree/master/ch8/board-react2
let React = require('react')
let ReactDOMServer = require('react-dom/server')
class MyComponent extends React.Component {
render() {
return <div>Hello World</div>
}
}
ReactDOMServer.renderToString(<MyComponent />)
TK check
let app = express()
app.set('views', __dirname + '/views')
app.set('view engine', 'jsx')
app.engine('jsx', require('express-react-views').createEngine())
Flash of content
<script>
window.__data=${JSON.stringify(data)}
</script>
ReactDOM.render(<MyComponent messages={window.__data}/>, document.getElementById('content'))
// app.js - loaded only on browser
React = require('react')
ReactDOM = require('react-dom')
{Header, Footer, MessageBoard} = require('./components.js')
ReactDOM.render(<Header />, document.getElementById('header'))
ReactDOM.render(<Footer />, document.getElementById('footer'))
ReactDOM.render(<MessageBoard messages={messages}/>, document.getElementById('message-board'))
app.get('/', function(req, res, next){
req.messages.find({}, {sort: {_id: -1}}).toArray(function(err, docs){
if (err) return next(err)
res.render('index', {
header: ReactDOMServer.renderToString(Header()),
footer: ReactDOMServer.renderToString(Footer()),
messageBoard: ReactDOMServer.renderToString(MessageBoard({messages: docs})),
props: `<script type="text/javascript">var messages=${JSON.stringify(docs)}</script>`
})
})
})
<div>{{{props}}}</div>
<div class="row-fluid">
<div class="span12">
<div id="content">
<div class="row-fluid" id="message-board" />{{{messageBoard}}}</div>
</div>
</div>
Routes, i.e., /users
not /#users
Serve index.html
for all routes
const express = require('express')
const path = require('path')
const port = process.env.PORT || 8080
const app = express()
app.use(express.static(__dirname + '/public'))
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})
app.listen(port)
console.log("server started on port " + port)
import { renderToString } from 'react-dom/server'
import { match, RouterContext } from 'react-router'
import routes from './routes'
serve((req, res) => {
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message)
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
res.status(200).send(renderToString(<RouterContext {...renderProps} />))
} else {
res.status(404).send('Not found')
}
})
})
render(<Router history={history} routes={routes} />, mountNode)
import { match, Router } from 'react-router'
import routes from './routes'
match({ history, routes }, (error, redirectLocation, renderProps) => {
render(<Router {...renderProps} />, mountNode)
})
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import counterApp from './reducers'
import App from './containers/App'
const app = Express()
app.use((req, res) =>{
const store = createStore(counterApp)
const html = renderToString(
<Provider store={store}>
<App />
</Provider>
)
res.send(renderHtml(html, store.getState()))
})
app.listen(3000)
let renderHtml = (html, initialState) => {
return `<div id="root">${html}</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}
</script>
<script src="/static/bundle.js"></script>`
}
const initialState = window.__INITIAL_STATE__
const store = createStore(counterApp, initialState)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
const counter = parseInt(req.params.counter, 10) || 0
let initialState = { counter }
const store = createStore(counterApp, initialState)
- Render Components on the server
- Make sure you get the same data to the browser
- Use nice URLs with React Router
- Use Redux on the server
Example: https://github.com/azat-co/react-quickly/tree/master/projects/autocomplete
Slides: https://github.com/azat-co/universal-web
React book: http://reactquickly.co
https://github.com/azat-co/react-quickly/tree/master/ch16/message-board