mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
4415cc8932
Owl 2 will not have a router or store implemented inside the library
257 lines
9.8 KiB
Markdown
257 lines
9.8 KiB
Markdown
# Comparison with Vue/React
|
|
|
|
OWL, React and Vue have the same main feature: they allow developers to build
|
|
declarative user interfaces. To do that, all these frameworks uses a virtual dom. However, there are still obviously many differences.
|
|
|
|
In this page, we try to highlight some of these differences. Obviously, a lot of
|
|
effort was put to be fair. However, if you disagree with some of the points
|
|
discussed, feel free to open an issue/submit a PR to correct this text.
|
|
|
|
## Content
|
|
|
|
- [Size](#size)
|
|
- [Class Based](#class-based)
|
|
- [Tooling/Build Step](#toolingbuild-step)
|
|
- [Templating](#templating)
|
|
- [Asynchronous rendering](#asynchronous-rendering)
|
|
- [Reactiveness](#reactiveness)
|
|
- [State Management](#state-management)
|
|
- [Hooks](#hooks)
|
|
|
|
## Size
|
|
|
|
OWL is intended to be small and to work at a slightly lower level of abstraction
|
|
than React and Vue. Also, jQuery is not the same kind of framework, but it is interesting to compare.
|
|
|
|
| Framework | Size (minified, gzipped) |
|
|
| ------------------------ | ------------------------ |
|
|
| OWL | 18kb |
|
|
| Vue + VueX | 30kb |
|
|
| Vue + VueX + Vue Router | 39kb |
|
|
| React + ReactDOM + Redux | 40kb |
|
|
| jQuery | 30kb |
|
|
|
|
Note that those comparisons are not entirely fair, because we do not compare
|
|
the same exact set of features. For example, VueX and Vue Router support more
|
|
advanced use cases.
|
|
|
|
## Class Based
|
|
|
|
Both React and Vue moved away from defining components with classes. They prefer
|
|
a more functional approach, in particular, with the new `hooks` mechanisms.
|
|
|
|
This has some advantages and disadvantages. But the end result is that React
|
|
and Vue both offers multiple different ways of defining new components. In
|
|
contrast, Owl has only one mechanism: class-based components. We believe that Owl
|
|
components are fast enough for all our usecases, and making it as simple as
|
|
possible for developers is more valuable (for us).
|
|
|
|
Also, functions or class based components are more than just syntax. Functions
|
|
come with a mindset of composition and class are about inheritance. Clearly,
|
|
both of these are important mechanisms for reusing code. Also, one does not
|
|
exclude the other.
|
|
|
|
It certainly looks like the world of UI frameworks is moving toward composition,
|
|
for many very good reasons. Owl is still good at composition (for example,
|
|
Owl supports slots, which is the primary mechanism to make generic reusable
|
|
components). But it can also use inheritance (and this is very important since
|
|
templates can also be inherited with `xpaths` transformations).
|
|
|
|
## Tooling/Build step
|
|
|
|
OWL is designed to be easy to use in a standalone way. For various reasons,
|
|
Odoo does not want to rely on standard web tools (such as webpack), and OWL can
|
|
be used by simply adding a script tag to a page.
|
|
|
|
```html
|
|
<script src="owl.min.js" />
|
|
```
|
|
|
|
In comparison, React encourages using JSX, which necessitate a build step, and
|
|
most Vue applications uses single file components, which also necessitate a build step.
|
|
|
|
On the flipside, external tooling may make it harder to use in some case, but it
|
|
also brings a lot of benefits. And React/Vue have both a large ecosystem.
|
|
|
|
Note that since Owl is not dependant on any external tool nor libraries, it is
|
|
very easy to integrate into any build toolchain. Also, since we cannot rely on
|
|
additional tools, we made a lot of effort to make the most of the web platform.
|
|
|
|
For example, Owl uses the standard `xml` parser that comes with every browser.
|
|
Because of that, Owl did not have to write its own template parser. Another
|
|
example is the [`xml`](../reference/tags.md#xml-tag) tag helper function, which makes use of
|
|
native template literals to allow in a natural way to write `xml` templates
|
|
directly in the javascript code. This can be easily integrated with editor
|
|
plugins to have autocompletion inside the template.
|
|
|
|
## Templating
|
|
|
|
OWL uses its own QWeb engine, which compiles templates on the
|
|
frontend, as they are needed. This is extremely convenient for our use case, in
|
|
particular because templates are described in XML files, and can be modified by
|
|
XPaths. Since Odoo is at its heart a modular application, this is an important
|
|
feature for us.
|
|
|
|
```xml
|
|
<div>
|
|
<button t-on-click="increment">Click Me! [<t t-esc="state.value"/>]</button>
|
|
</div>
|
|
```
|
|
|
|
Vue is actually kind of similar. Its template language is kind of close to QWeb,
|
|
with the `v` replaced by the `t`. However, it is also more fully featured. For
|
|
example, Vue templates have slots, or event modifiers. A large difference is that
|
|
most Vue applications will need to be built ahead of time, to compile the templates
|
|
into javascript functions. Note that Vue has a separate build which includes the
|
|
template compiler.
|
|
|
|
In contrast, most React applications do not use a templating language, but write
|
|
some JSX code, which is precompiled into plain JavaScript by a build step. This
|
|
example is done with the (kind of outdated) React class system:
|
|
|
|
```jsx
|
|
class Clock extends React.Component {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello, world!</h1>
|
|
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
This has the advantage of having the full power of Javascript, but is less
|
|
structured than a template language. Note that the tooling is quite impressive:
|
|
there is a syntax highlighter for jsx here on github!
|
|
|
|
By comparison, here is the equivalent Owl component, written with the
|
|
[`xml`](../reference/tags.md#xml-tag) tag helper:
|
|
|
|
```js
|
|
class Clock extends Component {
|
|
static template = xml`
|
|
<div>
|
|
<h1>Hello, world!</h1>
|
|
<h2>It is {props.date.toLocaleTimeString()}.</h2>
|
|
</div>
|
|
`;
|
|
}
|
|
```
|
|
|
|
## Asynchronous Rendering
|
|
|
|
This is actually a big difference between OWL and React/Vue: components in OWL
|
|
are totally asynchronous. They have two asynchronous hooks in their lifecycle:
|
|
|
|
- `willStart` (before the component starts rendering)
|
|
- `willUpdateProps` (before new props are set)
|
|
|
|
Both these methods can be implemented and return a promise. The rendering will
|
|
then wait for these promises to be completed before patching the DOM. This is
|
|
useful for some use cases: for example, a component may want to fetch an external
|
|
library (a calendar component may need a specialized calendar rendering library),
|
|
in its willStart hook.
|
|
|
|
```javascript
|
|
class MyCalendarComponent extends owl.Component {
|
|
...
|
|
|
|
willStart() {
|
|
return utils.lazyLoad('static/libs/fullcalendar/fullcalendar.js');
|
|
}
|
|
...
|
|
}
|
|
```
|
|
|
|
This may be dangerous (to stop the rendering waiting for the network), but it is
|
|
extremely powerful as well, as demonstrated by the Odoo Web Client.
|
|
|
|
Lazy loading static libraries can obviously be done with React/Vue, but it is
|
|
more convoluted. For example, in Vue, you need to use a dynamic import keyword
|
|
that needs to be transpiled at build time in order for the component to be loaded
|
|
asynchronously (see [the documentation](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components)).
|
|
|
|
## Reactiveness
|
|
|
|
React has a simple model: whenever the state changes, it is
|
|
replaced with a new state (via the `setState` method). Then, the DOM is patched.
|
|
This is simple, efficient, and a little bit awkward to write.
|
|
|
|
Vue is a little bit different: it replace magically the properties in the state
|
|
by getters/setters. With that, it can notify components whenever the state that
|
|
they read was changed.
|
|
|
|
Owl is closer to vue: it also tracks magically the state properties, but it does
|
|
only increment an internal counter whenever it changes. Note that it is done
|
|
with a `Proxy`, which means that it is totally transparent to the developers.
|
|
Adding new keys is supported. Once any part of the state has been changed, a
|
|
rendering is scheduled in the next microtask tick (promise queue).
|
|
|
|
## Hooks
|
|
|
|
[Hooks](https://reactjs.org/docs/hooks-intro.html#motivation) recently took over
|
|
the React world. They solve a lot of seemingly unconnected problems: attach
|
|
reusable behavior to a component, in a composable way, extract stateful logic
|
|
from a component or reuse stateful logic between component, without changing your
|
|
component hierarchy.
|
|
|
|
Here is an example of the React `useState` hook:
|
|
|
|
```js
|
|
import React, { useState } from "react";
|
|
|
|
function Example() {
|
|
// Declare a new state variable, which we'll call "count"
|
|
const [count, setCount] = useState(0);
|
|
|
|
return (
|
|
<div>
|
|
<p>You clicked {count} times</p>
|
|
<button onClick={() => setCount(count + 1)}>Click me</button>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
Because of the way React designed the hooks API, they only work for functional
|
|
components. But in that case, they really are powerful. Every major React library
|
|
is in the process of redesigning their API with hooks (for example,
|
|
[Redux](https://react-redux.js.org/next/api/hooks)).
|
|
|
|
Vue 2 does not have hooks, but the Vue project is working on its next version,
|
|
which will feature its new [composition API](https://vue-composition-api-rfc.netlify.com/).
|
|
This work is based on the new ideas introduced by React hooks.
|
|
|
|
From the way React and Vue introduce their hooks, it may look like hooks are not
|
|
compatible with class components. However, this is not the case, as shown by
|
|
Owl [hooks](../reference/hooks.md). They are inspired by both React and Vue. For example,
|
|
the `useState` hook is named after React, but its API is closer to the `reactive`
|
|
Vue hook.
|
|
|
|
Here is what the `Counter` example above look like in Owl:
|
|
|
|
```js
|
|
import { Component, Owl } from "owl";
|
|
import { xml } from "owl/tags";
|
|
|
|
class Example extends Component {
|
|
static template = xml`
|
|
<div>
|
|
<p>You clicked {count.value} times</p>
|
|
<button t-on-click="increment">Click me</button>
|
|
</div>`;
|
|
|
|
count = useState({ value: 0 });
|
|
|
|
increment() {
|
|
this.state.value++;
|
|
}
|
|
}
|
|
```
|
|
|
|
Since the Owl framework had hooks from early in its life, its main APIs
|
|
are designed to be interacted with hooks from the start. For example, the
|
|
`Context` abstraction.
|