Odoo 支持三种不同类型的 javascript 文件:
- 纯 javascript 文件(无模块系统),
- 原生 javascript 模块。
- Odoo JS模块(使用自定义模块系统),
所有 javascript 文件都捆绑在一起并提供给浏览器。请注意,本机 javascript 文件由 Odoo 服务器处理并转换为 Odoo 自定义模块。
让我们简要解释一下每种 javascript 文件背后的用途。纯 javascript 文件应仅保留用于外部库和一些小的特定低级用途。所有新的 javascript 文件都应该在本机 javascript 模块系统中创建。自定义模块系统仅对旧的、尚未转换的文件有用。
1、纯 Javascript 文件
普通的 javascript 文件可以包含任意内容。
(function () {
// some code here
let a = 1;
console.log(a);
})();
此类文件的优点是我们避免将局部变量泄漏到全局范围。
注:在 Odoo 中,所有外部库都作为纯 javascript 文件加载。
2、原生 Javascript 模块
/** @odoo-module **/
import { someFunction } from './file_b';
export function otherFunction(val) {
return someFunction(val + 3);
}
注意第一行的注释:它描述了这个文件应该被转换。
任何没有此注释的文件都将保持原样。然后这个文件将被翻译成如下所示的 Odoo 模块:
odoo.define('@web/file_a', function (require) {
'use strict';
let __exports = {};
const { someFunction } = require("@web/file_b");
__exports.otherFunction = function otherFunction(val) {
return someFunction(val + 3);
};
return __exports;
)};
其实,转换基本上是在顶部添加odoo.define,并更新导入/导出语句。
另一个重点是翻译后的模块有一个正式名称: @web/file_a。这是模块的实际名称。位于 addons中的每个文件 some_addon/static/src/path/to/file.js都将被分配一个以模块名称为前缀的名称,如下所示:@some_addon/path/to/file。
导入支持简短写法,但前提是JS模块位于同一个 addon模块中。所以,假设我们有以下文件结构:
addons/
web/
static/
src/
file_a.js
file_b.js
stock/
static/
src/
file_c.js
该文件file_b可以像这样导入file_a:
/** @odoo-module **/
import {something} from `./file_a`
但file_c需要使用全名:
/** @odoo-module **/
import {something} from `@web/file_a`
ES6(owl)模块取别名
/** @odoo-module alias=web.someName**/
import { someFunction } from './file_b';
export default function otherFunction(val) {
return someFunction(val + 3);
}
然后,翻译后的模块具有相同的别名:
odoo.define(`web.someName`, function(require) {
return require('@web/file_a')[Symbol.for("default")];
});
限制:
出于性能原因,Odoo 不使用完整的 javascript 解析器来转换原生模块。因此,存在许多限制,包括但不限于:
*import 或 export前不能有非空字符
*多行注释或字符串不能有以import 或 export开头
// 支持
import X from "xxx";
export X;
export default X;
import X from "xxx";
/*
* import X ...
*/
/*
* export X
*/
// 不支持
var a= 1;import X from "xxx";
/*
import X ...
*/
*导出对象不能有注释
// 支持
export {
a as b,
c,
d,
}
export {
a
} from "./file_a"
// 不支持
export {
a as b, // this is a comment
c,
d,
}
export {
a /* this is a comment */
} from "./file_a"
*Odoo 需要明确模块是由路径(like ./views/form_view)还是名称(like web.FormView)描述的。如果以/开头,则将其视为路径。这意味着 Odoo 不再真正支持带有 / 的模块名称。
3、odoo的JS模块
Odoo 定义了一个小模块系统(位于文件 addons/web/static/src/js/boot.js中,需要先加载)。受 AMD 启发的 Odoo 模块系统通过define 在全局 odoo 对象上定义函数来工作。然后我们通过调用该函数来定义每个 javascript 模块。在 Odoo 框架中,模块是一段将尽快执行的代码。它有一个名称和可能的一些依赖项。当它的依赖项被加载时,一个模块也将被加载。模块的值就是定义模块的函数的返回值。
例如,:
// in file a.js
odoo.define('module.A', function (require) {
"use strict";
var A = ...;
return A;
});
// in file b.js
odoo.define('module.B', function (require) {
"use strict";
var A = require('module.A');
var B = ...; // something that involves A
return B;
});
定义模块的另一种方法是在第二个参数中明确给出依赖项列表。
odoo.define('module.Something', ['module.A', 'module.B'], function (require) {
"use strict";
var A = require('module.A');
var B = require('module.B');
// some code
});
如果某些依赖项丢失/未准备好,则根本不会加载模块。几秒钟后控制台中会出现警告。
需要注意的是,不支持循环依赖。
定义一个模块
该odoo.define方法有三个参数:
moduleName: javascript 模块的名称。它应该是一个唯一的字符串。约定是 odoo 模块的名称后跟特定描述。例如,web.Widget描述web 插件中定义的一个模块,它导出一个Widget类(因为第一个字母是大写的)
如果名称不唯一,则会抛出异常并显示在控制台中。
dependencies: 第二个参数是可选的。如果给定,它应该是一个字符串列表,每个字符串对应一个 javascript 模块。这描述了在执行模块之前需要加载的依赖项。如果这里没有明确给出依赖关系,那么模块系统将通过调用 toString 从函数中提取它们,然后使用正则表达式查找所有require语句。
odoo.define('module.Something', ['web.ajax'], function (require) {
"use strict";
var ajax = require('web.ajax');
// some code here
return something;
});
最后一个参数是定义模块的函数。它的返回值是模块的值,可以传递给其他需要它的模块。请注意,异步模块有一个小例外,请参阅下一节。
如果发生错误,它将在控制台中记录(在调试模式下):
Missing dependencies:这些模块不会出现在页面中。JavaScript 文件可能不在页面中或模块名称错误
Failed modules: 检测到 javascript 错误
Rejected modules: 模块返回一个被拒绝的 Promise。它(及其相关模块)未加载。
Rejected linked modules: 依赖于被拒绝模块的模块
Non loaded modules: 依赖于缺失或失败模块的模块
异步模块
模块在准备好之前可能需要执行一些工作。例如,它可以执行 rpc 来加载一些数据。在这种情况下,模块可以简单地返回一个promise。模块系统将在注册模块之前简单地等待promise完成。
odoo.define('module.Something', function (require) {
"use strict";
var ajax = require('web.ajax');
return ajax.rpc(...).then(function (result) {
// some code here
return something;
});
});