补丁代码

有时,我们需要自定义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 the obj object (or class) and applies all key/value described in the patchValue object. This operation is registered under the patchName 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");