Share
Embed
Taming the JavaScript Beast in Magento 2
Presentation given by Max Bucknell and Marcin Szterling at Meet Magento Poland 2016, 2016-09-19.
Max Bucknell
September 19, 2016
Transcript
63 slides
-
Max Bucknell & Marcin Szterling Taming the JavaScript Beast in
Magento 2 Ujarzmić JavaScript w Magento 2 -
Take a Deep Breath • What's Changed for M2? •
RequireJS • JavaScript Components • jQuery UI Widgets • UI Apps -
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 -
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. -
AMD • Not a chip manufacturer • Stands for "Asynchronous
Module Definition" • The format of RequireJS "modules" • Used in all Magento 2 JavaScript files -
AMD – Dissection define([ 'jquery', ...otherDependencies, ], function moduleDefinition (
$, ...otherDependencies ) { ... }); -
define Function define([ 'jquery', ...otherDependencies, ], function moduleDefinition ( $,
...otherDependencies ) { ... }); -
Dependency Definition define([ 'jquery', ...otherDependencies, ], function moduleDefinition ( $,
...otherDependencies ) { ... }); -
Module Definition define([ 'jquery', ...otherDependencies, ], function moduleDefinition ( $,
...otherDependencies ) { ... }); -
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 -
RequireJS – Impacts • All JavaScript is loaded after your
content • Separation of concerns • No global scope pollution -
AMD Dependencies • How does RequireJS know what jquery means?
• ...or collapsible? • ...or mage/translate? • ...or Magento_Checkout/js/view/sidebar? -
AMD Dependencies • Modules are referred to by their name,
which comes from the URL: store.biz/static/frontend/Magento/luma/pl_PL/Magent -
AMD Dependencies PL/Magento_Checkout/js/view/sidebar.js • Modules are referred to by their
name, which comes from the URL: -
AMD Dependencies • Modules are referred to by their name,
which comes from the URL: PL/Magento_Checkout/js/view/sidebar -
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 -
requirejs-config.js var config = { map: { '*': { 'collapsible':
'mage/collapsible', }, } }; -
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 -
JS Component Definition • A normal AMD module that returns
a function. • Function takes parameters of a configuration object, and an HTML element. -
JS Component Definition define([], function addClass() { function main (config,
el) { el.classList.add( config.className); } return main; }); -
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 -
JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {
"className": "class__new" } } } </script> -
JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {
"className": "class__new" } } } </script> -
JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {
"className": "class__new" } } } </script> -
JS Component Declaration <script type="text/x-magento-init"> { "#my-element": { "My_Module/js/add-class": {
"className": "class__new" } } } </script> -
JS Component Definition define([], function addClass() { function main (config,
el) { el.classList.add( config.className); } return main; }); -
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. -
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 -
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. -
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. -
When to Use • For rendering private data. • Serves
whole page from Varnish. • Only loads private data through API. -
UI App Creation <script type="text/x-magento-init"> { "#checkout": { "Magento_Ui/js/core/app": <?=
$block->getJsLayout() ?> } } </script> -
uiComponent define([ 'uiComponent', ], function myComponent ( Component ) {
return Component.extend({ // Public API }); }); -
initialize Component.extend({ initialize: function () { this._super(); // Set up,
API requests, etc }, //... }); -
defaults Component.extend({ defaults: { template: 'My_Module/template/component', }, getTemplate: function ()
{ return this.template }, }); -
Templating • Your view-model is linked to a template. •
Templates are parsed by Knockout • Knockout uses a special binding syntax to make views dynamic. -
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. -
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. -
XML Configuration <referenceBlock name="checkout.root"> <arguments> <argument name="jsLayout" xsi:type="array" > <item
name="components"> ... </item> </argument> </arguments> -
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> -
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> -
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> -
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> -
XML Configuration <item name="my-component"> ... <item name="displayArea" >myComponent</item> <item name="children">
// It begins all over again. </item> </item> -
XML Configuration <item name="my-component"> ... <item name="displayArea" >myComponent</item> <item name="children">
// It begins all over again. </item> </item> -
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. -
Rendering a child <!-- ko foreach: getRegion('name') --> <!-- ko
template: getTemplate() --> <!-- /ko --> <!-- /ko --> -
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 -
Component Registry • All JavaScript is loaded and executed asynchronously.
• You cannot depend on components being loaded. • Magento has an answer here... -
Conclusion • This is quite a comprehensive framework for web
apps. • It's new. Expect issues. Some common things are hard or ugly.