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

290 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

×