Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Webpack Encore Symfony Live 2017 San Francisco

1 861 vues

Publié le

Ready to write an amazing front-end for your app? There are *so* many great tools, like React, Vue.js, module loaders, Sass, LESS, PostCSS and more. But, they all have one thing in common: you need to configure a *build* system before you write a single line of code! Thankfully, there's Webpack: the leading tool for processing & bundling your JavaScript and CSS. There's just one problem: configuring Webpack is tough and requires a lot of Webpack-specific knowledge. Say hello to Webpack Encore: a library built by Symfony to quickly bootstrap a sophisticated asset setup, complete with minification, SASS processing, automatic versioning, Babel support and *everything* you need to start writing great JavaScript quickly. In this talk, we'll also learn about using JavaScript modules, how to bootstrap a framework (like React) and other important modern practices. Give your assets a huge boost with Webpack Encore!

Publié dans : Technologie

Webpack Encore Symfony Live 2017 San Francisco

  1. 1. Webpack Encore: Pro JavaScript and CSS for all my Friends! … that means you!
  2. 2. > Lead of the Symfony documentation team
 > Writer for KnpUniversity.com > SensioLabs & Blackfire evangelist… Fanboy > Husband of the much more talented @leannapelham knpuniversity.com twitter.com/weaverryan Yo! I’m Ryan! > Father to my more handsome son, Beckett
  3. 3. … that means you! Webpack Encore: Pro JavaScript and CSS for all my Friends!
  4. 4. , ReactJS, webpack @weaverryan All of modern JavaScript in 45 minutes! ES6 the 12 new JS things they invent during this presentation , ES2015 , ECMAScript , Babel , NodeJS yarn , modules … … and of course …
  5. 5. Modern JavaScript is a lot like… @weaverryan
  6. 6. Game of Thrones @weaverryan
  7. 7. JavaScript @weaverryan GoT Countless libraries and competing standards fighting for influence Countless characters and completing factions fighting for influence
  8. 8. @weaverryan You spent 6 months building your site in “Hipster.js” only to read on Twitter that: “no self-respecting dev uses that crap anymore” That character you love and followed for 2 seasons, was just unceremoniously decapitated JavaScript GoT
  9. 9. JavaScript is a (weird) language IT JUST HAPPENS THAT BROWSERS CAN EXECUTE THAT LANGUAGE
  10. 10. @weaverryan // yay.js
 var message = 'I like Java...Script';
 
 console.log(message); > node yay.js I like Java...Script NodeJS: server-side JavaScript engine yarn/npm: Composer for NodeJS
  11. 11. // web/js/productApp.js
 var products = [
 'Sheer Shears',
 'Wool Hauling Basket',
 'After-Shear (Fresh Cut Grass)',
 'After-Shear (Morning Dew)'
 ];
 
 var loopThroughProducts = function(callback) {
 for (var i = 0, length = products.length; i < length; i++) {
 callback(products[i]);
 }
 };
 
 loopThroughProducts(function(product) {
 console.log('Product: '+product);
 }); {# app/Resources/views/default/products.html.twig' #} <script src="{{ asset('js/productApp.js') }}"></script> our store for sheep (baaaa)
  12. 12. class ProductCollection
 {
 constructor(products) {
 this.products = products;
 }
 }
 
 let collection = new ProductCollection([
 'Sheer Shears',
 'Wool Hauling Basket',
 'After-Shear (Fresh Cut Grass)',
 'After-Shear (Morning Dew)',
 ]);
 let prods = collection.getProducts();
 
 let loopThroughProducts = callback => {
 for (let prod of prods) {
 callback(prods);
 }
 };
 
 loopThroughProducts(product => console.log('Product: '+product)); what language is this? JavaScript
  13. 13. @weaverryan ECMAScript The official name of standard JavaScript ES6/ES2015/Harmony The 6th accepted (so official) version of ECMAScript
  14. 14. Proper class and inheritance syntax let: similar to var, but more hipster function (product) {
 console.log(product);
 } class ProductCollection
 {
 constructor(products) {
 this.products = products;
 }
 }
 
 let collection = new ProductCollection([
 'Sheer Shears',
 'Wool Hauling Basket',
 'After-Shear (Fresh Cut Grass)',
 'After-Shear (Morning Dew)',
 ]);
 let prods = collection.getProducts();
 
 let loopThroughProducts = callback => {
 for (let prod of prods) {
 callback(prods);
 }
 };
 
 loopThroughProducts(product => console.log('Product: '+product));
  15. 15. class ProductCollection
 {
 constructor(products) {
 this.products = products;
 }
 }
 
 let collection = new ProductCollection([
 'Sheer Shears',
 'Wool Hauling Basket',
 'After-Shear (Fresh Cut Grass)',
 'After-Shear (Morning Dew)',
 ]);
 let prods = collection.getProducts();
 
 let loopThroughProducts = callback => {
 for (let prod of prods) {
 callback(prods);
 }
 };
 
 loopThroughProducts(product => console.log('Product: '+product)); But will it run in a browser??? Maybe!
  16. 16. Now we just need to wait 5 years for the worst browsers to support this
  17. 17. @weaverryan Babel … or do we? A JS transpiler!
  18. 18. {
 "devDependencies": {
 "babel-cli": "^6.10.1"
 }
 } Babel is a NodeJS binary… … package.json > yarn add --dev babel-cli @weaverryan
  19. 19. > ./node_modules/.bin/babel web/js/productApp.js -o web/builds/productApp.js {# app/Resources/views/default/products.html.twig' #} <script src="{{ asset('builds/productApp.js') }}"></script> @weaverryan
  20. 20. > ./node_modules/.bin/babel web/js/productApp.js -o web/builds/productApp.js {# app/Resources/views/default/products.html.twig' #} <script src="{{ asset('builds/productApp.js') }}"></script> But, this made no changes js/productApp.js == builds/productApp.js @weaverryan
  21. 21. Babel can transpile anything CoffeeScript --> JavaScript Coffee --> Tea ES6 JS --> ES5 JS * Each transformation is called a preset
  22. 22. 1) Install the es2015 preset library 2) Add a .babelrc file > yarn add babel-preset-es2015 {
 "presets": [
 "es2015"
 ]
 } @weaverryan
  23. 23. > ./node_modules/.bin/babel web/js/productApp.js -o web/builds/productApp.js loopThroughProducts(
 product => console.log('Product: '+product)
 ); loopThroughProducts(function (product) {
 return console.log('Product: ' + product);
 });
 source: built:
  24. 24. But we can use new (or experimental) features now @weaverryan Modern JavaScript has a build step Big Takeaway #1:
  25. 25. @weaverryan New to ES6: JavaScript Modules!
  26. 26. The Classic Problem: If you want to organize your JS into multiple files, you need to manually include all those script tags! @weaverryan
  27. 27. // web/js/ProductCollection.js 
 class ProductCollection
 {
 constructor(products) {
 this.products = products;
 }
 
 getProducts() {
 return this.products;
 }
 
 getProduct(i) {
 return this.products[i];
 }
 }
 
 export ProductCollection;
 @weaverryan
  28. 28. // web/js/productApp.js
 
 import ProductCollection from './ProductCollection';
 
 var collection = new ProductCollection([
 'Sheer Shears',
 'Wool Hauling Basket',
 'After-Shear (Fresh Cut Grass)',
 'After-Shear (Morning Dew)',
 ]);
 
 // ... {# app/Resources/views/default/products.html.twig' #} <script src="{{ asset('builds/productApp.js') }}"></script> > ./node_modules/.bin/babel web/js/productApp.js -o web/builds/productApp.js
  29. 29. Module loading in a browser is hard to do @weaverryan
  30. 30. @weaverryan Introducing…
  31. 31. @weaverryan Webpack! • bundler • module loader • all-around nice guy
  32. 32. Install webpack > yarn add --dev webpack @weaverryan
  33. 33. // web/js/ProductCollection.js 
 class ProductCollection
 {
 // ...
 }
 
 export ProductCollection;
 // web/js/productApp.js
 
 import ProductCollection from ‘./ProductCollection';
 
 // ...
  34. 34. Go webpack Go! > ./node_modules/.bin/webpack web/js/productApp.js web/builds/productApp.js The one built file contains the code from both source files
  35. 35. Optional config to make it easier to use: // webpack.config.js
 module.exports = {
 entry: {
 product: './web/js/productApp.js'
 },
 output: {
 path: './web/build',
 filename: '[name].js',
 publicPath: '/build/'
 }
 };
 build/product.js {# app/Resources/views/default/products.html.twig' #} <script src="{{ asset('build/product.js') }}"></script>
  36. 36. > ./node_modules/.bin/webpack @weaverryan > yarn run webpack
  37. 37. Great! Now let’s add more features to Webpack! @weaverryan
  38. 38. @weaverryan Features, features, features, features, features • babel transpiling • dev server • production optimizations • CSS handling • Sass/LESS • PostCSS • React • Vue • versioning • source maps • image handling • extract text • shared entry • jQuery providing • TypeScript • friendly errors
  39. 39. No Problem @weaverryan
  40. 40. @weaverryan var path = require("path");
 var process = require('process');
 var webpack = require('webpack');
 var production = process.env.NODE_ENV === 'production';
 
 // helps load CSS to their own file
 var ExtractPlugin = require('extract-text-webpack-plugin');
 var CleanPlugin = require('clean-webpack-plugin');
 var ManifestPlugin = require('webpack-manifest-plugin');
 
 var plugins = [
 new ExtractPlugin('[name]-[contenthash].css'), // <=== where should content be piped
 
 // put vendor_react stuff into its own file
 // new webpack.optimize.CommonsChunkPlugin({
 // name: 'vendor_react',
 // chunks: ['vendor_react'],
 // minChunks: Infinity, // avoid anything else going in here
 // }),
 new webpack.optimize.CommonsChunkPlugin({
 name: 'main', // Move dependencies to the "main" entry
 minChunks: Infinity, // How many times a dependency must come up before being extracted
 }),
 new ManifestPlugin({
 filename: 'manifest.json',
 // prefixes all keys with builds/, which allows us to refer to
 // the paths as builds/main.css in Twig, instead of just main.css
 basePath: 'builds/'
 }),
 ];
 
 if (production) {
 plugins = plugins.concat([
 // This plugin looks for similar chunks and files
 // and merges them for better caching by the user
 new webpack.optimize.DedupePlugin(),
 
 // This plugins optimizes chunks and modules by
 // how much they are used in your app
 new webpack.optimize.OccurenceOrderPlugin(),
 
 // This plugin prevents Webpack from creating chunks
 // that would be too small to be worth loading separately
 new webpack.optimize.MinChunkSizePlugin({
 minChunkSize: 51200, // ~50kb
 }),
 
 // This plugin minifies all the Javascript code of the final bundle
 new webpack.optimize.UglifyJsPlugin({
 mangle: true,
 compress: {
 warnings: false, // Suppress uglification warnings
 },
 sourceMap: false
 }),
 
 // This plugins defines various variables that we can set to false
 // in production to avoid code related to them from being compiled
 // in our final bundle
 new webpack.DefinePlugin({
 __SERVER__: !production,
 __DEVELOPMENT__: !production,
 __DEVTOOLS__: !production,
 'process.env': {
 BABEL_ENV: JSON.stringify(process.env.NODE_ENV),
 'NODE_ENV': JSON.stringify('production')
 },
 }),
 new CleanPlugin('web/builds', {
 root: path.resolve(__dirname , '..')
 }),
 ]);
 }
 
 
 module.exports = {
 entry: {
 main: './main',
 video: './video',
 checkout_login_registration: './checkout_login_registration',
 team_pricing: './team_pricing',
 credit_card: './credit_card',
 team_subscription: './team_subscription',
 track_organization: './track_organization',
 challenge: './challenge',
 workflow: './workflow',
 code_block_styles: './code_block_styles',
 content_release: './content_release',
 script_editor: './script_editor',
 sweetalert2_legacy: './sweetalert2_legacy',
 admin: './admin',
 admin_user_refund: './admin_user_refund',
 
 // vendor entry points to be extracted into their own file
 // we do this to keep main.js smaller... but it's a pain
 // because now we need to manually add the script tag for
 // this file is we use react or react-dom
 // vendor_react: ['react', 'react-dom'],
 },
 output: {
 path: path.resolve(__dirname, '../web/builds'),
 filename: '[name]-[hash].js',
 chunkFilename: '[name]-[chunkhash].js',
 // in dev, make all URLs go through the webpack-dev-server
 // things *mostly* work without this, but AJAX calls for chunks
 // are made to the local, Symfony server without this
 publicPath: production ? '/builds/' : 'http://localhost:8090/builds/'
 },
 plugins: plugins,
 module: {
 loaders: [
 {
 test: /.js$/,
 exclude: /node_modules/,
 loader: "babel-loader"
 },
 {
 test: /.scss/,
 loader: ExtractPlugin.extract('style', 'css!sass'),
 },
 {
 test: /.css/,
 loader: ExtractPlugin.extract('style', 'css'),
 },
 {
 test: /.(png|gif|jpe?g|svg?(?v=[0-9].[0-9].[0-9])?)$/i,
 loader: 'url?limit=10000',
 },
 {
 // the ?(?v=[0-9].[0-9].[0-9])? is for ?v=1.1.1 format
 test: /.woff(2)?(?v=[0-9].[0-9].[0-9])?$/,
 // Inline small woff files and output them below font/.
 // Set mimetype just in case.
 loader: 'url',
 query: {
 prefix: 'font/',
 limit: 5000,
 mimetype: 'application/font-woff'
 },
 //include: PATHS.fonts
 },
 {
 test: /.ttf?(?v=[0-9].[0-9].[0-9])?$|.eot?(?v=[0-9].[0-9].[0-9])?$/,
 loader: 'file',
 query: {
 name: '[name]-[hash].[ext]',
 // does this do anything?
 prefix: 'font/',
 },
 //include: PATHS.fonts
 },
 {
 test: /.json$/,
 loader: "json-loader"
 },
 ]
 },
 node: {
 fs: 'empty'
 },
 debug: !production,
 devtool: production ? false : 'eval',
 devServer: {
 hot: true,
 port: 8090,
 // tells webpack-dev-server where to serve public files from
 contentBase: '../web/',
 headers: { "Access-Control-Allow-Origin": "*" }
 },
 };
 *** not visible here: the 1000 ways you can shot yourself in the foot
  41. 41. @weaverryan Hello Webpack Encore
  42. 42. @weaverryan oh hai!
  43. 43. Let’s start over > rm -rf package.json node_modules/ > cowsay “Make mooooore room please” @weaverryan
  44. 44. @weaverryan … and let’s start simple // assets/js/app.js 
 console.log('wow');

  45. 45. > yarn add @symfony/webpack-encore --dev @weaverryan Step 1: Install Encore
  46. 46. @weaverryan Step 2: webpack.config.js var Encore = require('@symfony/webpack-encore');
 
 Encore
 .setOutputPath('web/build/')
 .setPublicPath('/build')
 
 // will output as web/build/app.js
 .addEntry('app', './assets/js/app.js')
 
 .enableSourceMaps(!Encore.isProduction())
 ;
 
 module.exports = Encore.getWebpackConfig();

  47. 47. > yarn run encore dev {# base.html.twig #}
 <script src="{{ asset('build/app.js') }}"></script>
  48. 48. @weaverryan Yea… but I need my jQuery’s!
  49. 49. @weaverryan // assets/js/app.js
 var $ = require('jquery');
 
 $(document).ready(function() {
 $('h1').append('I love big text!');
 });

  50. 50. > yarn run encore dev
  51. 51. > yarn add jquery --dev @weaverryan > yarn run encore dev
  52. 52. @weaverryan CSS: An un-handled dependency of your JS app
  53. 53. Could we do this? // assets/js/app.js // ...
 require(‘../css/cool-app.css’);
 
 $(document).ready(function() {
 $('h1').append('I love big text!');
 });

  54. 54. > yarn run encore dev {# base.html.twig #}
 <link ... href="{{ asset('build/app.css') }}">
  55. 55. Want Bootstrap? @weaverryan
  56. 56. > yarn add bootstrap @weaverryan /* assets/css/cool-app.css */
 @import "~bootstrap/dist/css/bootstrap.css";
 /* ... */ > yarn run encore dev
  57. 57. @weaverryan But what happens with images? /* assets/css/cool-app.css */
 .product-price {
 color: green;
 background-image: url('../images/logo.png');
 }

  58. 58. > yarn add bootstrap @weaverryan /* assets/css/cool-app.css */
 @import "~bootstrap/dist/css/bootstrap.css";
 /* ... */ > yarn run encore dev
  59. 59. image is copied to builds/ and the new URL is written into the CSS
  60. 60. Stop @weaverryan thinking of your JavaScript as random code that executes
  61. 61. Start @weaverryan thinking of your JavaScript as a single application with dependencies that are all packaged up together
  62. 62. Sass instead of CSS? @weaverryan
  63. 63. @weaverryan > yarn run encore dev // assets/js/app.js
 // ...
 require('../css/app.scss');
  64. 64. @weaverryan > yarn add sass-loader node-sass --dev // webpack.config.js
 
 Encore
 // ...
 
 .enableSassLoader()
  65. 65. @weaverryan But I want page-specific CSS and JS files!
  66. 66. // assets/js/productApp.js
 import '../css/productApp.css';
 import $ from 'jquery';
 
 $(document).ready(function() {
 console.log('product page loaded!');
 });

  67. 67. // webpack.config.js
 
 Encore
 // ...
 
 .addEntry('app', './assets/js/app.js')
 .addEntry('productApp', './assets/js/productApp.js') {# product.html.twig #}
 {% block stylesheets %}
 {{ parent() }}
 
 <link href="{{ asset('build/productApp.css') }}">
 {% endblock %}
 
 {% block javascripts %}
 {{ parent() }}
 
 <script src="{{ asset('build/productApp.js') }}"></script>
 {% endblock %}

  68. 68. @weaverryan > yarn run encore dev
  69. 69. Wait! @weaverryan You have too many jQuery-ies!
  70. 70. @weaverryan jQuery is in here … and also here
  71. 71. // webpack.config.js
 
 Encore
 // ...
 
 .createSharedEntry('app', './assets/js/app.js')
 .addEntry('productApp', './assets/js/productApp.js')
  72. 72. {# base.html.twig #}
 <script src="{{ asset('build/manifest.js') }}"></script>
 <script src="{{ asset('build/app.js') }}"></script>
  73. 73. @weaverryan Asset Versioning
  74. 74. // webpack.config.js
 
 Encore
 // ...
 
 .enableVersioning()
  75. 75. Amazing! Automatic Cache Busting!!! … oh wait… @weaverryan
  76. 76. {# base.html.twig #}
 <script src="{{ asset('build/manifest.js') }}"></script>
 <script src="{{ asset('build/app.js') }}"></script>
  77. 77. manifest.json to the rescue!
  78. 78. manifest.json to the rescue! {
 "build/app.css": "/build/app.3666e24a0be80f22bd8f31c43a70b14f.css",
 "build/app.js": "/build/app.f18c7a7f2785d99e0c25.js",
 "build/images/logo.png": "/build/images/logo.482c1dc2.png",
 "build/manifest.js": "/build/manifest.d41d8cd98f00b204e980.js",
 "build/productApp.css": "/build/productApp.01f416c68486810b3cb9.css",
 "build/productApp.js": "/build/productApp.1af5d8e89a35e521309b.js"
 } {# base.html.twig #}
 <script src="{{ asset('build/manifest.js') }}"></script>
 <script src="{{ asset('build/app.js') }}"></script>
  79. 79. manifest.json to the rescue! # app/config/config.yml
 framework:
 # ...
 assets:
 json_manifest_path: ‘%kernel.project_dir%/web/build/manifest.json'
  80. 80. … so add long-term caching… @weaverryan
  81. 81. @weaverryan This presentation needs more buzz words React!
  82. 82. {# products.html.twig #}
 <div id="product-app"
 data-products="{{ products|json_encode|e('html_attr') }}">
 </div>
  83. 83. // assets/js/productApp.js
 
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ProductApp from './Components/ProductApp';
 import $ from 'jquery';
 
 $(document).ready(function() {
 const root = document.getElementById('product-app');
 const startingProducts = root.dataset['products'];
 
 ReactDOM.render(
 <ProductApp message="Great Products!” initialProducts={startingProducts} />, 
 root
 );
 });
  84. 84. // webpack.config.js
 
 Encore
 // ...
 .enableReactPreset()
 ; > yarn add --dev react react-dom babel-preset-react
  85. 85. @weaverryan • React • Vue • TypeScript • Code splitting • source maps • versioning • Sass/LESS … and is the life of any party … An Encore of Features
  86. 86. @weaverryan Putting it all together
  87. 87. @weaverryan ES6/ES2015/ECMAScript 2015 The newest version of Javascript, not supported by all browsers
  88. 88. @weaverryan Babel A tool that can transform JavaScript to different JavaScript presets A) ES6 js to “old” JS B) React to raw JS
  89. 89. @weaverryan Webpack A tool that follows imports to bundle JavaScript, CSS, and anything else you dream up into one JavaScript package
  90. 90. @weaverryan Webpack Encore A tool that makes configuring webpack not suck
  91. 91. Ryan Weaver @weaverryan THANK YOU! https://joind.in/talk/bac7c

×