问题描述
我正在试用 Mithril.js 并制作了三个小组件。
let Map = (function() {
let Map = {};
let parent;
Map.oninit = function(vnode) {
parent = vnode.attrs.parent;
}
Map.view = function(vnode) {
return m("p",parent)
}
Map.onupdate = function(vnode) {
console.log(parent);
}
return Map;
})();
let Popup = (function() {
let Popup = {};
Popup.view = function(vnode) {
return m(Map,{parent: "Popup"});
}
return Popup;
})();
let Dashboard = (function() {
let Dashboard = {};
Dashboard.view = function(vnode) {
return m("div",[
m(Map,{parent: "Dashboard"}),m(Popup),m("button",{
onclick: function(e) {
//Click event that doesn't do anything. Just added to trigger Mithril redraw
}
},"Redraw")
]);
}
return Dashboard;
})();
m.mount(document.body,Dashboard);
主要的外部组件是 Dashboard,应该显示 Map 组件和 Popup 组件。 Popup 组件显示了 Map 的另一个实例。当显示 Map 组件时,我发送一个带有父组件名称的字符串作为属性,并设置一个等于该属性的局部变量。 Map 组件只返回一个包含此变量的简单段落以显示父组件的名称。仪表板显示两个包含父组件名称的段落和一个按钮,以便 Mithril 在点击时重绘。
在初始化时,它按预期工作并显示一个包含“仪表板”的段落、一个包含“弹出窗口”的段落和一个按钮。问题是,当我单击按钮更新 DOM 时,两个段落现在都显示“弹出窗口”。因此,出于某种原因,两个实例都使用最新值。我可以通过将 Map 组件重写为不是 IIFE 来解决这个问题,如下所示:
function Map() {
let parent;
return {
oninit : function(vnode) {
parent = vnode.attrs.parent;
},view : function(vnode) {
return m("p",parent);
},onupdate: function(vnode) {
console.log(parent);
}
}
}
Link to JSFiddle showing the problem
为什么作为 IIFE 的版本没有像我预期的那样工作,我怎样才能让它工作,同时仍然保持作为 IIFE?
解决方法
就像@Bergi 所说的,可能更容易放弃 IIFE。在这里,我将您必须使用的闭包样式组件转换为 closure-component-state。您可以使用 POJO 作为组件,但我不推荐它,因为状态令人困惑。
当您立即调用该函数时,您将返回一个 Map 对象。该对象在 Popup 中使用,因此您将在所有代码中使用相同的对象。我敢肯定有一个巨大的大脑的人可以跟踪所有的调用来确定确切的位置,要么值只设置一次,要么值发生冲突,但不管你不能将父级设置为两个同一个 Map 对象上的不同内容。令人惊讶的是它第一次工作,但正如您所见,它是不可靠的。
我移除了 IIFE 并使用 Component 后缀重命名了组件,以使代码更清晰。
let MapComponent = function() {
let Map = {};
let parent;
Map.oninit = function(vnode) {
parent = vnode.attrs.parent;
}
Map.view = function(vnode) {
return m("p",parent)
}
Map.onupdate = function(vnode) {
console.log(parent);
}
return Map;
};
let PopupComponent = function() {
let Popup = {};
Popup.view = function(vnode) {
return m(MapComponent,{parent: "Popup"});
}
return Popup;
};
let Dashboard = (function() {
let Dashboard = {};
Dashboard.view = function(vnode) {
return m("div",[
m(MapComponent,{parent: "Dashboard"}),m(PopupComponent),m("button",{
onclick: function(e) {
//Click event that doesn't do anything. Just added to trigger Mithril redraw
}
},"Redraw")
]);
}
return Dashboard;
})();
m.mount(document.body,Dashboard);