【Odoo16前端源码分析】xml模板的加载

1 模板内容的来源

情况A,组件类的template属性,比如ActionContainer.template

javascript 复制代码
/* odoo/addons/web/static/src/webclient/actions/action_container.js */

export class ActionContainer extends Component {
    setup() {
        ..
    }
}
..
ActionContainer.template = xml`
    <t t-name="web.ActionContainer">
      <div class="o_action_manager">
        <t t-if="info.Component" t-component="info.Component" className="'o_action'" t-props="info.componentProps" t-key="info.id"/>
      </div>
    </t>`;

情况B,组件类的同名xml文件,比如webclient.xml

XML 复制代码
<!-- /* odoo/addons/web/static/src/webclient/webclient.xml */ -->

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

    <t t-name="web.WebClient" owl="1">
        <t t-if="!state.fullscreen">
            <NavBar/>
        </t>
        <ActionContainer/>
        <MainComponentsContainer/>
    </t>

</templates>

2 存放模板内容的变量

情况A,存放在owl.js文件中的globalTemplates变量中

javascript 复制代码
/* odoo/addons/web/static/lib/owl/owl.js */

const globalTemplates = {};
function xml(...args) {
    const name = `__template__${xml.nextId++}`;
    const value = String.raw(...args);
    globalTemplates[name] = value;
    return name;
}
xml.nextId = 1;

情况B,通过app.addTemplates方法,将xml保存在TemplateSet的rawTemplates中

javascript 复制代码
/* web.assets_backend.bundle.xml文件是由后台动态打包生成的 */

/*******************************************
                    *  Templates                               *
                    *******************************************/
odoo.define('web.assets_backend.bundle.xml', function(require) {
    'use strict';
    const {loadXML} = require('@web/core/assets');
    const templates = `<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
// xml文件的内容,做了省略处理
..
<t t-name="web.WebClient" owl="1">
        <t t-if="!state.fullscreen">
            <NavBar/>
        </t>
        <ActionContainer/>
        <MainComponentsContainer/>
    </t>
..
</templates>`;
    return loadXML(templates);
});

loadXML方法将templates中的xml文本,按照t-name属性提取出来,以key/value形式保存

javascript 复制代码
/* odoo/addons/web/static/src/core/assets.js */

let defaultApp;
/**
 * Loads the given xml template.
 *
 * @param {string} xml the string defining the templates
 * @param {App} [app=defaultApp] optional owl App instance (default value
 *      can be changed with setLoadXmlDefaultApp method)
 * @returns {Promise<true>} resolved when the template xml has been loaded
 */
export const _loadXML = (assets.loadXML = function loadXML(xml, app = defaultApp) {
    const doc = new DOMParser().parseFromString(xml, "text/xml");
    if (doc.querySelector("parsererror")) {
        throw doc.querySelector("parsererror div").textContent.split(":")[0];
    }

    for (const element of doc.querySelectorAll("templates > [t-name][owl]")) {
        element.removeAttribute("owl");
        const name = element.getAttribute("t-name");
        const previous = templates.querySelector(`[t-name="${name}"]`);
        if (previous) {
            console.debug("Override template: " + name);
            previous.replaceWith(element);
        } else {
            templates.documentElement.appendChild(element);
        }
    }
    if (app || defaultApp) {
        console.debug("Add templates in Owl app.");
        app.addTemplates(templates, app || defaultApp);
    } else {
        console.debug("Add templates on window Owl container.");
    }
});
/**
 * Update the default app to load templates.
 *
 * @param {App} app owl App instance
 */
export function setLoadXmlDefaultApp(app) {
    defaultApp = app;
}

这里的app就是全局开始创建的唯一app实例

javascript 复制代码
/* odoo/addons/web/static/src/start.js */

export async function startWebClient(Webclient) {
    ..
    setLoadXmlDefaultApp(app);
    ..
}

3 将globalTemplates作为原型来创建this.rawTemplates属性,所以以后通过rawTemplates就能访问globalTemplates(前提是rawTemplates中没有要访问的属性时,才会访问globalTemplates,这是JS的原型链原理)

javascript 复制代码
/* odoo/addons/web/static/lib/owl/owl.js */

class TemplateSet {
    constructor(config = {}) {
        this.rawTemplates = Object.create(globalTemplates);
        this.templates = {};
        ..
        if (config.templates) {
            this.addTemplates(config.templates);
        }
    }
    ..
}

因为App继承TemplateSet,并且App没有定义rawTemplates属性,所以app.addTemplates操作的是TemplateSet的rawTemplates

4 后面其他功能通过app.rawTemplates后去需要的模板

相关推荐
鬼手点金4 小时前
nanoGPT/nanochat 能做出 DeepSeek/Qwen 这样的大模型吗?
服务器·前端·javascript·bug·openclaw
蛋蛋的就会顺顺的4 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
程序员Sunday4 小时前
TypeScript 的 any、unknown、never 有什么区别?
前端·面试·typescript
朱涛的自习室5 小时前
70 美金的 Beyond Compare,我让 Gemini 4 和 DeepSeek 造个开源免费版(Day 2)
android·前端·人工智能
码艺-Alimjan5 小时前
Tauri 源码备份 · 打包 & 解压 总结,源码收集之路
前端·javascript·vue.js
小马不起床5 小时前
零基础玩转 FastAPI_11:前端后台管理页面
前端·状态模式·fastapi
子非鱼a7 小时前
【文件上传】一个普普通通的edu16
前端
美狐美颜SDK开放平台7 小时前
直播APP如何实现美白滤镜功能?视频美颜SDK技术开发方案详解
前端·数据库·人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk
pride.li8 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
屏什么录8 小时前
电脑如何录屏幕视频和声音?从工具到代码,7 种方案帮你彻底搞定
前端·音视频开发