<script type="text/x-magento-init">

我们在 magento 2 的 phtml 中经常会看到 <script type="text/x-magento-init">data-mage-init ,那么这两个标签是用来做什么的?本篇将给出解释,并说明这样设计的用意。

Magento 2 的自定义设计

首先 <script type="text/x-magento-init"> 是 Magento 2 的自定义设计。

我们新建一个 html 文件:

<!DOCTYPE html>
<html>
<head>
    <title>test</title>
</head>
<body>
<script type="text/javascript">
    alert('common');
</script>
<script type="text/x-magento-init">
    alert('magento');
</script>
</body>
</html>

然后从浏览器中访问他,我们会看到 common 的 alert 但不会看到 magento 的 alert 。这是因为浏览器不认识 type="text/x-magento-init" 的这个 <script>,所以会直接忽略它。

创建一个演示模块

我们还是以 ThankIT_HelloWorld 为基础。

创建 Controller

File:app\code\ThankIT\HelloWorld\Controller\Javascript\Example.php

<?php
namespace ThankIT\HelloWorld\Controller\Javascript;

class Example extends \Magento\Framework\App\Action\Action
{

    protected $resultPageFactory;

    /**
     * Constructor
     *
     * @param \Magento\Framework\App\Action\Context  $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory
    ) {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }

    /**
     * Execute view action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        return $this->resultPageFactory->create();
    }
}

创建 layout

File:app\code\ThankIT\HelloWorld\view\frontend\layout\helloworld_javascript_example.xml

<?xml version="1.0" ?>
<page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="ThankIT\HelloWorld\Block\Javascript\Example" name="Javascript.Example" template="ThankIT_HelloWorld::javascript/example.phtml"/>
        </referenceContainer>
    </body>
</page>

创建 Block

File:app\code\ThankIT\HelloWorld\Block\Javascript\Example.php

<?php
namespace ThankIT\HelloWorld\Controller\Javascript;

class Example extends \Magento\Framework\App\Action\Action
{

    protected $resultPageFactory;

    /**
     * Constructor
     *
     * @param \Magento\Framework\App\Action\Context  $context
     * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\View\Result\PageFactory $resultPageFactory
    ) {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }

    /**
     * Execute view action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        return $this->resultPageFactory->create();
    }
}

创建 template

File:app\code\ThankIT\HelloWorld\view\frontend\templates\javascript\example.phtml

内容为空

然后刷新缓存,访问 url http://example/helloworld/javascript/example
我们就会看到带 header 和 footer 的页面,内容为空。

这里没有什么稀奇的,只是一个准备工作,预热一下。

创建一个 requirejs 模块

我们先关闭掉 fpc (Full page caching),这样不用每次清缓存看结果。

我们先在 File:app\code\ThankIT\HelloWorld\view\frontend\templates\javascript\example.phtml 中填入以下内容

<script type="text/javascript">
    console.log('hello');
    jQuery(function($){
        alert("Hello World");
    });
</script>

然后我们刷新页面,查看控制台,会发现:
jQuery is not defined

这是因为 Magento 2 使用 RequireJS 做基础,对 JS 进行模块化,默认不会使用全局命名空间,jQuery 也不再暴露到全局空间。我们应该使用 RequireJS 的方式来访问 js 模块。

Magento 2 本身就是集成了 jQuery 的,所以我们可以这样访问:

<script type="text/javascript">
    requirejs(['jquery'], function($) {
        alert("Hello World");
    });
</script>

关于 RequireJS 可以参阅 Magento 2 and RequireJS (翻译)

我们现在创建以下文件:
File: app\code\ThankIT\HelloWorld\view\frontend\web\example.js

define([], function(){
    alert("A simple RequireJS module");
    var mageJsComponent = function()
    {
        alert("A simple magento component.");
    };
    return mageJsComponent;
});

这样我们就定义了一个简单的 RequireJS module,这个模块的位置是 app\code\ThankIT\HelloWorld\view\frontend\web\example.js,那么他的模块名,在没有重新命名的情况下,就是 ThankIT_HelloWorld/example

然后我们来更改 phtml

<script type="text/javascript">
    requirejs(['ThankIT_HelloWorld/example'], function($) {
        alert("Hello World");
    });
</script>

刷新网页,然后我们会先看到弹出框 A simple RequireJS module,然后是 Hello World。就是我们先加载 ThankIT_HelloWorld/example 然后执行 alert("Hello World");

好,这样看来,我们已经会给 Magento 2 加 js module ,并且在 phtml 中使用它了。

要了解更详细的解释,请参阅 Magento 2 Javascript Init Scripts(翻译)

<script type="text/x-magento-init">

让我们来修改 phtml 为以下内容:

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

example 的内容不变:

define([], function(){
    alert("A simple RequireJS module");
    var mageJsComponent = function()
    {
        alert("A simple magento component.");
    };
    return mageJsComponent;
});

这时候刷新页面(有时也需要情况浏览器缓存),我们会发现,这次先弹出 A simple RequireJS module 然后弹出 A simple magento component.

上面我们用 requirejs 的方法,并没有弹出 A simple magento component. 也就是说,<script type="text/x-magento-init"> 方式不仅会使用 requirejs 的方式加载该模块,而且还湖调用它。

传参

phtml 改成:

<div id="text"></div>
<script type="text/x-magento-init">
    {
        "#text": {
            "ThankIT_HelloWorld/example":{"config":{"text":"Hello Magento 2, Hello ThankIT ..."}}
        }
    }
</script>

example 改成:

define(['jquery'], function($){
    var setText = function(config, node) {
        console.log(config);
        console.log(node);
        $(node).html(config.config.text);
    }
    return setText;
});

清空浏览器缓存,刷新页面,这时候,我们的页面会是这样的:
magento 2 x-magento-init
magento 2 x-magento-init console log

不过这里console.log 出的结果有点奇怪,按照我的理解,输出的 node 不应该含有 Hello Magento 2, Hello ThankIT ... 内容。。。有清楚的朋友请给我留个言吧,感谢!

通过上面这个例子,我们知道 <script type="text/x-magento-init"> 方式可以传递要作用的节点,可以传递配置参数,并且之后被调用执行。通过这样的设计,Magento 鼓励开发者避免在 RequireJS 模块中硬编码 DOM 节点。传递的配置参数可以由服务器端生成。这进一步分离的会让代码更清晰。

data-mage-init

这个方式实际上和 <script type="text/x-magento-init"> 是一样的。
我们把 phtml 改成下面这样:

<div data-mage-init='{"ThankIT_HelloWorld/example": {"config":{"text":"Hello Magento 2, Hello ThankIT ... use data-mage-init ..."}}}'>A single div</div>

刷新页面,是不是先看到 A single div ,随后被替换成了 Hello Magento 2, Hello ThankIT ... use data-mage-init ...

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

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

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

关于如何实现的 type=”text/x-magento-init”

The code that uses them is located in lib/web/mage/apply/scripts.js

参考 In Magento2 what is<script type=“text/x-magento-init”>?

参考文档

Magento 2 and RequireJS (翻译)
Magento 2 Javascript Init Scripts(翻译)

发表评论

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