Magento 2 Javascript Init Scripts(翻译)

原文地址

在 Alan 的 Magento 2 for PHP MVC系列文章中,曾经提到 RequireJS 是 Magento 2 系统所有 JS 的基础。这的确是事实,但是 RequireJS 仅仅触及 Magento 中 javaScript 可能性的表面。

今天我们将探索 Magento 2 引入的各种系统,以执行非嵌入<script type="text/javascript">标签的 js 代码作为开始。

JavaScript Init Methods

Magento js init 方法解决了一些不同的问题:
第一,他提供了一种标准机制,阻止直接在页面中嵌入 js 代码的行为。
第二,提供了调用 RequireJS 模块程序的方式(通过 define 来定义)(原话:Second, they provide a way to invoke a stand alone RequireJS module (defined with define) as a program.)
第三,提供了给程序传递服务器端产生的 JSON 对象的方式。
第四,提供了一种方式,告诉程序哪个 DOM 节点(如果有的话)是它的作用对象。

记住这四点。They may help you if you’re struggling with the mental model behind these custom framework features.

Setting up a Module

今天的教程和 Magento 的 PHP 不怎么相关,你可以在任何phtml模板上进行实验。

这里 Alan 没有提供示例代码,不过博主建了一个,可以去https://github.com/PiscesThankIT/JavascriptInitTutorial下载。

下载后启用它

$ php bin/magento module:enable Pulsestorm_JavascriptInitTutorial
$ php bin/magento setup:upgrade

现在在浏览器中输入如下URL

http://magento.example.com/pulsestorm_javascriptinittutorial/

你应该看到app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml模板被渲染出来了。

Setting up a RequireJS Module

我们现在已经有了一个模块,下面我们创建一个 RequireJS 的模块。
创建如下文件:

//File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/web/example.js
define([], function(){
    alert("A simple RequireJS module");
    return {};
});

这是个很简单的 RequireJS 模块。根据模块的文件位置以及 Magento 载入js 文件的方式,我们可以推得该模块的名称/ID是Pulsestorm_JavascriptInitTutorial/example

下面,修改content.phtml文件成下面的样子:

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
<script type="text/javascript">
    requirejs(['Pulsestorm_JavascriptInitTutorial/example'],function(example){
        alert("Loaded");
        console.log(example);
    });
</script>

这里我们创建了一个依赖Pulsestorm_JavascriptInitTutorial/example模块的程序。

浏览器中刷新http://magento.example.com/pulsestorm_javascriptinittutorial/ URL,你应该看到 alert 弹出。

如果你觉得上面的内容不太熟悉,你可以参阅本站Magento 2 and RequireJS (翻译)

译者注:刷新浏览器后首先看到的是 A simple RequireJS module 弹出框,点击确定后,看到 Loaded 弹出框,点击确定后,在 Console 中看到输出的空对象。

页面载入时先执行了Pulsestorm_JavascriptInitTutorial/example 成功后返回值为空对象,传递给了回调函数的example。执行回调函数内部的alert 和 console 输出。

X-Magento-Init

首先我们来了解<script type="text/x-magento-init">初始化方法。这个方法最简单的用法是让你运行某个 RequireJS 模块。把content.phtml模板改成下面的样子:

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
<div id="one" class="foo">
    Hello World
</div>
<div id="two" class="foo">
    Goodbye World
</div>

<script type="text/x-magento-init">
    {
        "*": {
            "Pulsestorm_JavascriptInitTutorial/example":{}
        }
    }
</script>

现在在重新载入页面,你应该看到 A simple RequireJS module 弹出框。这个是我们example.js中的定义的。

如果你从来没看过这种语法,可能觉得他看起来很怪。让我们一点一点来看一看。

首先是<script/>标签

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
<script type="text/x-magento-init">
    //...
</script>

这不是 javaScript 的标签。注意type="text/x-magento-init"属性。如果浏览器不认识 script 标签中的值,它就会忽略该标签中的内容。Magento (其他现代前端框架类似)利用了这个行为来实现它自己的功能。Magento 中有 js 代码会查找带有text/x-magento-init属性的标签,这超出了本篇教程的范围。如果你想进一步探索的话,可以先看看这个this Stack Exchange question and answer

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
{
    "Pulsestorm_JavascriptInitTutorial/example":{}
}

Magento 会查找该对象的键,并且以 RequireJS 模块的方式将他载入进来。这里就是让example.js被载入的关键。

你很可能很想知道为什么这个对象没有值呢。你还可能很想知道为什么这个对象是另一个键为*的对象的值。

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
{
    "*": {/*...*/}
}

解释这些原因前,我们得先说一说 javascript components。

Magento JavaScript Components

上面的例子运行了一个 RequireJS 模块程序。Magento 经常用x-magento-init方法调用 RequireJS 模块程序。但是,x-magento-init真正强大的能力是创建Magento Javascript Component

Magento Javascript Component are RequireJS modules that return a function。 Magento’s system code will call this function in a specific way that exposes extra functionality.

下面我们来看例子。修改example.js

//File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/web/example.js
define([], function () {
    var mageJsComponent = function()
    {
        alert("A simple magento component.");
    };

    return mageJsComponent;
});

