补丁代码¶
有时,我们需要自定义UI的工作方式。许多常见需求都可以通过一些支持的API来实现。例如,所有的注册表都是很好的扩展点:字段注册表允许添加/删除专门的字段组件,或者主组件注册表允许添加应始终显示的组件。
然而,有些情况下这是不够的。在这些情况下,我们可能需要直接修改一个对象或类。为了实现这一点,Odoo提供了实用函数 patch
。它主要用于覆盖/更新某些其他组件/代码片段的行为,而这些组件/代码片段并不受我们控制。
描述¶
patch 函数位于 @web/core/utils/patch
:
- patch(obj, patchName, patchValue, options)¶
- 参数
obj (
Object()
) – 应该被打补丁的对象patchName (
string()
) – 描述补丁的唯一字符串patchValue (
Object()
) – 一个将每个键映射到 patchValue 的对象options (
Object()
) – 选项对象(见下文)
The
patch
function modifies in place theobj
object (or class) and applies all key/value described in thepatchValue
object. This operation is registered under thepatchName
name, so it can be unpatched later if necessary.大多数补丁操作通过使用
_super
属性来访问父值(请参见下面的示例)。为此,patch
方法将每对键/值包装在一个动态绑定_super
的 getter 中。唯一的选项是
pure (boolean)
。如果设置为true
,补丁操作不会绑定_super
属性。
补丁一个简单对象¶
这是一个简单的示例,展示了如何对一个对象进行打补丁:
import { patch } from "@web/core/utils/patch";
const object = {
field: "a field",
fn() {
// do something
},
};
patch(object, "patch name", {
fn() {
// do things
},
});
当修补函数时,我们通常希望能够访问 parent
函数。由于我们使用的是修补对象,而不是ES6类,我们不能使用原生的 super
关键字。因此,Odoo提供了一种特殊的方法来模拟这种行为: this._super
:
patch(object, "_super patch", {
fn() {
this._super(...arguments);
// do other things
},
});
警告
this._super
在每次调用修补函数后都会被重新赋值。这意味着如果您在修补中使用了异步函数,那么在``await``之后就不能调用``this._super``,因为它可能是您期望的函数,也可能不是。正确的做法是保留对初始``_super``方法的引用:
patch(object, "async _super patch", {
async myAsyncFn() {
const _super = this._super.bind(this);
await Promise.resolve();
await _super(...arguments);
// await this._super(...arguments); // this._super is undefined.
},
});
也支持 getter 和 setter:
patch(object, "getter/setter patch", {
get number() {
return this._super() / 2;
},
set number(value) {
this._super(value * 2);
},
});
修补 JavaScript 类¶
The patch
function is designed to work with anything: object or ES6 class.
然而,由于JavaScript类使用原型继承,当一个人希望从一个类中修补一个标准方法时,我们实际上需要修补 prototype
:
class MyClass {
static myStaticFn() {...}
myPrototypeFn() {...}
}
// this will patch static properties!!!
patch(MyClass, "static patch", {
myStaticFn() {...},
});
// this is probably the usual case: patching a class method
patch(MyClass.prototype, "prototype patch", {
myPrototypeFn() {...},
});
此外,Javascript 以一种特殊的本地方式处理构造函数,这使得它无法被修补。唯一的解决方法是调用原始构造函数中的一个方法,然后修补该方法:
class MyClass {
constructor() {
this.setup();
}
setup() {
this.number = 1;
}
}
patch(MyClass.prototype, "constructor", {
setup() {
this._super(...arguments);
this.doubleNumber = this.number * 2;
},
});
警告
无法直接修补类的 constructor
!
补丁组件¶
组件是由javascript类定义的,因此上面的所有信息仍然有效。出于这些原因,Owl组件应该使用 setup
方法,这样它们也可以很容易地进行修补(参见 best practices 部分)。
patch(MyComponent.prototype, "my patch", {
setup() {
useMyHook();
},
});
移除一个补丁¶
函数 patch
有一个对应的函数 unpatch
,也位于 @web/core/utils/patch
。
- unpatch(obj, patchName)¶
- 参数
obj (
Object()
) – 应该取消打补丁的对象patchName (
string()
) – 描述应该被移除的补丁的字符串
从对象
obj
中移除现有的补丁。这在测试目的中非常有用,当我们在测试开始时打补丁,然后在测试结束时取消补丁。patch(object, "patch name", { ... }); // test stuff here unpatch(object, "patch name");