Magento 2 - Alan Storm 博客 - 初始化 JavaScript
Magento 2 - Alan Storm 博客 - 初始化 JavaScript
https://alanstorm.com/magento_2_javascript_init_scripts/
今天我们探索的是 Magento 2 中几种不同的方法,不用插入 <script type="text/javascript"> 标签就能执行 JavaScript 代码。
JavaScript 初始化方法
我们将要讨论的 Magento JavaScript 初始化的方法解决了几个不同的问题。
- 他们规定了一个标准的阻止页面直接插入 JavaScript 的机制。
- 他们规定了一个调用独立 RequireJS 模块的方式。
- 规定了一个服务器端生成的 JSON 对象传递给程序的方式。
记住这四个目标。如果你正苦苦挣扎于这些定制框架特性背后的思维模型,那么它们可能会给你提供帮助。
创建一个模块
今天的教程不涉及太多的 Mangeto PHP 代码,你可以在任何渲染在库存页面的 Magento phtml 来运行这些例子。
我们将使用 pestle 通过运行下面几个命令来创建一个名为 Pulsestorm_JavascriptInitTutorial 的单 URL 端点模块。
$ pestle.phar generate_module Pulsestorm JavascriptInitTutorial 0.0.1 $ pestle.phar generate_route Pulsestorm_JavascriptInitTutorial frontend pulsestorm_javascriptinittutorial $ pestle.phar generate_view Pulsestorm_JavascriptInitTutorial frontend pulsestorm_javascriptinittutorial_index_index Main content.phtml 1column $ php bin/magento module:enable Pulsestorm_JavascriptInitTutorial $ php bin/magento setup:upgrade
对于完成 Magento 2 for PHP MVC developers 这个系列的人来说应该是熟悉这些命令。一旦你运行了以上命令,在你的系统中应该可以访问下面这个 URL - http://magento.example.com/pulsestorm_javascriptinittutorial/ 。并且看到渲染后的 app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml 模板。
创建一个 RequireJS 模块
现在,我们已经创建了一个 Magento 模块,让我们快速浏览一下然后创建一个 RequireJS 模块。首先新建一下这个文件:
//File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/web/example.js define([], function(){ alert("A simple RequireJS module"); return {}; });
这个一个很简单的 RequireJS 模块。根据这个模块在文件系统的位置和 Magento 加载 JavaScript 的方式,这个模块命名为 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>
到这里,我们创建了一个单模块依赖的 RequireJS 模块。这个依赖就是我们创建的模块(Pulsestorm_JavascriptInitTutorial/example)。
在你的系统中加载 http://magento.example.com/pulsestorm_javascriptinittutorial/ 这个URL ,你会看到这个提示信号。
如果对以上内容不熟悉,你可能需要浏览一下 Magento 2 and RequireJS 这篇文章。
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>
修改后重新加载页面,你回看到来自 example.js 的 alter 声明。
如果你之前没有见过这种语法,他可能看起来有点陌生。那我们分开来看。
首先是 <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(跟其他现在的 JavaScript 框架相似)充分利用这一特点。运行的 Magento JavaScript 代码扫描带有 text/x-magento-init 的 script 标签,但是这个不在本文讨论。如果你想自己探索,这个回答是个不错的开始。
我们即将讨论 x-magento-init 代码的另一部分是:
#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml { "Pulsestorm_JavascriptInitTutorial/example":{} }
Magento 将会查询这个对象的 key ,并且把他当作一个 RequireJS 模块。这就是如何加载 example.js 脚本了。
你可能想知道为什么这个对象没有值。你可能也想知道为什么这整个对象是另一个 key 为 * 的对象的一部分:
#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml { "*": {/*...*/} }
在我们讨论这个之前,我们需要讨论 JavaScript 组件。
Magento JavaScript 组件
以上的例子是把 RequireJS 模块当作一段程序来执行。这个可以使用,并且Mangento 自己经常使用 x-magento-init 方法来调用 RequireJS 模块的程序。然而,x-magento-init 的真正的作用是创建一个 Magento JavaScript 组件。
Magento Javascript 组件是返回值为 function 的 RequireJS 模块。Magneto 的系统代码将会以特定的方式调用,展示了额外的功能。
如果你不明白,试着按照以下内容改变你的 RequireJS 模块:
//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 的警告框。
这可能看起来很傻,如果Magneto 所做的都是调用它,那返回一个方法的意义是什么呢?你是对的,但是这因为我们还落下了一些东西。按照以下内容试着更改你的 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; });
如果你重新加载页面,你应该可以看到改变后的警告信息。如果你查看浏览器 Javascript 控制台,应该也会看到:
> Object {config:"value"}
当我们创建了一个 Magento Javascript 组件,Mangento 调用返回的方法,并且包含了来自 text/x-magento-init 的对象。
"Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}
这就是为什么 RequireJS 模块名字是一个 key -- value 是我们想要传入组件的对象。
在这些例子中,这可能是一个有点傻的抽象概念。然而,在真实的模块中,我们将会用 PHP 生成 JSON 。这个系统允许我们在 phtml 模板中渲染 JSON ,然后把他们传入 Javascript 代码中。这个有利于避免直接用 PHP 生成 Javascript 而造成的代码混乱、意外的错误和安全问题。
在结束 x-magento-init 之前,还要讨论最后一个特点。你将会记得我们说过的:
- 提供一种给这个代码传入服务器端生成的 JSON 对象方式
- 提供一种给这个代码提供执行的 DOM 节点方式
我们已经描述了如何传入服务器端生成的 JSON,但是 DOM 节点怎么传入呢?
按照以下内容改变你的 RequireJS 模块:
//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 节点。然而,如果你冲洗加载这个页面,你可以在控制台看到:
> Object {config:"value"} > false
Magento 确实传入了一个 node 值,但是这个值是 false,从哪里来的?
Well,Magento 不可能知道我们想要在哪个 DOM 节点上执行,我们需要告诉他,更改你的 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 节点的特殊情况,这个对象的 key 实际上是一个 CSS/jQuery 样式的选择器,它告诉 Magneto 程序 Pulsestorm_JavascriptInitTutorial/example 应该在哪个 DOM 节点上执行。如果我们重新加载页面,我们可以在控制台看到:
> Object {config: "value"} > <div id="one" class="foo">Hello World</div>
ID 也是不限制的,你也可以使用 CSS 类:
#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml ".foo": { "Pulsestorm_JavascriptInitTutorial/example":{"config":"value"} }
你可以在控制台看到:
> Object {"config":"value"} > <div id="one" class="foo">Hello World</div> > Object {"config":"value"} > <div id="two" class="foo">Goodbye World</div>
通过建立这种系统,Magneto 鼓励开发者避免把 DOM 节点生硬地写到 RequireJS 模块中。x-magento-init 意味着有一个系统级别的方法来建立依赖服务器生成的 JSON 的 Javascript 模块,并且可以执行在任意 DOM 节点。Mangento 模块开发人员始终可以用自己系统来实现这种功能,但是 Magento 2 提供了一种标准的内置方法来实现它。
Data-mage-init 属性
除了 <script type="text/x-magento-init">,还有另一种办法在特定的 DOM 节点上调用相似的功能,就是用 data-mage-init 属性。试着用以下内容替换已有的 phtml 模板:
<div data-mage-init='{"Pulsestorm_JavascriptInitTutorial/example": {"another":"example"}}'> A single div </div>
刷新页面,你可以在 Javascript 控制台看到:
> Object {another: "example"} > <div>A single div</div>
这里,我们给特定的 div 添加了 data-mage-init 属性。这个属性的值是 JSON 对象。跟 x-magento-init 相似,这个对象的 key 是我们想要作为程序调用的 RequireJS 模块或者 Magento Javascript 组件,并且这个值是嵌套的 JSON 对象,作为 config 参数传入 Magento Javascript 组件的方法种。
你可能注意到了,在属性上我们用的是单引号
<div data-mage-init='...'>A single div</div>
这是必须的。data-mage-init 属性的值会被严格的 JSON 解析器解析,这就意味着 JSON 对象的引号必须是双引号,即我们在属性上不能用双引号。在 HTML5 技术上是可以的,但是对于一定年龄的网站开发人员却带来了关于 Microsot Frontpage 的不好的回忆。
总结
无论你最终使用 <script type="text/x-magento-init"> 组件还是在特定节点上使用 data-mage-init 属性,这两个技术都提供了系统标准的方式把 Javascript 引入你的页面。很多 Magento 的前端和后台 UI 功能都依赖于这个语法,所以即使你个人避免他们,理解这些系统如何工作还是成为 Magento 2 开发人员很重要的一部分。