Taming the JavaScript Beast in Magento 2

7d3cf77698dbeb77db8072adc2cd149c?s=47 Max Bucknell
September 19, 2016

Taming the JavaScript Beast in Magento 2

Presentation given by Max Bucknell and Marcin Szterling at Meet Magento Poland 2016, 2016-09-19.

7d3cf77698dbeb77db8072adc2cd149c?s=128

Max Bucknell

September 19, 2016
Tweet

Transcript

  1. Max Bucknell & Marcin Szterling Taming the JavaScript Beast in

    Magento 2 Ujarzmić JavaScript w Magento 2
  2. None
  3. Take a Deep Breath • What's Changed for M2? •

    RequireJS • JavaScript Components • jQuery UI Widgets • UI Apps
  4. What's Changed? • No more Prototype • No more inline

    scripts • No more adding JS to the <head> • No more global variables • And a bunch of new stuff
  5. RequireJS

  6. RequireJS • A JavaScript module loader • 1.0 in 2011

    • Loads JavaScript on a page. • Loads a "module", and its dependencies, and its dependencies, and so on.
  7. AMD • Not a chip manufacturer • Stands for "Asynchronous

    Module Definition" • The format of RequireJS "modules" • Used in all Magento 2 JavaScript files
  8. AMD – Dissection define([ 'jquery', ...otherDependencies, ], function moduleDefinition (

    $, ...otherDependencies ) { ... });
  9. define Function define([ 'jquery', ...otherDependencies, ], function moduleDefinition ( $,

    ...otherDependencies ) { ... });
  10. Dependency Definition define([ 'jquery', ...otherDependencies, ], function moduleDefinition ( $,

    ...otherDependencies ) { ... });
  11. Module Definition define([ 'jquery', ...otherDependencies, ], function moduleDefinition ( $,

    ...otherDependencies ) { ... });
  12. Module Definition • To define an AMD module, you write

    a function • This function will be called once when your module is loaded • Whatever you return is what dependents get, your public API
  13. RequireJS – Impacts • All JavaScript is loaded after your

    content • Separation of concerns • No global scope pollution
  14. AMD Dependencies • How does RequireJS know what jquery means?

    • ...or collapsible? • ...or mage/translate? • ...or Magento_Checkout/js/view/sidebar?
  15. AMD Dependencies • Modules are referred to by their name,

    which comes from the URL: store.biz/static/frontend/Magento/luma/pl_PL/Magent
  16. AMD Dependencies PL/Magento_Checkout/js/view/sidebar.js • Modules are referred to by their

    name, which comes from the URL:
  17. AMD Dependencies • Modules are referred to by their name,

    which comes from the URL: PL/Magento_Checkout/js/view/sidebar
  18. AMD Dependencies • Can also be configured via a mapping

    • requirejs-config.js • As a sibling to the web directory • Standard RequireJS Config • Merged by Magento\Framework\Requirejs
  19. requirejs-config.js var config = { map: { '*': { 'collapsible':

    'mage/collapsible', }, } };
  20. JavaScript Components

  21. JavaScript Component • Basic unit of functionality in Magento 2

    • Built on top of AMD, loaded by RequireJS • Gives us The Way to get JavaScript into a page • Comes in two parts: Definition, and Declaration
  22. JS Component Definition • A normal AMD module that returns

    a function. • Function takes parameters of a configuration object, and an HTML element.
  23. JS Component Definition define([], function addClass() { function main (config,

    el) { el.classList.add( config.className); } return main; });
  24. JS Component Declaration • JavaScript is included in the template,

    not the layout XML • Upshot: JavaScript is only included when it needs to be • Specially formatted JSON, that is picked up by Magento
  25. JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {

    "className": "class__new" } } } </script>
  26. JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {

    "className": "class__new" } } } </script>
  27. JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {

    "className": "class__new" } } } </script>
  28. JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {

    "className": "class__new" } } } </script>
  29. JS Component Definition define([], function addClass() { function main (config,

    el) { el.classList.add( config.className); } return main; });
  30. JS Components • JS Components will only load once, but

    may be run many times. • Convention comes from jQuery UI: All jQuery UI Widgets are valid JS Components. • To run some general JavaScript, use * as the element selector.
  31. UI Apps

  32. UI App • A UI App (defined in Magento_Ui) is

    a JavaScript Component. • Renders a tree of uiComponents into a dynamic web app. • uiComponents ≠ UI Components
  33. UI Components? • UI Components are UI Apps under the

    hood. • The checkout is not a UI Component, it is a UI App. • Configured by the layout, not a ui_component file. • UI Components are out of scope.
  34. uiComponent • AMD Module. • A view model, or "block"

    in Magento language. • Extend from this root component to create your own. • Define the public API for a template.
  35. When to Use • For rendering private data. • Serves

    whole page from Varnish. • Only loads private data through API.
  36. UI App Creation <script type="text/x-magento-init"> { "#checkout": { "Magento_Ui/js/core/app": <?=

    $block->getJsLayout() ?> } } </script>
  37. uiComponent define([ 'uiComponent', ], function myComponent ( Component ) {

    return Component.extend({ // Public API }); });
  38. initialize Component.extend({ initialize: function () { this._super(); // Set up,

    API requests, etc }, //... });
  39. defaults Component.extend({ defaults: { template: 'My_Module/template/component', }, getTemplate: function ()

    { return this.template }, });
  40. Templating • Your view-model is linked to a template. •

    Templates are parsed by Knockout • Knockout uses a special binding syntax to make views dynamic.
  41. Binding <button data-bind="click: show"> Click Me! </button> <div data-bind="visible: isVisible">

    We are visible! </div>
  42. Observables • Your view model will receive notifications about state

    changes. • Your view needs to respond to these. • Knockout Observables are data objects you can "subscribe" to, and be notified of changes.
  43. Observables Component.extend({ isVisible: ko.observable(false), show: function () { this.isVisible(true); },

    });
  44. Observables Component.extend({ isVisible: ko.observable(false), show: function () { this.isVisible(true); },

    });
  45. Binding <button data-bind="click: show"> Click Me! </button> <div data-bind="visible: isVisible">

    We are visible! </div>
  46. Component Configuration • uiComponents are configured in the layout XML

    as a constructor argument. • Syntax is like DI XML, with deeply nested arrays. • Magento\Checkout\Block\Onepage resolves this array into JSON.
  47. XML Configuration <referenceBlock name="checkout.root"> <arguments> <argument name="jsLayout" xsi:type="array" > <item

    name="components"> ... </item> </argument> </arguments>
  48. XML Configuration <item name="my-component"> <item name="component" >My_Module/js/view/component</item> <item name="config"> <item

    name="template >Some_Module/template/component</item> </item> </item>
  49. XML Configuration <item name="my-component"> <item name="component" >My_Module/js/view/component</item> <item name="config"> <item

    name="template >Some_Module/template/component</item> </item> </item>
  50. XML Configuration <item name="my-component"> <item name="component" >My_Module/js/view/component</item> <item name="config"> <item

    name="template >Some_Module/template/component</item> </item> </item>
  51. XML Configuration <item name="my-component"> <item name="component" >My_Module/js/view/component</item> <item name="config"> <item

    name="template >Some_Module/template/component</item> </item> </item>
  52. XML Configuration <item name="my-component"> ... <item name="displayArea" >myComponent</item> <item name="children">

    // It begins all over again. </item> </item>
  53. XML Configuration <item name="my-component"> ... <item name="displayArea" >myComponent</item> <item name="children">

    // It begins all over again. </item> </item>
  54. Rendering Children • An app is a tree of UI

    Components. • Get a child by calling getRegion(name). • getRegion always returns an array. • Get all children with this.regions.
  55. Rendering a child <!-- ko foreach: getRegion('name') --> <!-- ko

    template: getTemplate() --> <!-- /ko --> <!-- /ko -->
  56. Component Registry • A component "class" is a function that

    returns a constructor, not the component itself. • You cannot load a component as a dependency • Use the registry to get a component. • Registry key is the component's name with all parent component's names. Not displayArea
  57. Component Registry let shippingStep = registry.get( 'checkout.steps.shipping-step' ); // may

    be undefined
  58. Component Registry • All JavaScript is loaded and executed asynchronously.

    • You cannot depend on components being loaded. • Magento has an answer here...
  59. Component Registry registry.get( 'checkout.steps.shipping-step', function (component) { component.isVisible.subscribe( onComponentVisibleChange );

    } );
  60. Conclusion • This is quite a comprehensive framework for web

    apps. • It's new. Expect issues. Some common things are hard or ugly.
  61. None
  62. Join a Growing Team

  63. ?