我正在尝试在没有控制器的情况下在 JS 中实现观察者模式,IE:模型只是在其数据更改时重新呈现相关视图。
我的问题是我需要将一个
view
传递给model
的registerObserver
函数作为参考,否则我无法检查观察者是否尚未注册,这里:
if (!this.observers.includes(observer))
this.observers.push(observer)
如果我无法检查观察者,它们会在每次重新渲染时不断添加,所以我在
nestedViews
属性中保留对它们的引用。但是我正在努力解决父视图也可能需要观察模型的问题 - 所以我添加了对self
的引用。但是,如果我在模型更改时重新渲染父视图,我将丢失对嵌套视图的引用,并且会出现同样的问题。
相对于这种方法,我是 webdev 的新手(这只是一个改进我的 vanilla js 的练习),所以我确定这一定是一个我看不到的简单修复方法?
父视图:
class View {
nestedViews = {};
self = this;
constructor(parentEl, model){
this.model = model;
this.parentEl = parentEl;
}
update() { this.render(); }
render() {
// method to render child classes
this.onRender();
// bind model
this.bindModel();
//... Logic to insert the item into the dom
}
}
class NestedView extends View {
bindModel() {
Object.entries(this.nestedViews).forEach(([key, value]) =>
this.model.registerObserver(value)
);
}
onRender() {
this.nestedViews['nestedView1'] = new NestedView1(
parent,
this.model
);
this.nestedViews['nestedView1'].render();
this.nestedViews['nestedView2'] = new NestedView2(
parent,
this.model
);
this.nestedViews['nestedView2'].render();
}}
型号:
class Model {
observers = [];
registerObserver(observer) {
if (!this.observers.includes(observer)) this.observers.push(observer);
}
unregisterObserver(observer){
this.observers = this.observers.filter((obs) => obs !== observer);
}
updateObservers() {
this.observers.forEach((observer) => observer.update());
}
set() {
// set attr
this.updateObservers();
}
}
谢谢
我想我理解你面临的问题。您希望避免每次模型更改时都重新渲染父视图,因为这会导致对嵌套视图的引用丢失。一种解决方案是使用单独的方法来更新嵌套视图,并从父视图的更新方法中调用它。这样,父视图只负责更新嵌套视图,而不是重新渲染它们。
这是一个示例,说明如何修改代码以实现此目的:
class View {
nestedViews = {};
constructor(parentEl, model) {
this.model = model;
this.parentEl = parentEl;
}
update() {
// Update nested views without re-rendering the parent view
this.updateNestedViews();
}
updateNestedViews() {
Object.values(this.nestedViews).forEach((nestedView) => nestedView.update());
}
render() {
// method to render child classes
this.onRender();
// bind model
this.bindModel();
}
}
class NestedView extends View {
bindModel() {
Object.values(this.nestedViews).forEach((nestedView) =>
this.model.registerObserver(nestedView)
);
}
onRender() {
this.nestedViews['nestedView1'] = new NestedView1(this.parentEl, this.model);
this.nestedViews['nestedView1'].render();
this.nestedViews['nestedView2'] = new NestedView2(this.parentEl, this.model);
this.nestedViews['nestedView2'].render();
}
}
在这个解决方案中,父视图的更新方法不会重新渲染自己;相反,它调用 updateNestedViews,后者又调用每个嵌套视图的更新方法。这应该有助于维护对嵌套视图的引用,并防止它们被多次注册为观察者。
此外,考虑在您的模型类中使用以下经过修改的 registerObserver 方法,以便在观察者被销毁或不再需要时自动注销观察者:
class Model {
// ...
registerObserver(observer) {
if (!this.observers.includes(observer)) {
this.observers.push(observer);
// Automatically unregister the observer when it's destroyed
const unregister = () => {
this.unregisterObserver(observer);
};
observer.onDestroy(unregister);
}
}
// ...
}
在您的 View 类中,添加 onDestroy 方法:
class View {
// ...
onDestroy(callback) {
this._onDestroyCallback = callback;
}
destroy() {
if (this._onDestroyCallback) {
this._onDestroyCallback();
}
}
// ...
}
这样,您可以在不再需要视图时调用 destroy 方法,它会自动从模型中注销自己作为观察者。