有时,我们需要自定义用户界面的工作方式。一些受支持的应用程序接口可以满足许多常见需求。例如,所有注册表都是很好的扩展点:字段注册表允许添加/删除专门的字段组件,或者主组件注册表允许添加应始终显示的组件。
然而,在某些情况下,这些注册表并不足够。在这种情况下,我们可能需要在原处修改一个对象或一个类。为此,Odoo 提供了实用功能 patch。它主要用于覆盖/更新某些不受自己控制的其他组件/代码的行为。
说明
补丁功能位于 @web/core/utils/patch 中:
patch(objToPatch, extension)
修补简单对象
下面是一个如何修补对象的简单示例:
import { patch } from "@web/core/utils/patch";
const object = {
field: "a field",
fn() {
// do something
},
};
patch(object, {
fn() {
// do things
},
});
在修补函数时,我们通常希望能够访问父函数。为此,我们只需使用本地 super 关键字即可:
patch(object, {
fn() {
super.fn(...arguments);
// do other things
},
});
super 只能在方法中使用,不能在函数中使用。这意味着以下结构对 javascript 无效。
const obj = {
a: function () {
// Throws: "Uncaught SyntaxError: 'super' keyword unexpected here"
super.a();
},
b: () => {
// Throws: "Uncaught SyntaxError: 'super' keyword unexpected here"
super.b();
},
};
还支持getter和setter:
patch(object, {
get number() {
return super.number / 2;
},
set number(value) {
super.number = value;
},
});
修补 JavaScript 类
补丁功能可用于任何对象或 ES6 类。
不过,由于 javascript 类采用原型继承方式,因此当我们希望对类中的标准方法打补丁时,实际上需要对原型打补丁:
class MyClass {
static myStaticFn() {...}
myPrototypeFn() {...}
}
// this will patch static properties!!!
patch(MyClass, {
myStaticFn() {...},
});
// this is probably the usual case: patching a class method
patch(MyClass.prototype, {
myPrototypeFn() {...},
});
此外,Javascript 会以一种特殊的本地方式处理构造函数,因此无法对其进行修补。唯一的解决办法是调用原始构造函数中的一个方法,然后对该方法进行修补:
class MyClass {
constructor() {
this.setup();
}
setup() {
this.number = 1;
}
}
patch(MyClass.prototype, {
setup() {
super.setup(...arguments);
this.doubleNumber = this.number * 2;
},
});
修补组件
组件是由 javascript 类定义的,因此上述所有信息仍然有效。因此,Owl 组件应使用设置方法,这样它们也可以很容易地打补丁:
patch(MyComponent.prototype, {
setup() {
useMyHook();
},
});
删除补丁
patch 函数会返回对应的补丁。这主要用于测试目的,即在测试开始时打上补丁,并在测试结束时取消补丁。
const unpatch = patch(object, { ... });
// test stuff here
unpatch();
将同一修补程序应用于多个对象
const obj1 = {
method() {
doSomething();
},
};
const obj2 = {
method() {
doThings();
},
};
function createExtensionObj() {
return {
method() {
super.method();
doCommonThings();
},
};
}
patch(obj1, createExtensionObj());
patch(obj2, createExtensionObj());