这里我们定义了一个匿名函数,并将他分配给了mageJsComponent变量。然后我们返回他。

重新载入页面,你会看到 A Simple Magento Component 弹出框。

看起来可能有点蠢——如果 Magento 只是要调用它,返回一个 function 的意义是什么呢?你是对的,但是你对是因为我们少了点什么。修改phtml模板:

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
<div id="one" class="foo">
    Hello World
</div>
<div id="two" class="foo">
    Goodbye World
</div>

<script type="text/x-magento-init">
    {
        "*": {
            "Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}
        }
    }
</script>

修改 RequireJS 模块

//File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/web/example.js
define([], function () {
    var mageJsComponent = function(config)
    {
        alert("Look in your browser's console");
        console.log(config);
        //alert(config);
    };

    return mageJsComponent;
});

重新载入页面,你可以看到 alert 中的信息变成了Look in your browser’s console。你在浏览器的 console 中会看到:

> Object {config:"value"}

When we create a Magento Javascript Component, Magento calls the returned function and includes the object from text/x-magento-init

"Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}

键是 RequireJS 模块名,值是我们要传递给该 component 的对象。

这些例子可能看起来又傻又抽象。但是,在实际使用的模块中,我们会使用 PHP 来生成 JSON。系统允许我们在 phtml 模板中 render the JSON,并将他们传递给 js 代码。这有助于避免使用 PHP 直接生成 js 代码,直接生成的做法会产生糟糕的代码,还可能引入错误或者安全问题。

结束x-magento-init之前,还有最后一点要说。记得前文说x-magento-init

提供了给程序传递服务器端产生的 JSON 对象的方式。
提供了一种方式,告诉程序哪个 DOM 节点(如果有的话)是它的作用对象。

我们已经说过如何将服务器端生成的 JSON 传递给js 了,还没有说 DOM 节点。

修改example.js模块

//File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/web/example.js
define([], function () {
    var mageJsComponent = function(config, node)
    {
        console.log(config);
        console.log(node);
        //alert(config);
    };

    return mageJsComponent;
});

这里我们给mageJsComponent函数增加了一个参数。第二个参数就是我们程序要作用的 DOM node。但是,刷新页面,你会看到:

> Object {config:"value"}
> false

Magento 确实传递了值给node——但值输出是false,这个值是什么呢?

Magento不能神奇地知道你要作用于哪个 DOM node。我们得告诉他。修改phtml模板:

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml

<div id="one" class="foo">Hello World</div>
<div id="two" class="foo">
    Goodbye World
</div>

<script type="text/x-magento-init">
    {
        "#one": {
            "Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}
        }
    }
</script>

这里我们把原先的*改成了#one。之前我们用的*代表了这样一种特殊情形:当程序不需要作用于任何 DOM node 的时候。这个对象的键实际上是 CSS/jQuery style selector 。这个键告诉Pulsestorm_JavascriptInitTutorial/example程序哪个 DOM node 是他的作用对象。现在我们刷新页面(记得刷新下缓存),我们会在 console 中看到:

> Object {config: "value"}
> <div id="one" class="foo">Hello World</div>

你不仅可以使用id选择器,还可以用css class 选择器。

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
".foo": {
    "Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}
}

在 console 中会看到:

> Object {"config":"value"}
> <div id="one" class="foo">Hello World</div>
> Object {"config":"value"}
> <div id="one" class="foo">Goodbye World</div>

通过这样的系统,Magento 鼓励开发者避免在 RequireJS 模块中硬编码 DOM nodes。The x-magento-init means there’s a system level path forward for building Javascript modules that rely on server side rendered JSON, and operate on any arbitrary DOM node. It’s always been possible for Magento module developers to implement their own systems for this sort of functionality, but Magento 2 provides a standard, built in way to achieve this.

Data-mage-init Attribute

除了<script type="text/x-magento-init">方式,还有一种方式可以对特定的 DOM node 调用实现类似功能,就是使用data-mage-init属性。
将 phtml 模板替换成以下内容:

<div data-mage-init='{"Pulsestorm_JavascriptInitTutorial/example": {"another":"example"}}'>A single div</div>

cache:clean后重新载入页面,在console 中你应该看到:

> Object {another: "example"}
> <div>A single div</div>

这里,我们给特定的div 增加了一个data-mage-init属性。这个属性的值是一个 JSON 对象。类似于x-magento-init,这个对象的键是我们要调用的 RequireJS 模块或是 Magento Javascript Component,他的值是要传递给js component config 参数的 JSON 对象。

注意,我们的属性中使用的是单引号

<div data-mage-init='...'>A single div</div>

这是必要的,data-mage-init属性会严格按照 JSON 格式解析,就是说 JSON 对象必须使用双引号,所以我们的属性就只能用单引号了。

Wrap up

不管是用<script type="text/x-magento-init">还是用data-mage-init, both these techniques provide a standard, system unified way of introducing Javascript entry points onto your page.Many of Magento’s front end and back end UI features rely on this syntax, so even if you personally eschew them, understanding how these systems work is an important part of being a Magento 2 developer.

示例代码下载

https://github.com/PiscesThankIT/JavascriptInitTutorial

发表评论

电子邮件地址不会被公开。 必填项已用*标注