OWL是什么
从odoo 14.0开始,Odoo官方又一次更换了前端技术,推出了新一代的前端框架OWL(Odoo Web Libary)。OWL从odoo独立出来,成为了一个独立的前端框架。其特点是包含一个组件声明系统、基于钩子的响应系统、默认的并发支持和一个存储的前端路由。
为什么是OWL?
为什么odoo没有使用现有的成熟的前端框架比如React和Vue,而是要重新独立开发一套新的框架。odoo官方给出的一个长篇的理由解释。大概有如下几个理由:
- odoo官方不想将前端技术依赖于外界的某个特定公司的产品,想要完全拥有技术的掌控权。
- 尽管函数式编程是一种趋势,但是odoo并不想放弃一些务实的特性,比如继承。
- React和Vue的现有的社区库,不能满足odoo动态编译延迟计算的需求。
- odoo基于Xml布局,替换React和Vue的话要重写编译模块,费时费力且对开发者不够友好。
- JIT编译和响应的需要
- React和Vue的并发特性不满足Odoo前端的需求。
简而言之,就是odoo官方觉得市面上的产品没有可以直接拿来使用的,不如自己开发一套。
一个简单的例子
接下来,我们看一个简单的例子,这个例子将在页面中画一个按钮,单击按钮实现计数器自动加1:
1 | const { Component, useState } = owl; |
首先从owl取Compent、useState和xml组件,然后定义一个Counter组件,使用内置模板,然后定义一个App,加载Counter组件,最后挂载到页面中。
实现的效果如下:
这样我们就完成了一个简单的OWL应用。
基础教程
接下来,我们来写一个学习应用todoApp。我们创建一个文件夹todoApp,里边包含一个index.html,app.css,app.js:
1 | todoapp/ |
然后编写index.html:
1 |
|
app.js:
1 | (function(){ |
刷新页面,将可以从console中看到owl的版本号。
添加第一个组件
Owl应用是由组件组成的,每个应用只能有一个根组件,我们来定义一个App组件:
1 | const {Component} = owl; |
显示一些任务
1 | class App extends Component { |
这里用到了qweb的技术,不熟的同学可以去查看相关资料。
添加一下样式
1 | .task-list { |
这里将完成的任务设置一下透明度,以显示区别:
1 | <div class="task" t-att-class="task.isCompleted ? 'done' : ''"> |
对应的样式:
1 | .task.done { |
将任务抽象为子组件
任务应该抽离出来成为一个单独的子组件,拥有闭合的行为和外观。
Task组件表现为一个任务,但是它不能拥有任务的状态,状态仍然为App组件所拥有。因此,Task可以通过prop来获取数据。
1 | // ------------------------------------------------------------------------- |
- 当我们定义一个子组件时,我们需要将它添加到components中。
- Task中有一个props属性,它仅用于验证目的。
- 仅当Owl的状态为dev时,验证机制才会生效。生产记得将dev去掉,否则会影响性能。
添加任务1
目前我们还是硬编码的任务,现在,我们将把任务的添加权限交还给用户:
1 | <div class="todo-app"> |
1 | addTask(ev) { |
t-on-keyup绑定了按键事件,当用户按下enter键时,将任务名称输出到控制台中。
然后我们给input添加一个自动的焦点:
1 |
|
添加任务2
目前为止,我们仅仅添加了控制台的输出,并没有将任务真正添加到页面中,接下来,我们将把任务真正地插入到列表中。
1 |
|
运行后你会发现,任务并没有如我们期望的那样显示在界面中,但是打开console你会发现,代码如期地运行了。这里的问题在于owl不知道何时应该重新渲染页面,我们可以使用useState这个钩子来使tasks变得更reactive。
1 | // on top of the file |
改变任务状态
虽然我们可以在界面上添加任务并显示出来了,但是我们又发现了新问题:勾选了任务,并没有改变透明度。原因是没有改变任务的isCompleted属性。
这里有个问题,任务是由Task组件显示出来的,但是它并不是控件状态的所有者,我们需要与它的父控件App交互,我们可以在Task中触发事件,并在App中进行监听。
1 | <input type="checkbox" t-att-checked="props.task.isCompleted" t-on-click="toggleTask"/> |
input控件中添加对单击事件的监听。
1 | toggleTask(ev) { |
将task中的触发事件继续向上传递,在App中
1 | <div class="task-list" t-on-toggle-task='toggleTask'> |
1 | toggleTask(ev) { |
这样就实现了完成的任务透明度变浅的效果。
添加删除按钮
接下来,我们添加一个删除按钮:
1 | <div class="task" t-att-class="props.task.isCompleted ? 'done' : ''"> |
添加按钮删除事件:
1 | deleteTask() { |
在App中监听delete-task事件:
1 | <div class="task-list" t-on-toggle-task="toggleTask" t-on-delete-task="deleteTask"> |
1 | deleteTask(ev) { |
Store
Owl Store是Owl应用状态的管理中心,由React Redux and VueX开发。
使用Store重构的代码:
1 | const { Component, Store } = owl; |
本地化存储任务
目前,我们的任务管理应用可以正常地增删,只是如果一旦刷新浏览器,数据就没了。接下来,我们将任务进行本地化存储:
1 |
|
过滤任务
最后,我们希望给这个这个应用增加一个过滤系统,可以根据任务的状态进行过滤:
1 | // on top of file, readd useState: |
在页面中显示过滤按钮:
1 | <div class="todo-app"> |
1 | .task-panel { |
至此,我们就使用OWL完成了一个简单应用的编写。