OWL教程4 OWL五大组成部分之三模板编译器
参考文档:https://github.com/odoo/owl/blob/master/doc/miscellaneous/compiled_template.md
粗略的讲,Owl有五个主要部分:
- 虚拟DOM系统(src/blockdom)
- 组件系统(src/component)
- 模板编译器(src/compiler 目录)
- 一个小的运行时让各层组合在一起(src/app)
- 响应式系统(src/reactivity.ts)
三 关于Owl编译模板的说明
本页将解释Owl编译的模板是什么样子的。这是一份技术文档,面向有兴趣了解Owl内部工作原理的开发人员。
一般来说,Owl将模板编译成一个javascript函数(闭包),该函数返回一个函数(“render”函数)。闭包的目的是有一个地方存储模板特定的所有值(特别是“块”)。一旦模板被编译,它的闭包函数被调用一次以获得渲染函数,从那时起,只使用渲染函数。
渲染函数接受一些上下文(和一些附加信息),并以块树的形式返回渲染模板的虚拟dom表示。块树是一种非常轻量级的表示,它只包含模板的动态部分及其结构。它实际上独立于模板的静态部分(包含在闭包捕获的块中)。这意味着在渲染时执行的工作只是收集动态数据,并描述结果的块结构。
它看起来像这样,在伪代码中:
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;
}
}
现在,让我们看一个例子。考虑以下模板:
<div class="some-class">
<div class="blabla">
<span><t t-esc="state.value"/></span>
</div>
<t t-if="state.info">
<p class="info" t-att-class="someAttribute">
<t t-esc="state.info"/>
</p>
</t>
<SomeComponent value="value"/>
</div>
如果你仔细观察,有5个动态的东西:
- 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
下面是这个模板的编译代码:
function closure(bdom, helpers) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(
`<div class="some-class"><div class="blabla"><span><block-text-0/></span></div><block-child-0/><block-child-1/></div>`
);
let block2 = createBlock(`<p class="info" block-attribute-0="class"><block-text-1/></p>`);
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]);
};
}
闭包中捕获的值捕获模板的静态部分:我们在这里定义了两个块(其中包含一个模板节点,可以在挂载块时进行深度克隆)。然后,渲染函数仅根据上下文描述结果的块树结构。这意味着我们将在渲染时完成的工作量最小化。
然后,当我们想要patch dom时,Owl将使用来自blockdom的patch函数,该函数将对块树进行diff,并在插入新块时深度克隆新块,跟踪每个块的动态部分,并相应地更新它们。
在这种设计中,渲染模板的成本与动态值的数量成正比,而与模板的大小无关。