Knockout 是 Magento 2 前端世界中引入的最大变化之一,虽然一开始它真的很难掌握,但最终它会成为一个非常有用的工具,使用起来很有趣。

在您必须根据用户的输入或其他一些操作更新某些内容的情况下,所讨论的库确实很出色。它将“可观察值”带到表中——可以自动更新的变量,并通知所有相关方它们已被修改。除此之外,我们还有一种“计算”类型的函数,它们会在更新其中任何可观察对象时自动触发。添加将 html 元素绑定到这些变量的选项,现在应该清楚容易获得的可能性的数量。

在本文中,我们将创建一个简单的模块,负责与我们的客户打乒乓球。我将向您展示如何:
– 创建一个简单的前端模块,
– 声明并为可观察变量赋值,
– 将变量从模板/块/php 传递到 JavaScript,
– 在模板中使用 knockout 可观察
对象, – 在可观察到的变化。

让我们从创建模块 Magently_TableTennis 开始,该模块将为主页提供 JavaScript、模板和布局更新。

//registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Magently_TableTennis',
    __DIR__
);

//etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Magently_TableTennis" setup_version="0.1.0">
        <sequence>
            <!-- add sequence since we will update cms_index_index layout -->
            <module name="Magento_Cms"/>
        </sequence>
    </module>
</config>

And this is where the fun part begins:
//view/frontend/web/js/view/tableTennis.js

//first we have to define modules we want to use in our script
define([
    'uiComponent',
    'ko'
], function (Component, ko) {
    //we'll make our game a Component to allow someone to easily modify it in the future
    return Component.extend({
        initialize: function () {
            //next line is basically equivalent of parent::__construct() in php 
            this._super();
            //let's divide our component to 2 separate elements
            //one responsible for populating our game's interface
            this.populateUi();
            //and one responsible for the gameplay itself
            this.gameplay();
        },
        populateUi: function () {
            //here we are creating an observable array and set its items to ping and pong
            this.userActions = ko.observableArray(['ping', 'pong']);
            //this observable will be responsible for storing information about currently selected action
            //initialMove will be passed from template
            this.selectedAction = ko.observable(this.initialMove);
            //and here we will create an observable without initial value
            //that will be responsible for storing computer's action
            this.aiMove = ko.observable();
        },
        gameplay: function () {
            //assign this to self so we can use it inside a function
            var self = this;
            //handleMove will be fired every time an observable inside it changes
            ko.computed(function handleMove()
            {
                if (self.selectedAction() == self.userActions()[0]) {
                    self.aiMove(self.userActions()[1]);
                }
                else {
                    self.aiMove(self.userActions()[0]);
                }
            });
        }
    });
});


//view/frontend/layout/cms_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="top.container">
            <block class="\Magento\Framework\View\Element\Template" name="tabletennis" template="Magently_TableTennis::tabletennis.phtml" before="-" />
        </referenceContainer>
    </body>
</page>

//view/frontend/templates/tabletennis.phtml

<!-- data-bind attribute lets us bind knockout entities to our elemets -->
<div data-bind="scope: 'tableTennis'">
    <!-- here we create select input with options fetched from userActions observable array -->
    <!-- and bind the selected option to selectedAction observable -->
    <select data-bind="options: userActions, value: selectedAction"></select>
    <!-- analogically we bind the text of a paragraph element to aiMove -->
    <p data-bind="text: aiMove"></p>
</div>
<!-- here we initialize our script -->
<script type="text/x-magento-init">
{
    <?php // we can specify site elements on which we want to trigger our script ?>
    <?php // * means we launch it only once - for the whole page ?>
    "*": {
        <?php // this is javascript we are launching ?>
        "Magento_Ui/js/core/app": {
            <?php // and here we declare components of Magento_Ui/js/core/app ?>
            "components": {
                "tableTennis": {
                    "component": "Magently_TableTennis/js/view/tableTennis",
                    <?php // here we set the initial move of the player ?>
                    "initialMove": "ping"
                }
            }
        }
    }
}
</script>