$30 off During Our Annual Pro Sale. View Details »

A Webpack Survival Guide for Rails Developers

A Webpack Survival Guide for Rails Developers

Moving from Webpack basics to using it in production means understanding how it works. We'll demystify how Webpack bundles assets for the browser, how it differs from the Rails asset pipeline, and highlight common challenges that may occur coming from Sprockets.

This is the talk we would have wanted to see before recently adopting Webpack in our own Rails app.

Ross Kaffenberger

April 10, 2018
Tweet

More Decks by Ross Kaffenberger

Other Decks in Technology

Transcript

  1. LOOK BEFORE YOU import
    ROSS KAFFENBERGER @rossta
    WEBPACK
    FOR RAILS DEVELOPERS
    SURV I VA L G UI D E
    A

    View Slide

  2. View Slide

  3. ROSS KAFFENBERGER @rossta
    WEBPACK
    WIT HOUT LOSIN G
    YOUR HAIR
    HOW TO

    View Slide

  4. View Slide

  5. View Slide

  6. ROSS

    View Slide

  7. R O S S
    uby pen ource oftware

    View Slide

  8. ROSS conf

    View Slide

  9. learnzillion.com/careers

    View Slide

  10. View Slide


  11. I really mean:

    View Slide

  12. View Slide

  13. #poop

    View Slide

  14. #poop











    View Slide

  15. View Slide

  16. Last year…

    View Slide

  17. View Slide

  18. View Slide

  19. 3 scenarios

    View Slide

  20. 3 scenarios
    6 problems

    View Slide

  21. 3 scenarios
    6 problems
    7 survival tips

    View Slide

  22. First…

    View Slide

  23. Sprockets Webpack

    View Slide

  24. Sprockets Webpack
    Packaging assets for the browser

    View Slide

  25. Sprockets Webpack

    View Slide

  26. Sprockets Webpack
    File concatenation

    View Slide

  27. Sprockets Webpack
    File concatenation Static module bundling

    View Slide

  28. View Slide

  29. Better dev tools

    View Slide

  30. Better dev tools
    Better dependency
    management

    View Slide

  31. Better dev tools
    Better dependency
    management
    Huge ecosystem

    View Slide

  32. Better dev tools
    Better dependency
    management
    Huge ecosystem
    ES6*

    View Slide

  33. Better dev tools
    Better dependency
    management
    Huge ecosystem
    ES6*
    Dynamic code splitting

    View Slide

  34. View Slide

  35. View Slide

  36. View Slide

  37. View Slide

  38. https://webpack.wtf

    View Slide

  39. •Webpack WTF?
    •screenshots
    •quotes

    View Slide

  40. View Slide

  41. Webpack tutorials
    SurviveJS Webpack Book - Juho Vepsäläinen
    survivejs.com/webpack
    Webpack Learning Academy - Sean Thomas Larkin
    webpack.academy
    Webpack from Nothing - David Bryant Copeland
    what-problem-does-it-solve.com/webpack/index.html

    View Slide

  42. SURVIVAL SCENARIOS

    View Slide

  43. SURVIVAL SCENARIOS
    ORGANIZING CODE

    View Slide

  44. SURVIVAL SCENARIOS
    ORGANIZING CODE
    TAMING DEPENDENCIES

    View Slide

  45. SURVIVAL SCENARIOS
    ORGANIZING CODE
    TAMING DEPENDENCIES
    PREDICTABLE CACHING

    View Slide

  46. SURVIVAL SCENARIOS
    >ORGANIZING CODE
    TAMING DEPENDENCIES
    PREDICTABLE CACHING

    View Slide

  47. Organizing code
    Directory structure

    View Slide

  48. sprockets

    View Slide

  49. sprockets
    Organizing code

    View Slide

  50. sprockets
    Organizing code
    app/
    assets/
    javascripts/
    application.js
    admin.js
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js

    View Slide

  51. sprockets
    Organizing code
    app/
    assets/
    javascripts/
    application.js
    admin.js
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    public/
    assets/
    javascripts/
    application-abc123...js
    admin-def456...jss

    View Slide

  52. sprockets
    Organizing code
    app/
    assets/
    javascripts/
    application.js
    admin.js
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    public/
    assets/
    javascripts/
    application-abc123...js
    admin-def456...jss
    Rails.application.configure do
    config.assets.precompile += %w(admin.js)
    end

    View Slide

  53. webpacker+

    View Slide

  54. webpacker+
    Organizing code
    app/
    assets/
    javascripts/
    application.js
    admin.js
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js

    View Slide

  55. webpacker+
    app/
    webpacker+
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/
    Organizing code

    View Slide

  56. webpacker+
    app/
    webpacker+
    entry
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/
    Organizing code

    View Slide

  57. webpacker+
    app/ public/
    assets/
    javascripts/
    ...
    packs/
    application-abc123...js
    admin-def456...js
    webpacker+
    entry
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/
    Organizing code

    View Slide

  58. webpacker+
    app/ public/
    assets/
    javascripts/
    ...
    packs/
    application-abc123...js
    admin-def456...js
    webpacker+
    output
    entry
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/
    Organizing code

    View Slide

  59. webpacker+
    app/ public/
    assets/
    javascripts/
    ...
    packs/
    application-abc123...js
    admin-def456...js
    webpacker+
    output
    entry
    “packs” auto-configured
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/
    Organizing code

    View Slide

  60. Organizing code

    View Slide

  61. Organizing code

    View Slide

  62. Organizing code

    View Slide

  63. Organizing code

    View Slide

  64. Organizing code

    View Slide

  65. webpacker+
    app/
    webpacker+
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/
    Organizing code

    View Slide

  66. Organizing code webpacker+
    app/
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/

    View Slide

  67. Organizing code

    webpacker+
    app/
    assets/
    javascripts/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    application.js
    admin.js
    ...
    javascript/
    packs/

    View Slide

  68. Organizing code webpacker+
    $ bin/webpack

    View Slide

  69. Organizing code webpacker+
    $ bin/webpack

    View Slide

  70. Organizing code webpacker+
    $ bin/webpack

    View Slide

  71. Organizing code webpacker+
    $ bin/webpack

    View Slide

  72. Quick fix
    Keep entry js separate

    View Slide

  73. webpacker+
    app/
    assets/
    javascripts/
    ...
    javascript/
    my_function.js
    my_module.js
    components/
    component_a.js
    component_b.js
    packs/
    application.js
    admin.js
    public/
    assets/
    javascripts/
    ...
    packs/
    application-abc123...js
    admin-def456...js
    Organizing code webpacker+

    View Slide

  74. Organizing code webpacker+

    View Slide

  75. Organizing code webpacker+

    View Slide

  76. Survival tip #1
    Don’t assume Webpacker
    conventions match
    Sprockets conventions

    View Slide

  77. Organizing code
    How to “require_tree”?

    View Slide

  78. sprockets
    Organizing code
    // app/assets/javascripts/application.js
    //= require_tree ./some_directory

    View Slide

  79. webpacker+
    Organizing code
    // app/javascript/packs/application.js
    import 'some_directory'

    View Slide

  80. webpacker+
    require.context
    Organizing code
    // app/javascript/packs/application.js
    import 'some_directory'

    View Slide

  81. webpacker+
    require.context
    Organizing code
    // app/javascript/some_directory/index.js
    const requireModule = require.context('.', true, /\.js$/)
    // app/javascript/packs/application.js
    import 'some_directory'

    View Slide

  82. webpacker+
    require.context
    Organizing code
    // app/javascript/some_directory/index.js
    const requireModule = require.context('.', true, /\.js$/)
    // app/javascript/packs/application.js
    import 'some_directory'
    requireModule.keys() // ['./module_a.js', ‘./module_b.js', ...]
    .filter(filename => filename !== './index.js')
    .forEach(requireModule)

    View Slide

  83. webpacker+
    Organizing code
    // app/javascript/packs/application.js
    import { SomeComponent } from 'some_directory'

    View Slide

  84. webpacker+
    Organizing code
    // app/javascript/some_directory/index.js
    import camelCase from 'lodash/camelCase'
    const requireModule = require.context('.', false, /\.js$/)
    const modules = {}
    requireModule.keys().forEach(fileName => {
    if (fileName === './index.js') return
    const moduleName = camelCase(
    fileName.replace(/(\.\/|\.js)/g, '')
    )
    modules[moduleName] = requireModule(fileName)
    })
    export default modules

    View Slide

  85. webpacker+
    Organizing code
    // app/javascript/some_directory/index.js
    import camelCase from 'lodash/camelCase'
    const requireModule = require.context('.', false, /\.js$/)
    const modules = {}
    requireModule.keys().forEach(fileName => {
    if (fileName === './index.js') return
    const moduleName = camelCase(
    fileName.replace(/(\.\/|\.js)/g, '')
    )
    modules[moduleName] = requireModule(fileName)
    })
    export default modules

    View Slide

  86. webpacker+
    Organizing code
    // app/javascript/some_directory/index.js
    import camelCase from 'lodash/camelCase'
    const requireModule = require.context('.', false, /\.js$/)
    const modules = {}
    requireModule.keys().forEach(fileName => {
    if (fileName === './index.js') return
    const moduleName = camelCase(
    fileName.replace(/(\.\/|\.js)/g, '')
    )
    modules[moduleName] = requireModule(fileName)
    })
    export default modules

    View Slide

  87. webpacker+
    Organizing code
    // app/javascript/some_directory/index.js
    import camelCase from 'lodash/camelCase'
    const requireModule = require.context('.', false, /\.js$/)
    const modules = {}
    requireModule.keys().forEach(fileName => {
    if (fileName === './index.js') return
    const moduleName = camelCase(
    fileName.replace(/(\.\/|\.js)/g, '')
    )
    modules[moduleName] = requireModule(fileName)
    })
    export default modules

    View Slide

  88. webpacker+
    Organizing code
    // app/javascript/some_directory/index.js
    import camelCase from 'lodash/camelCase'
    const requireModule = require.context('.', false, /\.js$/)
    const modules = {}
    requireModule.keys().forEach(fileName => {
    if (fileName === './index.js') return
    const moduleName = camelCase(
    fileName.replace(/(\.\/|\.js)/g, '')
    )
    modules[moduleName] = requireModule(fileName)
    })
    export default modules

    View Slide

  89. View Slide

  90. View Slide

  91. Survival tip #2
    In Webpack, accept
    control > friendliness

    View Slide

  92. SURVIVAL SCENARIOS
    ORGANIZING CODE✔
    >TAMING DEPENDENCIES
    PREDICTABLE CACHING

    View Slide

  93. Taming dependencies
    Code splitting

    View Slide

  94. sprockets
    Taming dependencies
    app/
    assets/
    javascripts/
    application.js
    vendor.js
    sprockets

    View Slide

  95. sprockets
    Taming dependencies
    app/
    assets/
    javascripts/
    application.js
    vendor.js
    sprockets
    // app/views/layouts/application.html.erb
    <%= javascript_include_tag "vendor" %>
    <%= javascript_include_tag "application" %>

    View Slide

  96. sprockets
    Taming dependencies
    app/
    assets/
    javascripts/
    application.js
    vendor.js
    sprockets
    // app/assets/javascripts/vendor.js
    //= require jquery-rails
    // app/views/layouts/application.html.erb
    <%= javascript_include_tag "vendor" %>
    <%= javascript_include_tag "application" %>

    View Slide

  97. sprockets
    Taming dependencies
    app/
    assets/
    javascripts/
    application.js
    vendor.js
    sprockets
    // app/assets/javascripts/vendor.js
    //= require jquery-rails
    // app/assets/javascripts/application.js
    //= require chosen-js
    //= require slick-carousel
    // app/views/layouts/application.html.erb
    <%= javascript_include_tag "vendor" %>
    <%= javascript_include_tag "application" %>

    View Slide

  98. sprockets
    Taming dependencies
    app/
    assets/
    javascripts/
    application.js
    vendor.js
    sprockets
    // app/assets/javascripts/vendor.js
    //= require jquery-rails
    // app/assets/javascripts/application.js
    //= require chosen-js
    //= require slick-carousel
    manual code
    splitting
    // app/views/layouts/application.html.erb
    <%= javascript_include_tag "vendor" %>
    <%= javascript_include_tag "application" %>

    View Slide

  99. webpacker+
    Taming dependencies
    app/
    javascript/
    packs/
    application.js
    vendor.js

    View Slide

  100. webpacker+
    Taming dependencies
    app/
    javascript/
    packs/
    application.js
    vendor.js
    // app/views/layouts/application.html.erb
    <%= javascript_pack_tag "vendor" %>
    <%= javascript_pack_tag "application" %>

    View Slide

  101. webpacker+
    Taming dependencies
    app/
    javascript/
    packs/
    application.js
    vendor.js
    // app/javascript/packs/vendor.js
    import 'jquery'
    // app/views/layouts/application.html.erb
    <%= javascript_pack_tag "vendor" %>
    <%= javascript_pack_tag "application" %>

    View Slide

  102. webpacker+
    Taming dependencies
    app/
    javascript/
    packs/
    application.js
    vendor.js
    // app/javascript/packs/vendor.js
    import 'jquery'
    // app/javascript/packs/application.js
    import 'chosen-js'
    import 'slick-carousel'
    // app/views/layouts/application.html.erb
    <%= javascript_pack_tag "vendor" %>
    <%= javascript_pack_tag "application" %>

    View Slide

  103. webpacker+
    Taming dependencies
    app/
    javascript/
    packs/
    application.js
    vendor.js
    // app/javascript/packs/vendor.js
    import 'jquery'
    // app/javascript/packs/application.js
    import 'chosen-js'
    import 'slick-carousel'
    // app/views/layouts/application.html.erb
    <%= javascript_pack_tag "vendor" %>
    <%= javascript_pack_tag "application" %>
    We added “$”
    to global scope

    View Slide

  104. webpacker+
    Taming dependencies
    // app/javascript/packs/vendor.js
    import 'jquery'

    View Slide

  105. webpacker+
    Taming dependencies
    // app/javascript/packs/application.js
    // app/javascript/packs/vendor.js
    import 'jquery'

    View Slide

  106. webpacker+
    Taming dependencies
    // app/javascript/packs/application.js
    // app/javascript/packs/application.js
    import 'chosen-js'
    // app/javascript/packs/vendor.js
    import 'jquery'

    View Slide

  107. webpacker+
    > typeof $.fn.chosen
    “function"
    Taming dependencies
    // app/javascript/packs/application.js
    // app/javascript/packs/application.js
    import 'chosen-js'
    // app/javascript/packs/vendor.js
    import 'jquery'

    View Slide

  108. webpacker+
    > typeof $.fn.chosen
    “function"
    Taming dependencies
    // app/javascript/packs/application.js
    // app/javascript/packs/application.js
    import 'chosen-js'
    // app/javascript/packs/application.js
    import 'chosen-js'
    import 'slick-carousel'
    // app/javascript/packs/vendor.js
    import 'jquery'

    View Slide

  109. webpacker+
    > typeof $.fn.chosen
    “function"
    > typeof $.fn.chosen
    “function"
    > typeof $.fn.slick
    "function"
    Taming dependencies
    // app/javascript/packs/application.js
    // app/javascript/packs/application.js
    import 'chosen-js'
    // app/javascript/packs/application.js
    import 'chosen-js'
    import 'slick-carousel'
    // app/javascript/packs/vendor.js
    import 'jquery'

    View Slide

  110. webpacker+
    > typeof $.fn.chosen
    “function"
    > typeof $.fn.chosen
    “function"
    > typeof $.fn.slick
    "function"
    Taming dependencies
    // app/javascript/packs/application.js
    // app/javascript/packs/application.js
    import 'chosen-js'
    // app/javascript/packs/application.js
    import 'chosen-js'
    import 'slick-carousel'
    > typeof $.fn.chosen
    "function"
    > typeof $.fn.slick
    "function"
    > typeof $.fn.chosen
    "undefined"
    // app/javascript/packs/vendor.js
    import 'jquery'

    View Slide

  111. View Slide

  112. View Slide

  113. webpacker+
    Taming dependencies
    $ yarn add webpack-bundle-analyzer

    View Slide

  114. webpacker+
    Taming dependencies
    // config/webpack/development.js
    const environment = require('./environment')
    const BundleAnalyzerPlugin =
    require(‘webpack-bundle-analyzer').BundleAnalyzerPlugin
    environment.plugins.append(
    'BundleAnalyzerPlugin',
    new BundleAnalyzerPlugin()
    )
    $ yarn add webpack-bundle-analyzer

    View Slide

  115. webpacker+
    Taming dependencies

    View Slide

  116. webpacker+
    Taming dependencies

    View Slide

  117. webpacker+
    Taming dependencies

    View Slide

  118. webpacker+
    Taming dependencies

    View Slide

  119. webpacker+
    Taming dependencies

    View Slide

  120. webpacker+
    Taming dependencies
    import 'chosen-js'

    View Slide

  121. webpacker+
    (function() {
    // ...
    $ = jQuery
    $.fn.extend({
    chosen: function(options) {
    // ...
    }
    })
    // ...
    }).call(this)
    Taming dependencies
    import 'chosen-js'

    View Slide

  122. webpacker+
    (function() {
    // ...
    $ = jQuery
    $.fn.extend({
    chosen: function(options) {
    // ...
    }
    })
    // ...
    }).call(this)
    Taming dependencies
    import 'chosen-js'
    assumes “jQuery” in global scope

    View Slide

  123. webpacker+
    Taming dependencies
    import 'slick-carousel'

    View Slide

  124. webpacker+
    Taming dependencies
    (function(factory) {
    if (typeof exports !== 'undefined') {
    module.exports = factory(require('jquery'))
    } else if (typeof define === 'function' && define.amd) {
    define(['jquery'], factory)
    } else {
    factory(jQuery)
    }
    })(function($) {
    // library code
    })
    import 'slick-carousel'

    View Slide

  125. webpacker+
    Taming dependencies
    (function(factory) {
    if (typeof exports !== 'undefined') {
    module.exports = factory(require('jquery'))
    } else if (typeof define === 'function' && define.amd) {
    define(['jquery'], factory)
    } else {
    factory(jQuery)
    }
    })(function($) {
    // library code
    })
    import 'slick-carousel'
    “Universal Module Definition”

    View Slide

  126. webpacker+
    Taming dependencies
    (function(factory) {
    if (typeof exports !== 'undefined') {
    module.exports = factory(require('jquery'))
    } else if (typeof define === 'function' && define.amd) {
    define(['jquery'], factory)
    } else {
    factory(jQuery)
    }
    })(function($) {
    // library code
    })
    import 'slick-carousel'
    “Universal Module Definition”
    adds jQuery as a dependency

    View Slide

  127. webpacker+
    Taming dependencies

    View Slide

  128. Quick fix
    Code splitting?
    Let Webpack do the work

    View Slide

  129. webpacker+
    Taming dependencies
    // config/webpack/environment.js
    const webpack = require('webpack')
    const { environment } = require('@rails/webpacker')
    environment.plugins.append(
    'CommonsChunkVendor',
    new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    })
    )

    View Slide

  130. webpacker+
    Taming dependencies
    // config/webpack/environment.js
    const webpack = require('webpack')
    const { environment } = require('@rails/webpacker')
    environment.plugins.append(
    'CommonsChunkVendor',
    new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    })
    ) Extracts common modules into one bundle

    View Slide

  131. webpacker+
    Taming dependencies
    // config/webpack/environment.js
    const webpack = require('webpack')
    const { environment } = require('@rails/webpacker')
    environment.plugins.append(
    'CommonsChunkVendor',
    new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    })
    ) Extracts common modules into one bundle
    Changes in Webpack 4

    View Slide

  132. webpacker+
    Taming dependencies

    View Slide

  133. webpacker+
    Taming dependencies

    View Slide

  134. Survival tip #3
    Know your dependencies

    View Slide

  135. Taming dependencies
    Module shimming

    View Slide

  136. Taming dependencies
    (Applying transformations)

    View Slide

  137. webpacker+
    Taming dependencies

    View Slide

  138. webpacker+
    Taming dependencies
    $ yarn add expose-loader

    View Slide

  139. webpacker+
    Taming dependencies
    // config/webpack/environment.js
    const { environment } = require('@rails/webpacker')
    environment.loaders.append('jquery', {
    test: require.resolve('jquery'),
    use: [{
    loader: 'expose-loader',
    options: '$',
    }],
    })
    $ yarn add expose-loader

    View Slide

  140. webpacker+
    Taming dependencies
    // config/webpack/environment.js
    const { environment } = require('@rails/webpacker')
    environment.loaders.append('jquery', {
    test: require.resolve('jquery'),
    use: [{
    loader: 'expose-loader',
    options: '$',
    }],
    })
    $ yarn add expose-loader

    View Slide

  141. webpacker+
    Taming dependencies
    // config/webpack/environment.js
    const { environment } = require('@rails/webpacker')
    environment.loaders.append('jquery', {
    test: require.resolve('jquery'),
    use: [{
    loader: 'expose-loader',
    options: '$',
    }],
    })
    $ yarn add expose-loader
    exposes “$” to global scope

    View Slide

  142. webpacker+
    (function() {
    // ...
    $ = jQuery
    $.fn.extend({
    chosen: function(options) {
    // ...
    }
    })
    // ...
    }).call(this)
    Taming dependencies
    import 'chosen-js'

    View Slide

  143. webpacker+
    (function() {
    // ...
    $ = jQuery
    $.fn.extend({
    chosen: function(options) {
    // ...
    }
    })
    // ...
    }).call(this)
    Taming dependencies
    import 'chosen-js'

    View Slide

  144. webpacker+
    Taming dependencies
    $ yarn add imports-loader

    View Slide

  145. webpacker+
    // config/webpack/environment.js
    const { environment } = require('@rails/webpacker')
    environment.loaders.append('chosen-js', {
    test: require.resolve('chosen-js'),
    use: [{
    loader: 'imports-loader',
    options: 'jQuery=jquery,$=jquery,this=>window',
    }]
    })
    Taming dependencies
    $ yarn add imports-loader

    View Slide

  146. webpacker+
    // config/webpack/environment.js
    const { environment } = require('@rails/webpacker')
    environment.loaders.append('chosen-js', {
    test: require.resolve('chosen-js'),
    use: [{
    loader: 'imports-loader',
    options: 'jQuery=jquery,$=jquery,this=>window',
    }]
    })
    Taming dependencies
    $ yarn add imports-loader

    View Slide

  147. webpacker+
    // config/webpack/environment.js
    const { environment } = require('@rails/webpacker')
    environment.loaders.append('chosen-js', {
    test: require.resolve('chosen-js'),
    use: [{
    loader: 'imports-loader',
    options: 'jQuery=jquery,$=jquery,this=>window',
    }]
    })
    Taming dependencies
    inject dependencies into legacy modules
    $ yarn add imports-loader

    View Slide

  148. Survival tip #4
    Legacy dependencies?
    fix or fire

    View Slide

  149. SURVIVAL SCENARIOS
    ORGANIZING CODE✔
    TAMING DEPENDENCIES✔
    >PREDICTABLE CACHING

    View Slide

  150. Predictable caching
    Fingerprints

    View Slide

  151. webpacker+
    Predictable caching

    View Slide

  152. webpacker+
    Predictable caching

    View Slide

  153. webpacker+
    Predictable caching
    // app/javascript/packs/application.js
    import myFunction from 'my_function'
    myFunction()

    View Slide

  154. webpacker+
    Predictable caching
    // app/javascript/packs/application.js
    import myFunction from 'my_function'
    myFunction()

    View Slide

  155. webpacker+
    new
    dependency
    Predictable caching
    // app/javascript/packs/application.js
    import myFunction from 'my_function'
    import myModule from 'my_module'
    myFunction()
    console.log('MyModule', myModule)

    View Slide

  156. webpacker+
    Predictable caching
    // app/javascript/packs/application.js
    import myFunction from 'my_function'
    import myModule from 'my_module'
    myFunction()
    console.log('MyModule', myModule)
    // app/javascript/my_module.js
    export default {
    name: 'MyModule',
    render() {
    return 'MyModule'
    },
    }

    View Slide

  157. webpacker+
    Predictable caching
    // app/javascript/packs/application.js
    import myFunction from 'my_function'
    import myModule from 'my_module'
    myFunction()
    console.log('MyModule', myModule)

    View Slide

  158. webpacker+
    Predictable caching
    // app/javascript/packs/application.js
    import myFunction from 'my_function'
    import myModule from 'my_module'
    myFunction()
    console.log('MyModule', myModule)

    View Slide

  159. webpacker+
    application-6abd5649df983f5ce62c.js
    vendor-1f142b407ddab8388a61.js
    before…
    Predictable caching

    View Slide

  160. webpacker+
    application-6abd5649df983f5ce62c.js
    vendor-1f142b407ddab8388a61.js
    application-3f225177efeb4e965518.js
    vendor-0434e3b09308803b8702.js
    before…
    after…
    Predictable caching

    View Slide

  161. webpacker+
    application-6abd5649df983f5ce62c.js
    vendor-1f142b407ddab8388a61.js
    application-6abd5649df983f5ce62c.js
    vendor-1f142b407ddab8388a61.js
    application-3f225177efeb4e965518.js
    vendor-0434e3b09308803b8702.js
    application-3f225177efeb4e965518.js
    vendor-0434e3b09308803b8702.js
    before…
    after…
    Predictable caching

    View Slide

  162. webpacker+
    application-6abd5649df983f5ce62c.js
    vendor-1f142b407ddab8388a61.js
    application-6abd5649df983f5ce62c.js
    vendor-1f142b407ddab8388a61.js
    application-3f225177efeb4e965518.js
    vendor-0434e3b09308803b8702.js
    application-3f225177efeb4e965518.js
    vendor-0434e3b09308803b8702.js
    application-3f225177efeb4e965518.js
    vendor-0434e3b09308803b8702.js
    application-6abd5649df983f5ce62c.js
    vendor-1f142b407ddab8388a61.js
    before…
    after…
    Surprise! Both bundles changed!
    Predictable caching

    View Slide

  163. View Slide

  164. View Slide

  165. webpacker+
    Anatomy of a bundle
    .js
    A brief cartoon interlude…

    View Slide

  166. webpacker+
    Anatomy of a bundle
    .js
    Entry

    View Slide

  167. webpacker+
    Anatomy of a bundle
    .js
    .js
    .js
    .js
    .jpg .css
    .js
    Entry

    View Slide

  168. webpacker+
    Anatomy of a bundle

    View Slide

  169. webpacker+
    Anatomy of a bundle

    View Slide

  170. webpacker+
    Anatomy of a bundle

    View Slide

  171. webpacker+
    Runtime
    typical-bundle.js
    Anatomy of a bundle

    View Slide

  172. webpacker+
    Runtime
    typical-bundle.js
    0 1 2 3 4 5 6
    Anatomy of a bundle

    View Slide

  173. webpacker+
    Anatomy of a bundle
    vendor.js application.js

    View Slide

  174. webpacker+
    vendor.js
    0 1 2 3 4
    application.js
    5 6 7 8
    Anatomy of a bundle

    View Slide

  175. webpacker+
    vendor.js
    0 1 2 3 4
    application.js
    5 6 7 8 9
    Anatomy of a bundle

    View Slide

  176. webpacker+
    vendor.js
    0 1 2 3 4
    application.js
    5 6 7 8 9
    Anatomy of a bundle

    View Slide

  177. Quick fix
    More configuration
    (yay)

    View Slide

  178. webpacker+
    // config/webpack/environment.js
    const webpack = require('webpack')
    const { environment } = require('@rails/webpacker')
    environment.plugins.append(
    'CommonsChunkVendor',
    new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    })
    )
    Predictable caching

    View Slide

  179. webpacker+
    // config/webpack/environment.js … continued
    Predictable caching

    View Slide

  180. webpacker+
    // config/webpack/environment.js … continued
    Predictable caching
    // config/webpack/environment.js … continued
    environment.plugins.append(
    'CommonsChunkManifest',
    new webpack.optimize.CommonsChunkPlugin({
    name: 'runtime',
    minChunks: Infinity,
    }),
    ) extract the runtime into its own bundle

    View Slide

  181. webpacker+
    // config/webpack/environment.js … continued
    Predictable caching
    // config/webpack/environment.js … continued
    environment.plugins.append(
    'CommonsChunkManifest',
    new webpack.optimize.CommonsChunkPlugin({
    name: 'runtime',
    minChunks: Infinity,
    }),
    )
    use consistent module ids for runtime
    extract the runtime into its own bundle
    environment.plugins.append(
    'HashedModuleIds',
    new webpack.HashedModuleIdsPlugin()
    )

    View Slide

  182. webpacker+
    vendor.js
    0 1 2 3 4
    application.js
    5 6 7 8
    Anatomy of a bundle

    View Slide

  183. webpacker+
    vendor.js
    FA C1 3D A6 33
    application.js
    2F EC 4B D8
    Anatomy of a bundle
    runtime.js

    View Slide

  184. Survival tip #5
    Understand the runtime

    View Slide

  185. View Slide

  186. View Slide

  187. Predictable caching
    Asychronous bundles

    View Slide

  188. webpacker+
    import $ from 'jquery'
    $('.pdf-canvas').each(() => {
    const url = $(this).data('url')
    Predictable caching
    PDFJS.getDocument(url).then(pdf => {
    // ... render pdf data to canvas
    })
    })
    import PDFJS from 'pdfjs/webpack'

    View Slide

  189. webpacker+
    import $ from 'jquery'
    $('.pdf-canvas').each(() => {
    const url = $(this).data('url')
    Predictable caching
    PDFJS.getDocument(url).then(pdf => {
    // ... render pdf data to canvas
    })
    })
    import PDFJS from 'pdfjs/webpack'

    View Slide

  190. webpacker+
    Predictable caching
    import $ from 'jquery'
    $('.pdf-canvas').each(() => {
    const url = $(this).data('url')
    PDFJS.getDocument(url).then(pdf => {
    // ... render pdf data to canvas
    })
    import('pdfjs/webpack').then((PDFJS) => {
    })
    })

    View Slide

  191. webpacker+
    Predictable caching
    import $ from 'jquery'
    $('.pdf-canvas').each(() => {
    const url = $(this).data('url')
    PDFJS.getDocument(url).then(pdf => {
    // ... render pdf data to canvas
    })
    import('pdfjs/webpack').then((PDFJS) => {
    })
    })

    View Slide

  192. webpacker+
    Predictable caching
    import $ from 'jquery'
    $('.pdf-canvas').each(() => {
    const url = $(this).data('url')
    PDFJS.getDocument(url).then(pdf => {
    // ... render pdf data to canvas
    })
    import('pdfjs/webpack').then((PDFJS) => {
    })
    })
    Webpack’s sweet spot!

    View Slide

  193. webpacker+
    Predictable caching
    import( 'pdfjs/webpack').then(…)

    View Slide

  194. webpacker+
    Predictable caching
    import( 'pdfjs/webpack').then(…)
    /* webpackChunkName: "pdfjs" */

    View Slide

  195. webpacker+
    Predictable caching
    import(
    Use a “chunk name” for consistent bundle caching
    'pdfjs/webpack').then(…)
    /* webpackChunkName: "pdfjs" */

    View Slide

  196. webpacker+
    Predictable caching
    import(
    Use a “chunk name” for consistent bundle caching
    'pdfjs/webpack').then(…)
    /* webpackChunkName: "pdfjs" */

    View Slide

  197. webpacker+
    Predictable caching
    import(
    Use a “chunk name” for consistent bundle caching
    'pdfjs/webpack').then(…)
    /* webpackChunkName: "pdfjs" */

    View Slide

  198. Survival tip #6
    Prioritize async bundles

    View Slide

  199. SURVIVAL SCENARIOS
    ORGANIZING CODE✔
    TAMING DEPENDENCIES✔
    PREDICTABLE CACHING✔

    View Slide

  200. www.codementor.io/help/rails-with-webpack-not-for-everyone-feucqq83z

    View Slide

  201. View Slide

  202. Survival tip #7
    Webpack and Sprockets:
    fundamentally different

    View Slide

  203. Prerequisite
    Positive attitude

    View Slide

  204. @rossta
    rossta.net/talks
    [email protected]

    View Slide