Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Half Stack Fest: Webpack

Jack Franklin
November 18, 2016

Half Stack Fest: Webpack

Jack Franklin

November 18, 2016
Tweet

More Decks by Jack Franklin

Other Decks in Technology

Transcript

  1. webpack does what the browser would do if given your

    app: » find all images in your CSS and download them » parse your JavaScript dependencies and download them » parse all CSS for imports and download them
  2. webpack can do this right at the beginning And create

    a more optimised bundle, saving the browser (and user) time
  3. A module Any file that your application uses. Not just

    JavaScript. CSS, images, text files, anything.
  4. A loader A function that takes a file's source and

    transforms it to return a new source file
  5. webpack.config.js var path = require('path') module.exports = { // where

    should it look? entry: path.resolve('src', 'main.js'), // where should it output to? output: { path: path.resolve(__dirname, 'dist'), filename: 'main.js', publicPath: '/dist/' } }
  6. run webpack Hash: c09c87fc891968a374a6 Version: webpack 2.1.0-beta.26 Time: 70ms Asset

    Size Chunks Chunk Names main.js 2.63 kB 0 [emitted] main [0] ./src/main.js 194 bytes {0} [built]
  7. webpack is watching the files… Hash: c09c87fc891968a374a6 Version: webpack 2.1.0-beta.26

    Time: 103ms Asset Size Chunks Chunk Names main.js 2.63 kB 0 [emitted] main [0] ./src/main.js 194 bytes {0} [built] Hash: d9aad0a1ca8f706975ad Version: webpack 2.1.0-beta.26 Time: 11ms Asset Size Chunks Chunk Names main.js 2.6 kB 0 [emitted] main [0] ./src/main.js 163 bytes {0} [built]
  8. webpack-dev-server Project is running at http://localhost:8080/ webpack output is served

    from /dist/ Hash: 1d366a627611ba06845c Version: webpack 2.1.0-beta.26 Time: 947ms Asset Size Chunks Chunk Names main.js 247 kB 0 [emitted] main chunk {0} main.js (main) 233 kB [entry] [rendered] [0] ./~/inherits/inherits_browser.js 672 bytes {0} [built] [1] (webpack)/buildin/global.js 506 bytes {0} [built] [2] ./~/debug/browser.js 3.76 kB {0} [built] [3] ./~/process/browser.js 5.3 kB {0} [built] ...
  9. Loaders fetch('https://api.github.com/users/jackfranklin') .then(function(d) { return d.json() }) .then(function(data) { console.log('Got

    github data', data) }) Would be much nicer as: const username = 'jackfranklin' fetch(`https://api.github.com/users/${username}`) .then(d => d.json()) .then(data => console.log('Got github data', data))
  10. { // apply this rule to any files that end

    in ".js" test: /\.js$/, // only look for files in the src directory include: path.resolve('src'), // configure the loaders for this rule use: [{ }] }
  11. { test: /\.js$/, include: path.resolve('src'), use: [{ // for files

    that this rule applies to // run the babel-loader against them loader: 'babel-loader', // specific options for the babel loader options: { presets: ['es2015'] } }] }
  12. <div id="app"> <form id="github-form"> <input type="text" id="input-box" value="jackfranklin" /> <button

    type="submit">Go!</button> </form> <div id="results"></div> </div> <script src="dist/main.js"></script>
  13. const form = document.getElementById('github-form') form.addEventListener('submit', e => { e.preventDefault() const

    username = document.getElementById('input-box').value fetchPerson(username) })
  14. Let's get some CSS in here Remember, the goal of

    webpack is for it to manage all our assets, CSS included. This is weird at first but stick with me... (Also I suck at design, please forgive)
  15. form { width: 200px; margin: 10px auto; } #results {

    width: 300px; margin: 0 auto; border: 1px solid #111; background: #ddd; }
  16. ERROR in ./src/style.css Module parse failed: /github-app/src/style.css Unexpected token (1:5)

    You may need an appropriate loader to handle this file type. | form { | width: 200px; | margin: 10px auto; @ ./src/main.js 3:0-22 @ multi main
  17. npm install --save-dev css-loader npm install --save-dev style-loader » CSS

    Loader: parses CSS files » Style Loader: dynamically inserts stylesheets into HTML
  18. Add another rule { test: /\.css$/, include: path.resolve('src'), use: [{

    loader: 'style-loader', }, { loader: 'css-loader', }] } Loaders are applied from right to left, or bottom to top
  19. var { getIfUtils, removeEmpty } = require('webpack-config-utils') var { ifProduction,

    ifNotProduction } = getIfUtils(process.env.NODE_ENV || 'development') » removeEmpty: removes undefined from arrays » ifProduction: returns what it's given if NODE_ENV === 'production' » ifNotProduction: returns what it's given if NODE_ENV !== 'production'
  20. Starting point npm run build:prod - main.js: 24.9kb (All CSS

    is contained within main.js and dynamically inserted)
  21. Plugins A webpack plugin will typically work on the bundle

    as a whole, rather than on individual files.
  22. Minifying var webpack = require('webpack') ... output: { ... },

    plugins: removeEmpty([ ifProduction(new webpack.optimize.UglifyJsPlugin()) ]), module: { ... }
  23. We should keep our first page load super quick »

    download JS » parse JS » execute JS This takes time.
  24. src/fetch-person.js export const fetchPerson = username => fetch(`https://api.github.com/users/${username}`) .then(d =>

    d.json()) .then(displayPerson) const displayPerson = user => document.getElementById('results').innerHTML = ` <h1>${user.name}</h1> <h3>${user.company}</h3> <p>${user.bio || 'No Bio :('}</p> `
  25. import './style.css' import { fetchPerson } from './fetch-person' const form

    = document.getElementById('github-form') form.addEventListener('submit', e => { e.preventDefault() const username = document.getElementById('input-box').value fetchPerson(username) })
  26. import './style.css' const form = document.getElementById('github-form') form.addEventListener('submit', e => {

    e.preventDefault() const username = document.getElementById('input-box').value System.import('./fetch-person') .then(module => module.fetchPerson) .then(fetchPerson => fetchPerson(username)) })
  27. Wait, the build went up? This is a contrived example,

    because this app is tiny! You pay a small cost because webpack has code that it inserts for lazily loading modules, but if your pages are big enough you'll get still save.
  28. Lazy loading: not a silver bullet But when you do

    want it, webpack makes it easy :)
  29. Bonus: dead code elimination webpack 2 can parse ES2015 modules,

    that is: import { x } from './y' export default function foo() {...}
  30. ES2015 module imports and exports have to be static. So

    we can go through them and see which ones are used and which ones aren't needed. This means we can eliminate any code relating to ununsed exports!
  31. src/not-used.js export const SO_NOT_USED = 'I AM NOT USED BY

    ANYTHING EVER' export const SO_USED = 'I GET USED BY THINGS' src/main.js import { SO_USED } from './not-used' console.log(SO_USED)
  32. Stop Babel converting modules Stop Babel converting: use: [{ loader:

    'babel-loader', options: { presets: [['es2015', { modules: false }]] } }]
  33. webpack 2 » Much improved documentation & community engagement »

    Improved configuration with a nicer API and automatic validation of config » Tree shaking, easier code splitting and lazy loading » More performant
  34. Fin » Slides & code: https://github.com/jackfranklin/half-stack- webpack » webpack 2:

    webpack.js.org » Me: @Jack_Franklin, javascriptplayground.com, elmplayground.com Thanks to: Glen Maddern, Sean Larkinn, Kent C Dodds