UI组件的PHP代码呈现了如下所示的javascript初始化
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
页面中的这段代码意味着Magento将调用Magento_Ui/js/core/app
RequireJS模块以获取回调,然后通过将该{types:..., components:...}
JSON对象作为参数传递来调用该回调(data
如下)
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
define([
'./renderer/types',
'./renderer/layout',
'Magento_Ui/js/lib/ko/initialize'
], function (types, layout) {
'use strict';
return function (data) {
types.set(data.types);
layout(data.components);
};
});
数据对象包含呈现UI组件所需的所有数据,以及将某些字符串与某些Magento RequireJS模块链接的配置。该映射发生在types
和layout
RequireJS模块中。该应用程序还加载Magento_Ui/js/lib/ko/initialize
RequireJS库。该initialize
模块启动了Magento的KnockoutJS集成。
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
/** Loads all available knockout bindings, sets custom template engine, initializes knockout on page */
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
define([
'ko',
'./template/engine',
'knockoutjs/knockout-repeat',
'knockoutjs/knockout-fast-foreach',
'knockoutjs/knockout-es5',
'./bind/scope',
'./bind/staticChecked',
'./bind/datepicker',
'./bind/outer_click',
'./bind/keyboard',
'./bind/optgroup',
'./bind/fadeVisible',
'./bind/mage-init',
'./bind/after-render',
'./bind/i18n',
'./bind/collapsible',
'./bind/autoselect',
'./extender/observable_array',
'./extender/bound-nodes'
], function (ko, templateEngine) {
'use strict';
ko.setTemplateEngine(templateEngine);
ko.applyBindings();
});
每个单独的bind/...
RequireJS模块都为Knockout 设置了一个自定义绑定。
该extender/...
RequireJS模块的一些辅助方法添加到本地KnockoutJS对象。
Magento还在./template/engine
RequireJS模块中扩展了Knockout的javascript模板引擎的功能。
最后,Magento调用applyBindings()
KnockoutJS对象。通常,这是Knockout程序将视图模型绑定到HTML页面的地方-但是,Magento调用时applyBindings
没有视图模型。这意味着淘汰赛将开始将页面作为视图处理,但没有数据绑定。
在股票淘汰赛设置中,这有点傻。但是,由于前面提到的自定义Knockout绑定,Knockout有很多做事情的机会。
我们对范围绑定感兴趣。您可以在同样由HTML UI组件系统呈现的HTML中看到这一点。
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
<div class="spinner">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<!-- ko template: getTemplate() --><!-- /ko -->
<script type="text/x-magento-init">
</script>
</div>
具体来说,data-bind="scope: 'customer_listing.customer_listing'">
属性。当Magento启动时applyBindings
,Knockout将看到此自定义scope
绑定,并调用./bind/scope
RequireJS模块。应用自定义绑定的能力是纯KnockoutJS。范围绑定的实现是Magento Inc.所做的。
范围绑定的实现在
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
该文件的重要位置在这里
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
无需赘述,该registry.get
方法将使用component
变量中的字符串作为标识符提取已经生成的对象,并将其applyComponents
作为第三个参数传递给该方法。字符串标识符是scope:
(customer_listing.customer_listing
上面的)的值
在 applyComponents
function applyComponents(el, bindingContext, component) {
component = bindingContext.createChildContext(component);
ko.utils.extend(component, {
$t: i18n
});
ko.utils.arrayForEach(el.childNodes, ko.cleanNode);
ko.applyBindingsToDescendants(component, el);
}
调用createChildContext
将基于已实例化的组件对象创建实质上是一个新的viewModel对象,然后将其div
应用于所使用的原始对象的所有后代元素data-bind=scope:
。
那么,什么是已经实例化的组件对象?还记得打layout
回的电话app.js
吗?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
的layout
功能/模块将下降到在传递data.components
(再次,此数据来自在经由传递的对象text/x-magento-init
)。对于找到的每个对象,它将寻找一个config
对象,并在该配置对象中寻找一个component
钥匙。如果找到组件密钥,它将
使用RequireJS
返回一个模块实例-仿佛模块都在一个叫做requirejs
/ define
依赖。
将该模块实例称为javascript构造函数
将生成的对象存储在registry
对象/模块中
所以,要花很多钱。这是使用
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
<div class="spinner">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<!-- ko template: getTemplate() --><!-- /ko -->
<script type="text/x-magento-init">
</script>
</div>
作为起点。该scope
值customer_listing.customer_listing
。
如果我们从text/x-magento-init
初始化中查看JSON对象
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
我们看到该components.customer_listing.customer_listing
对象有一个config
对象,而config对象有一个component
设置为的对象uiComponent
。该uiComponent
字符串是RequireJS模块。实际上,它是与Magento_Ui/js/lib/core/collection
模块相对应的RequireJS别名。
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
在中layout.js
,Magento具有与以下等效的运行代码。
//The actual code is a bit more complicated because it
//involves jQuery's promises. This is already a complicated
//enough explanation without heading down that path
require(['Magento_Ui/js/lib/core/collection'], function (collection) {
object = new collection({/*data from x-magento-init*/})
}
出于真正的好奇,如果您查看集合模型并遵循其执行路径,您会发现这collection
是一个JavaScript对象,lib/core/element/element
模块和lib/core/class
模块均对其进行了增强。研究这些自定义项超出了此答案的范围。
实例化后,layout.js
将其存储object
在注册表中。这意味着当淘汰赛开始处理绑定并遇到自定义scope
绑定时
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento将从注册表中获取该对象,并将其绑定为视图模型,以将其绑定到div
。换句话说,getTemplate
当Knockout调用无标签绑定(<!-- ko template: getTemplate() --><!-- /ko -->
)时调用的getTemplate
方法是new collection
对象上的方法。