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.

Utiliser Webpack dans une application Symfony

4,964 views

Published on

Depuis la version 2.8 de Symfony, Assetic le gestionnaire d'assets PHP n'est plus inclus par défaut dans la Standard Edition. Du coup, se pose la question, faut-il encore l'utiliser ? Quels sont les alternatives qui s'offrent à nous ?

Au cours de cette présentation, je vous présenterai l'outil Webpack, qui permet de packager nos assets via une configuration. Étape par étape, nous verrons comment migrer une application Symfony utilisant Assetic vers une application Symfony avec Webpack, du dev à la production.

Published in: Engineering

Utiliser Webpack dans une application Symfony

  1. 1. Integrate Webpack in a Symfony app How to ? 1 . 1
  2. 2. Me ? Lead Developer SensioLabs @al0neh Alain Hippolyte 1 . 2
  3. 3. Assetic Transform assets via filters Not in Symfony Standard Edition anymore 2 . 1
  4. 4. 2 . 2
  5. 5. Assetic drawbacks Not the best DX Not content aware Not frontend dev friendly Poorly maintained 2 . 3
  6. 6. 3 . 1
  7. 7. Module bundler 3 . 2
  8. 8. Module one single functional unit https://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript 3 . 3
  9. 9. Bundler takes modules with dependencies and emits static assets representing those modules like the Service Container in Symfony 3 . 4
  10. 10. ≠ 3 . 5
  11. 11. Features Loads source files Transforms assets Produces asset bundles Generates artifacts (hashes, srcmaps) Great DX 3 . 6
  12. 12. How does it work ? 3 . 7
  13. 13. 3 . 8
  14. 14. The 4 Core Concepts Entries - Where to start ? Output - Where to output ? Loaders - How to transform ? Plugins - How to bundle ? 4 . 1
  15. 15. webpack.config.js module.exports = { entry: { ... }, output: { ... }, module: { rules: [ ... ] }, plugins: [ ... ] }; 4 . 2
  16. 16. Entries // shortand syntax const config = { entry: './src/app.js' }; // Object syntax const config = { entry: { app: './src/app.js', vendor: './src/vendor.js' } }; Where to start? 4 . 3
  17. 17. Output module.exports = { output: { path: './web/builds', filename: 'bundle.js', publicPath: '/builds/' } }; Where to output ? 4 . 4
  18. 18. Loaders module.exports = { module: { rules: [ { test: /.js$/, use: 'babel-loader' } ] } }; How to transform ? 4 . 5
  19. 19. Common loaders Transpiler : babel-loader, ts-loader Styles : css-loader, style-loader Files : url-loader, file-loader Linting : jslint-loader https://webpack.js.org/loaders/ 4 . 6
  20. 20. Plugins module.exports = { plugins: [ new webpack.optimize.UglifyJsPlugin() ] }; https://webpack.js.org/plugins/ bundle-wide processing 4 . 7
  21. 21. Getting Started 5 . 1
  22. 22. Agenda Entry Configure Webpack with SCSS files Import fonts 5 . 2
  23. 23. Install Webpack 1/ Make a package.json file { "name": "sf-live-2017-symfony-webpack", "version": "1.0.0", "devDependencies": { "babel-core": "^6.24.0", "babel-loader": "^6.4.1", "webpack": "^2.2.1" } } $ npm install $ ./node_modules/.bin/webpack 5 . 3
  24. 24. My first webpack entry // app/Resources/assets/js/main.js console.log('Symfony Live Paris 2017'); {# app/Resources/views/base.html.twig #} <script src="{{ asset('builds/bundle.js') }}"></script> 5 . 4
  25. 25. webpack.config.js module.exports = { entry: { app: './app/Resources/assets/js/app.js', }, output: { path: './web/builds', filename: 'bundle.js', publicPath: '/builds/' }, module: { rules: [ { test: /.js$/, exclude: /(node_modules)/, use: 'babel-loader' } ] } }; 5 . 5
  26. 26. ./node_modules/.bin/webpack 5 . 6
  27. 27. Add our sass loader // app/Resources/assets/scss/app.scss $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; @import "variables"; @import "~bootstrap-sass/assets/stylesheets/bootstrap"; @import "bootstrap-theme"; // app/Resources/assets/js/app.js import '../scss/app.scss'; 6 . 1
  28. 28. Install sass dependencies module: { rules: [ // ... + { + test: /.scss$/, + use: [ + { loader: "style-loader" }, + { loader: "css-loader" }, + { loader: "sass-loader" } + ] + } ] }; ./node_modules/.bin/webpack $ npm install --save-dev style-loader css-loader node-sass sass-loader 6 . 2
  29. 29. Houston we have a problem 6 . 3
  30. 30. // app/Resources/assets/scss/bootstrap.scss @font-face { font-family: 'Glyphicons Halflings'; src: url(#{$icon-font-path}glyphicons-halflings-regular.eot')); // ... } 6 . 4
  31. 31. Let's fix that ! module.exports = { module: { rules: [ // ... + { + test: /.woff2?$|.ttf$|.eot$|.svg$/, + use: "file-loader" + } ] } }; Install file-loader dependency 6 . 5
  32. 32. // app/Resources/assets/saas/main.scss // ... @import "../css/font-awesome-4.6.3.min.css"; @import "../css/font-lato.css"; @import "../css/bootstrap-datetimepicker.min.css"; @import "../css/highlight-solarized-light.css"; @import "../css/main.css"; Import other styles 6 . 6
  33. 33. 12 3 4 5 6 1. Google Font Lato import 2. Bootstrap 3. font-lato.css 4. bootstrap-datetimepicker.min.css 5. highlight-solarized-light.css 6. main.css 6 . 7
  34. 34. Summary Import a bootstrap theme Use Webpack to transform SCSS files Use Webpack to work with fonts 7
  35. 35. Now, JS 8 . 1
  36. 36. // app/Resources/assets/js/app.js import "../scss/app.scss"; import "./jquery-2.1.4.min"; import "./bootstrap-3.3.4.min"; // ... Common problem with Webpack Jquery Inline JS 8 . 2
  37. 37. Let's get fix them 8 . 3
  38. 38. Jquery const jqueryPath = 'app/Resources/assets/js/jquery-2.1.4.min.js'; module.exports = { plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", "window.jQuery": "jquery", }), ], resolve: { alias: { jquery: path.resolve(__dirname, jqueryPath) } }, }; 8 . 4
  39. 39. // login.html.twig {% block javascripts %} {# ... #} <script> $(document).ready(function() { var usernameEl = $('#username'); var passwordEl = $('#password'); if (!usernameEl.val() && !passwordEl.val()) { usernameEl.val('anna_admin'); passwordEl.val('kitten'); } }); </script> {% endblock %} 8 . 5
  40. 40. $ npm install --save-dev expose-loader rules: [ + { + test: /jquery/, + use: [ + { + loader: 'expose-loader', + options: '$' + }, + { + loader: 'expose-loader', + options: 'jQuery' + } + ] + } ] 8 . 6
  41. 41. Everything is good ! 8 . 7
  42. 42. Webpack Dev Server 9 . 1
  43. 43. $ npm install --save-dev webpack-dev-server module.exports = { plugins: [ new webpack.HotModuleReplacementPlugin() ], devServer: { hot: true, contentBase: './web/' }, devtool: 'inline-source-map', }; 9 . 2
  44. 44. // app/AppKernel.php class AppKernel extends Kernel { public function registerContainerConfiguration(LoaderInterface $loader) { //... $loader->load(function($container) { if ($container->getParameter('use_webpack_dev_server')) { $container->loadFromExtension('framework', [ 'assets' => [ 'base_url' => 'http://localhost:8080/' ] ]); } }); } } Ryan Weaver ./node_modules/.bin/webpack-dev-server 9 . 3
  45. 45. Prepare for production 10 . 1
  46. 46. 10 . 2
  47. 47. module.exports = { module: { rules: [{ test: /.scss$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'sass-loader'] + }) }] }, plugins: [ + new ExtractTextPlugin('app.css') ] }; {# app/Resources/views/base.html.twig #} +{% block stylesheets %} + <link rel="stylesheet" href="{{ asset('builds/app.css') }}"> +{% endblock %} Extract css into a separated file 10 . 3
  48. 48. Split vendors with CommonChunksPlugin module.exports = { entry: { vendor: [ 'jquery', 'bootstrap-sass' ] }, output: { filename: '[name].js' }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' }) ] }; {# app/Resources/views/base.html.twig #} {% block javascripts %} + <script src="{{ asset('builds/vendor.js') }}"></script> <script src="{{ asset('builds/app.js') }}"></script> {% endblock %} 10 . 4
  49. 49. Minify with UglifyJs Supported by Webpack out of the box ! module.exports = { plugins: [ + new webpack.optimize.UglifyJsPlugin({ + beautify: false, + compress: { + screw_ie8: true, + warnings: false + }, + mangle: { + screw_ie8: true, + keep_fnames: true + }, + comments: false + }) ] }; 10 . 5
  50. 50. Minify our styles { test: /.scss$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: [ { loader: 'css-loader', options: { // CSS Nano configuration minimize: { discardComments: { removeAll: true }, core: true, minifyFontValues: true } } }, 'sass-loader' ] }) } 10 . 6
  51. 51. Long term caching 10 . 7
  52. 52. const WebpackManifestPlugin = require('webpack-manifest-plugin'); module.exports = { output: { filename: '[name].[chunkhash].js' }, plugins: [ new WebpackManifestPlugin({ fileName: 'manifest.json' }) ] }; $ npm install --save-dev webpack-manifest-plugin Install Webpack Manifest plugin 10 . 8
  53. 53. https://github.com/symfony/symfony/pull/22046 Symfony 3.3 10 . 9
  54. 54. // app/config/config_prod.yml framework: assets: json_manifest_path: '%kernel.root_dir%/../web/builds/manifest.json' 10 . 10
  55. 55. Tips 11 . 1
  56. 56. Tree shaking only include code in your bundle that is being used https://blog.engineyard.com/2016/tree-shaking 11 . 2
  57. 57. Env vars EnvironmentPlugin : reference env vars through process.env DefinePlugin : global constants 11 . 3
  58. 58. OptimizeJs Plugin optimize a JavaScript file for faster initial execution and parsing https://github.com/vigneshshanmugam/optimize-js-plugin 11 . 4
  59. 59. DedupePlugin Deduplicate common files https://medium.com/@rajaraodv/two-quick-ways-to- reduce-react-apps-size-in-production-82226605771a 11 . 5
  60. 60. Thank you ! https://joind.in/talk/94c36 https://github.com/alOneh/sf-live-2017-symfony-webpack 12
  61. 61. Questions ? 13

×