Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Building and Deploying React
Applications
Boris Nadion
boris@astrails.com
@borisnadion
@borisnadion
boris@astrails.com
astrails
http://astrails.com
2005
awesome web and mobile apps
building & deploying
building & deploying
facebookincubator/create-react-app
Create React apps with no build configuration.
npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
customize
like adding css preprocessors
yarn eject / npm eject
yarn eject / npm eject
not trivial
start from scratch
setup internals
environments
✓ development
✓ production
x test
package.json
"babel": { 

"presets": [ 

[ 

"es2015", 

{ 

"modules": false 

} 

], 

"react", 

"stage-0" 

], 

"plugins": [ 

"tr...
babel-plugin-transform-class-properties
class Bork { 

//Property initializer syntax 

instanceProperty = "bork"; 

boundF...
transform-react-inline-elements
babelHelpers.jsx(Baz, { foo: "bar" }, "1");
const Hr = () => { return <hr className="hr" /...
“start":
"env NODE_ENV=development webpack-dev-server --progress --colors",
“build":
"rimraf dist &&
env NODE_ENV=producti...
webpack config
webpack config
http://imgur.com/gallery/EnAmi
const ENV = process.env.NODE_ENV; 

const VALID_ENVIRONMENTS = ['development', 'production']; 



if (!VALID_ENVIRONMENTS....
let’s look at the code
webpack.config.js + dev + prod
3 bundles
bundle.js
+ css
client.js
+ css
async.js
+ css
yarn start v0.24.4
$ env NODE_ENV=development webpack-dev-server --progress --colors
10% building modules 2/2 modules 0 ac...
dev mode demo
hot reload, async load, eslint errors
//…
import { AppContainer } from 'react-hot-loader'; 



//…

const hotRender = () => { 

render( 

<AppContainer> 

<Appl...
import asyncComponent from 'components/AsyncComponent'; 



const AsyncDashboard = asyncComponent(() => 

import('./Dashbo...
import React from 'react'; 



const asyncComponent = (getComponent) => 

class AsyncComponent extends React.Component { 
...
prod build
yarn build
yarn build v0.24.4
$ rimraf dist && env NODE_ENV=production webpack --colors && cp ./dist/* ../public/assets/
Hash: f54043...
new webpack.HashedModuleIdsPlugin(),
new ManifestPlugin({ 

fileName: 'webpack-asset-manifest.json' 

}), 



new ChunkMan...
// webpack-chunk-manifest.json
{"0":"0-b065752a37e19efffbe1.bundle.js"} 

// webpack-asset-manifest.json

{ 

"0-b065752a3...
4 bundles
vendor.js client.js 0.js client.css
html from server
/* … */
<script type=“text/javascript”>
window.apiEndPoint = "http://stage.example.com"
</script>
<link h...
<div id="root"></div>
<%= api_endpoint_from_environment %>
<%= client_application_stylesheet_tag 'client.css' %>

<%= clie...
module ClientApplicationHelper
# client_application_javascript_tag 'client.js'

def client_application_javascript_tag(bund...
serve from
• webpack dev server (for dev mode)
• same server, static assets
• static assets through CDN
• CDN direct
• wha...
awesome
almost awesome
<%= client_application_stylesheet_tag 'client.css' %>
<%= client_application_javascript_tag 'vendor.js' %> 

<%= client_ap...
current_user
req.current_user, request.user[. is_authenticated], …
module ClientApplicationHelper


def client_application_javascript_tag(bundle) 

src = 

if client_application[:use_manife...
storing manifests per user
S3, database, redis, memcache, etc
+ default manifest for the rest of the users
assets_manifest_for(current_user)[bundle]
• A/B testing
• features testing in production env
• UI experiments
• gradually ...
assets_manifest_for(current_user)[bundle]
bundles v1.12
default
bundles v1.13
debugging an issue
bundles v2.0
testing new ...
separate server and client
deployments
client lifecycle
• build: get new bundles + manifest
• deploy: upload bundles to remote
storage (S3) + warm up CDN
• relea...
awesome
almost awesome
http://www.enjoyart.com/single_posters/animals_art_photo/NoahsArkTakinoAnimalsArtPrintPoster.htm
zoo
bundles v2.0bundles v2.1bundles v2.2bundles v2.3bundles v2.4
server
compatibility
API
compatibility
develop deploy test release
new frontend version
new backend version
local staging production
release = update default manifest
for all the users
server first
server is always backward compatible
easier to maintain compatibility on server with API versioning
zoo = not an engineering issue
but administrative one
awesome
really awesome
https://github.com/astrails/rails_react_webpack
thanks to mike@astrails.com aka @mihap
http://astrails.com/blog
slides will be available
thanks!
Boris Nadion
http://astrails.com
boris@astrails.com
@borisnadion
Building and deploying React applications
Building and deploying React applications
Building and deploying React applications
Upcoming SlideShare
Loading in …5
×

Building and deploying React applications

727 views

Published on

It’s trivial today to start writing and debugging some React code, but it’s not 100% clear how to properly deploy the application, manage versions and what implications that has on the build configurations. Especially if you want to allow different versions for different users in order to perform some A/B testing, testing new features in production environment, come up with some UI experiments, or gradually roll out new features for a subset of users.

In this presentation I hopefully covered all that.

Published in: Software
  • Be the first to comment

Building and deploying React applications

  1. 1. Building and Deploying React Applications Boris Nadion boris@astrails.com @borisnadion
  2. 2. @borisnadion boris@astrails.com
  3. 3. astrails http://astrails.com
  4. 4. 2005
  5. 5. awesome web and mobile apps
  6. 6. building & deploying
  7. 7. building & deploying
  8. 8. facebookincubator/create-react-app Create React apps with no build configuration.
  9. 9. npm install -g create-react-app create-react-app my-app cd my-app/ npm start
  10. 10. customize like adding css preprocessors
  11. 11. yarn eject / npm eject
  12. 12. yarn eject / npm eject
  13. 13. not trivial
  14. 14. start from scratch
  15. 15. setup internals
  16. 16. environments ✓ development ✓ production x test
  17. 17. package.json
  18. 18. "babel": { 
 "presets": [ 
 [ 
 "es2015", 
 { 
 "modules": false 
 } 
 ], 
 "react", 
 "stage-0" 
 ], 
 "plugins": [ 
 "transform-runtime", 
 "babel-plugin-transform-class-properties", 
 "transform-object-rest-spread" 
 ], 
 "env": { 
 "production": { 
 "plugins": [ 
 "transform-react-inline-elements", 
 "transform-react-constant-elements", 
 "transform-react-remove-prop-types" 
 ] 
 } 
 } 
 }, 

  19. 19. babel-plugin-transform-class-properties class Bork { 
 //Property initializer syntax 
 instanceProperty = "bork"; 
 boundFunction = () => { 
 return this.instanceProperty; 
 } 
 
 //Static class properties 
 static staticProperty = "babelIsCool"; 
 static staticFunction = function() { 
 return Bork.staticProperty; 
 } 
 } 
 let n = { x, y, ...z }; 
 transform-object-rest-spread
  20. 20. transform-react-inline-elements babelHelpers.jsx(Baz, { foo: "bar" }, "1"); const Hr = () => { return <hr className="hr" />; }; 
 
 const _ref = <hr className="hr" />; 
 const Hr = () => { 
 return _ref; 
 }; 
 transform-react-inline-elements Baz.propTypes = {…} transform-react-remove-prop-types
  21. 21. “start": "env NODE_ENV=development webpack-dev-server --progress --colors", “build": "rimraf dist && env NODE_ENV=production webpack --colors && cp ./dist/* ../public/assets/",
  22. 22. webpack config
  23. 23. webpack config http://imgur.com/gallery/EnAmi
  24. 24. const ENV = process.env.NODE_ENV; 
 const VALID_ENVIRONMENTS = ['development', 'production']; 
 
 if (!VALID_ENVIRONMENTS.includes(ENV)) { 
 throw new Error(`${ ENV } is not valid environment!`); 
 } 
 const DEVELOPMENT_CONFIG = require('./config/webpack.dev'); 
 const PRODUCTION_CONFIG = require('./config/webpack.prod'); 
 
 const config = { 
 development: DEVELOPMENT_CONFIG, 
 production: PRODUCTION_CONFIG 
 }[ENV]; 
 
 
 const COMMON_CONFIG = { … };
 module.exports = webpackMerge.smart(COMMON_CONFIG, config); 

  25. 25. let’s look at the code webpack.config.js + dev + prod
  26. 26. 3 bundles bundle.js + css client.js + css async.js + css
  27. 27. yarn start v0.24.4 $ env NODE_ENV=development webpack-dev-server --progress --colors 10% building modules 2/2 modules 0 active Project is running at http://0.0.0.0:9001/ webpack output is served from http://localhost:9001/assets/ 404s will fallback to /index.html Hash: 9a91c0c826ebb4c40f2a Version: webpack 2.6.1 Time: 6507ms Asset Size Chunks Chunk Names 0.js 806 bytes 0 [emitted] client.js 313 kB 1 [emitted] [big] client vendor.js 1.45 MB 2 [emitted] [big] vendor 0.js.map 572 bytes 0 [emitted] client.js.map 360 kB 1 [emitted] client index.html 421 bytes [emitted] webpack: Compiled successfully.
  28. 28. dev mode demo hot reload, async load, eslint errors
  29. 29. //… import { AppContainer } from 'react-hot-loader'; 
 
 //…
 const hotRender = () => { 
 render( 
 <AppContainer> 
 <Application store={ store } /> 
 </AppContainer>, 
 document.getElementById('root') 
 ); 
 }; 
 
 hotRender(); 
 
 module.hot.accept('components/Application', hotRender); 
 
 hot reload
  30. 30. import asyncComponent from 'components/AsyncComponent'; 
 
 const AsyncDashboard = asyncComponent(() => 
 import('./Dashboard').then(module => module.default) 
 ); 
 
 export default AsyncDashboard; 
 
 async load
  31. 31. import React from 'react'; 
 
 const asyncComponent = (getComponent) => 
 class AsyncComponent extends React.Component { 
 state = { Component: null }; 
 
 componentWillMount() { 
 if (!this.state.Component) { 
 getComponent().then(Component => { 
 this.setState({ Component }); 
 }); 
 } 
 } 
 render() { 
 const { Component } = this.state; 
 if (Component) { 
 return <Component { ...this.props } />; 
 } 
 return null; 
 } 
 }; 
 
 export default asyncComponent; 
 async load
  32. 32. prod build yarn build
  33. 33. yarn build v0.24.4 $ rimraf dist && env NODE_ENV=production webpack --colors && cp ./dist/* ../public/assets/ Hash: f5404348a5a4eadca2c5 Version: webpack 2.6.1 Time: 9894ms Asset Size Chunks Chunk Names client-149e7f81934ccd4797d6.bundle.js.map 183 kB 1 [emitted] client 0-b065752a37e19efffbe1.bundle.js 318 bytes 0 [emitted] webpack-chunk-manifest.json 79 bytes [emitted] vendor-411f8db22ac4a264ff0d.bundle.js 265 kB 2 [emitted] [big] vendor client-e9da9d78d42878a4c3a5a7ab1330ea79.css 2.7 kB 1 [emitted] client 0-b065752a37e19efffbe1.bundle.js.map 2.11 kB 0 [emitted] client-149e7f81934ccd4797d6.bundle.js 24 kB 1 [emitted] client client-e9da9d78d42878a4c3a5a7ab1330ea79.css.map 120 bytes 1 [emitted] client client-149e7f81934ccd4797d6.bundle.js.gz 8.35 kB [emitted] vendor-411f8db22ac4a264ff0d.bundle.js.gz 77.9 kB [emitted] index.html 493 bytes [emitted] webpack-asset-manifest.json 468 bytes [emitted]
  34. 34. new webpack.HashedModuleIdsPlugin(), new ManifestPlugin({ 
 fileName: 'webpack-asset-manifest.json' 
 }), 
 
 new ChunkManifestPlugin({ 
 filename: 'webpack-chunk-manifest.json', 
 manifestVariable: 'webpackManifest' 
 }), 

  35. 35. // webpack-chunk-manifest.json {"0":"0-b065752a37e19efffbe1.bundle.js"} 
 // webpack-asset-manifest.json
 { 
 "0-b065752a37e19efffbe1.bundle.js": "0-b065752a37e19efffbe1.bundle.js", 
 "0-b065752a37e19efffbe1.bundle.js.map": "0-b065752a37e19efffbe1.bundle.js.map", 
 "client.css": "client-e9da9d78d42878a4c3a5a7ab1330ea79.css", 
 "client.css.map": "client-e9da9d78d42878a4c3a5a7ab1330ea79.css.map", 
 "client.js": "client-149e7f81934ccd4797d6.bundle.js", 
 "client.js.map": "client-149e7f81934ccd4797d6.bundle.js.map", 
 "vendor.js": "vendor-411f8db22ac4a264ff0d.bundle.js" 
 } 

  36. 36. 4 bundles vendor.js client.js 0.js client.css
  37. 37. html from server /* … */ <script type=“text/javascript”> window.apiEndPoint = "http://stage.example.com" </script> <link href="//xxx/client.css" rel="stylesheet" /> <script src="//xxx/vendor.js”></script> <script src="//xxx/client.js"></script> /* … */ } xxx=?
  38. 38. <div id="root"></div> <%= api_endpoint_from_environment %> <%= client_application_stylesheet_tag 'client.css' %>
 <%= client_application_javascript_tag 'vendor.js' %> 
 <%= client_application_javascript_tag 'client.js' %> 
 server template
  39. 39. module ClientApplicationHelper # client_application_javascript_tag 'client.js'
 def client_application_javascript_tag(bundle) 
 src = 
 if client_application[:use_manifest] # "client.js": "client-149e7f81934ccd4797d6.bundle.js", 
 manifest = client_application[:asset_manifest][bundle] 
 # static asset
 "/assets/#{bundle}" 
 else # dev mode
 "http://localhost:9001/assets/#{bundle}" 
 end 
 
 javascript_include_tag(src)
 end 
 
 def client_application_stylesheet_tag(bundle) # … # almost the same but no need to render in dev mode
 end 
 end 
 

  40. 40. serve from • webpack dev server (for dev mode) • same server, static assets • static assets through CDN • CDN direct • whatever
  41. 41. awesome
  42. 42. almost awesome
  43. 43. <%= client_application_stylesheet_tag 'client.css' %> <%= client_application_javascript_tag 'vendor.js' %> 
 <%= client_application_javascript_tag 'client.js' %> 
 in a context of a request
  44. 44. current_user req.current_user, request.user[. is_authenticated], …
  45. 45. module ClientApplicationHelper 
 def client_application_javascript_tag(bundle) 
 src = 
 if client_application[:use_manifest] # "client.js": "client-149e7f81934ccd4797d6.bundle.js", 
 manifest = assets_manifest_for(current_user)[bundle]
 # …
 end 
 
 javascript_include_tag(src)
 end 
 end 
 

  46. 46. storing manifests per user S3, database, redis, memcache, etc + default manifest for the rest of the users
  47. 47. assets_manifest_for(current_user)[bundle] • A/B testing • features testing in production env • UI experiments • gradually rolling out new features
  48. 48. assets_manifest_for(current_user)[bundle] bundles v1.12 default bundles v1.13 debugging an issue bundles v2.0 testing new release user with a bug in v1.12 marketing user all users
  49. 49. separate server and client deployments
  50. 50. client lifecycle • build: get new bundles + manifest • deploy: upload bundles to remote storage (S3) + warm up CDN • release: update user’s or default manifest
  51. 51. awesome
  52. 52. almost awesome
  53. 53. http://www.enjoyart.com/single_posters/animals_art_photo/NoahsArkTakinoAnimalsArtPrintPoster.htm zoo
  54. 54. bundles v2.0bundles v2.1bundles v2.2bundles v2.3bundles v2.4 server compatibility
  55. 55. API compatibility
  56. 56. develop deploy test release new frontend version new backend version local staging production
  57. 57. release = update default manifest for all the users
  58. 58. server first server is always backward compatible easier to maintain compatibility on server with API versioning
  59. 59. zoo = not an engineering issue but administrative one
  60. 60. awesome
  61. 61. really awesome
  62. 62. https://github.com/astrails/rails_react_webpack thanks to mike@astrails.com aka @mihap
  63. 63. http://astrails.com/blog slides will be available
  64. 64. thanks! Boris Nadion http://astrails.com boris@astrails.com @borisnadion

×