前端

Odoo 前端跳转为什么不是“doAction 一把梭”:action service、client action 与导航分流讲透

很多人把 Odoo 前端跳转理解成“拿到 action 就 doAction”,但 action_service.js 真正复杂的地方在分流:不同 action type 会走完全不同的执行器,client action 还要经过 registry、extractProps、target 与 UI 更新逻辑。本文专门讲透 doAction 到 client action 的导航契约,而不是只讲 router 状态。

前端
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 7 阅读

提到 Odoo 前端跳转,很多人第一反应就是一句:

  • this.actionService.doAction(...)

这当然没错,但只说到这一步,其实什么都还没解释。

因为 doAction() 在 Odoo 里根本不是“执行一个统一页面跳转”的单函数,而更像一个动作分发器。看 addons/web/static/src/webclient/actions/action_service.js 就会发现,它真正做的是:

  1. 先加载 action;
  2. 再按 action.type 分流执行;
  3. 不同类型进入完全不同的 UI 装配路径;
  4. 其中 ir.actions.client 又是一套单独的 registry 与 controller 生成逻辑。

所以这篇文章想讲透的不是 router 状态,而是:

Odoo 为什么要把“执行动作”设计成 doAction 分流,而不是单一路由跳转。

一、doAction() 的第一身份是 dispatcher,不是 navigator

源码里 doAction(actionRequest, options) 的关键结构非常直白:

  • _loadAction() 先把 request 解析成完整 action;
  • _preprocessAction() 做预处理;
  • 然后按 action.type 进入不同分支:
  • ir.actions.act_url
  • ir.actions.act_window
  • ir.actions.act_window_close
  • ir.actions.client
  • ir.actions.server
  • ir.actions.report

这已经说明一个事实:

Odoo 眼里的 action 不是“页面地址”,而是“可执行的前端/后端动作描述”。

也正因为如此,doAction() 本质上更接近 command dispatcher,而不是 Vue Router / React Router 那类纯路径导航器。

二、为什么 act_windowclient action 必须分开看

很多文章会把它们都归为“前端跳转”,但在 Odoo 内部,这两类 action 的装配过程差异非常大。

act_window 更像“围绕模型视图建立 controller”

它通常要处理:

  • 视图列表;
  • 当前 viewType;
  • model / resId / domain / context;
  • 是否需要多记录视图;
  • 是否要切换现有 controller。

client action 更像“按 tag 从 registry 取前端入口组件”

_executeClientAction() 里,官方会:

  1. action.tagactionRegistry 取 client action;
  2. 继承 registry 项上的 pathtarget
  3. 若它是一个 OWL Component,就执行 extractProps?.(action)
  4. _makeController()_getActionInfo() 包成 controller;
  5. 最后 _updateUI(controller, options)

这就很关键。

它说明 client action 不是“后端视图的一种别名”,而是通过 registry 挂进 Web Client 的前端动作入口

三、extractProps 暴露了 client action 的真正边界

_executeClientAction() 里有一行非常值得注意:

  • const props = clientAction.extractProps?.(action) || {}

很多人会忽略它,但它其实和字段系统里的 extractProps 是同一种哲学:

action 描述不是组件 props,必须先翻译一层。

这说明一个 client action 的设计边界很清楚:

  • 后端 action 负责声明“要执行什么”;
  • registry 项负责把 action 描述翻译成组件所需 props;
  • controller 与 UI 系统负责把它接进当前 Web Client。

所以写 client action 时,如果一上来就把业务逻辑塞进全局 service 或外层页面,而不尊重这层翻译边界,后面就很难维护。

四、clearUncommittedChanges() 说明跳转不是纯渲染行为

无论是 act_window 还是很多 client action 路径,Odoo 都会在合适时机调用 clearUncommittedChanges()

这一步非常重要,因为它揭示了 action service 的真实职责:

  • 不只是生成下一个页面;
  • 还要协调当前页面是否允许离开。

也就是说,doAction 不是“立刻换屏”,而是带有工作流责任的导航入口。

这和桌面 ERP 的体验很一致:

  • 你当前编辑了东西;
  • 跳走前要么保存、要么确认、要么阻止离开;
  • 导航必须服从当前控制器状态。

五、为什么 client action 适合承接“非传统视图页面”

一旦理解 _executeClientAction() 的结构,就会明白 client action 特别适合什么:

  • 仪表盘;
  • 报表容器;
  • 配置向导;
  • 完全前端驱动的交互页面;
  • 某些不以标准 form/list/kanban 为核心的入口。

因为它不是从 view arch 编译出来,而是直接以 registry 组件作为运行入口。

但这也意味着它要自己更明确地处理:

  • props 结构;
  • target 行为;
  • 与 breadcrumbs / title / action stack 的接入方式。

六、开发时最容易犯的两个误区

误区一:把所有跳转都理解成 router 改 URL

其实 router 只是状态承载层之一。真正决定怎么执行的是 action service 对 action type 的分流。

误区二:把 client action 当“特殊页面组件”,不当成动作契约

如果只把 client action 看成“某个组件直接打开”,就容易漏掉:

  • registry 注册;
  • extractProps
  • target 约束;
  • UI 更新与 controller 包装;
  • 离开保护。

这样写出来的东西通常一开始能跑,后面很快和 Odoo Web Client 的整体导航契约脱节。

结语

doAction() 真正厉害的地方,不是把所有跳转统一成一个 API,而是把不同动作类型放进统一入口、分流执行:

  • act_window 负责标准视图工作流;
  • client action 负责 registry 驱动的前端入口;
  • 其他 action 再走各自执行器。

所以更准确地说:

Odoo 前端导航不是“调用 doAction 结束”,而是“从 doAction 开始,进入一套按动作语义分流的 UI 执行契约”。

这也是为什么 Odoo 的 action system 比普通单页路由更像一套工作流引擎。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。