From bb373e6a7adbfa7d5a951e06c5e4200188a2efa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 1 Mar 2022 15:36:56 +0100 Subject: [PATCH] [DOC] add explanation on structure of compiled template --- README.md | 1 + doc/miscellaneous/compiled_template.md | 98 ++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 doc/miscellaneous/compiled_template.md diff --git a/README.md b/README.md index aecdc6d3..118ffc5a 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ Are you new to Owl? This is the place to start! - [Comparison with React/Vue](doc/miscellaneous/comparison.md) - [Why did Odoo build Owl?](doc/miscellaneous/why_owl.md) - [Changelog (from owl 1.x to 2.x)](CHANGELOG.md) +- [Notes on compiled templates](doc/miscellaneous/compiled_template.md) ## Installing Owl diff --git a/doc/miscellaneous/compiled_template.md b/doc/miscellaneous/compiled_template.md new file mode 100644 index 00000000..f6b246ca --- /dev/null +++ b/doc/miscellaneous/compiled_template.md @@ -0,0 +1,98 @@ +# 🦉 Notes On Owl Compiled Templates 🦉 + +This page will explain what an Owl compiled template look like. This is a +technical document intended for developers interested in understanding how Owl +works internally. + +Broadly speaking, Owl compiles templates into a javascript function (a closure) +that returns a function (the "render" function). The point of the closure is to +have a place to store all values specific to the template (in particular, "blocks"). +Once a template is compiled, its closure function is called once to get the +render function, and from then on, only the render function is used. + +The render function takes some context (and some additional information) and +return a virtual dom representation of the rendered template, as a block tree. +A block tree is a very light weight representation that only contains the dynamic +part of the template, and its structure. It is actually independant of the +static part of the templates (which are contained in the blocks captured by the +closure). This means that the work performed at render time is only to collect +dynamic data, and to describe the block structure of the result. + +It looks like this, in pseudo code: + +```js +function closure(bdom, helpers) { + // here is some place to put stuff specific to the template, such as + // blocks + ... + + return function render(context, node, key) { + // only build here all dynamic parts of the template + // build a block tree + return tree; + } +} +``` + +Now, let us see an example. Consider the following template: + +```xml +
+
+ +
+ +

+ +

+
+ +
+``` + +If you look carefully, there are 5 dynamic things: + +- a text value (the first `t-esc`), +- a sub block (the `t-if`), +- a dynamic attribute (the `t-att-class` attribute), +- another text value (the second `t-esc`), +- and finally, a sub component + +Here is the compiled code for this template: + +```js +function closure(bdom, helpers) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + let block1 = createBlock( + `
` + ); + let block2 = createBlock(`

`); + + return function render(ctx, node, key = "") { + let b2, b3; + let txt1 = ctx["state"].value; + if (ctx["state"].info) { + let attr1 = ctx["someAttribute"]; + let txt2 = ctx["state"].info; + b2 = block2([attr1, txt2]); + } + b3 = component(`SomeComponent`, { value: ctx["value"] }, key + `__1`, node, ctx); + return block1([txt1], [b2, b3]); + }; +} +``` + +The values captured in the closure capture the static part of the template: we +define here two blocks (which contains a template node, that can be deep cloned +whenever a block is mounted). Then the render function only describes the block +tree structure of the result, depending on the context. This means that we +minimize the amount of work done at render time. + +Then, when we want to patch the dom, Owl will uses the `patch` function from +blockdom, which then will diff the block tree, and deep clone new blocks whenever +a new block is inserted, keep track of dynamic parts of each block, and update +them accordingly. + +With this design, the cost of rendering a template is proportional to the number +of dynamic values, and not to the size of the template.