我正在查看nativescript hello world typescript repo,我遇到了一些乏味的工作,使用nativescript的可观察量实现。
如果您查看视图模型是如何定义的,您会发现它是一个简单地扩展Observable
库的类。每当为属性定义setter
方法时, 您需要手动调用super.notifyPropertyChange("propertyName", propertyValue);
恕我直言,如果您有具有许多属性的视图模型,则此过程效率非常低且容易出错。
有没有办法自动执行此任务?(也许有一个基类告诉任何二传手notifyPropertyChange
? 如果没有,您如何处理这个问题?Observable
机制还有其他实现吗?
以下是我在几个生产应用程序中使用的内容:
import { Observable } from "data/observable";
export class ObservableModel extends Observable {
constructor() {
super();
}
public get(propertyName: string) {
return this["_" + propertyName];
}
public set(propertyName: string, value) {
if (this["_" + propertyName] === value) {
return;
}
this["_" + propertyName] = value;
this.refresh(propertyName);
}
public refresh(propertyName: string) {
super.notify({
eventName: Observable.propertyChangeEvent,
propertyName,
object: this,
value: this["_" + propertyName],
});
}
}
然后,您的模型如下所示:
export class LoginViewModel extends ObservableModel {
get userName(): string { return this.get("userName"); }
set userName(val: string) { this.set("userName", val); }
get password(): string { return this.get("password"); }
set password(val: string) { this.set("password", val); }
}
当您需要使用您只使用的值时:
const vm = new LoginViewModel();
vm.userName = "jdoe";
vm.password = "$3cr3T";
更新装饰器实现:
export function ObservableProperty() {
return (target: Observable, propertyKey: string) => {
Object.defineProperty(target, propertyKey, {
get: function () {
return this["_" + propertyKey];
},
set: function (value) {
if (this["_" + propertyKey] === value) {
return;
}
this["_" + propertyKey] = value;
this.notify({
eventName: Observable.propertyChangeEvent,
propertyName: propertyKey,
object: this,
value,
});
},
enumerable: true,
configurable: true
});
};
}
型:
export class LoginViewModel extends Observable {
@ObservableProperty() public userName: string;
@ObservableProperty() public password: string;
}
它确实很冗长且容易出错。这是违反 DRY 的行为,应避免。有很多方法可以使用JavaScript干净地完成此操作。
一种方法可能是使用修饰器以通用方式自动连接所有样板文件,并保持模型类的清洁和声明性。
例如,我们可以创建以下函数
观察到的.ts
export default function<T extends Notifier<T, K>, K extends keyof T>(target: T, key: K) {
let storedValue = target[key];
const get = () => storedValue;
const set = (value: T[K]) => {
if (storedValue !== value) {
storedValue = value;
target.notifyPropertyChange(key, storedValue);
}
};
Object.defineProperty(target, key, {
get,
set,
enumerable: true,
configurable: false
});
}
export interface Notifier<T, K extends keyof T> {
notifyPropertyChange(key: K, value: T[K]): void;
}
现在我们可以使用它从模型本身中删除所有样板。我们甚至删除了吸气剂和二传手,并使用了简单的属性
模型.ts
// Stub observable class to verify inheritance works correctly (as requested)
class Observable {
notifyPropertyChange(key: string, value: {}) {
console.log(`from super: ${key} ===> ${value}`);
}
}
export class Model extends Observable {
import observed from './observed';
export class Model extends Observable {
@observed name = 'Bob';
@observed age = 38;
@observed birthdate = moment();
notifyPropertyChange<K extends keyof this>(key: K, value: this[K]): void {
super.notifyPropertyChange(key, value);
console.log(`${key} ===> ${value}}`);
}
}
const model = new Model();
model.name = 'Rob';
model.name = 'Robert';
这种方法的一些好处是
它相当干燥
我们有一个比手动定义 getter 和 setter 更简洁易读的模型类
我们的装饰器通过要求类提供
notifyPropertyChange
方法并在正确的属性键上调用它来改进类型检查。如果我们违反这一点,TypeScript 将发出编译器错误用于存储值的实际变量是真正的私有变量。它仅在装饰器的闭包范围内,除非通过吸气器和设置器才能访问。这不是命名约定,它是真正的隐私,老式的JavaScript方式。
- 我们避免引入仅用于代码共享的基类。也就是说,我们可以更喜欢组合而不是继承
- 我们获得了内联初始值设定项和由此产生的类型推断的便利
这可以概括为装饰整个班级,使其更加干燥。