odoo16前端框架源码阅读——env.js

env.js(env的初始化以及服务的加载)

路径:addons\web\static\src\env.js

这个文件的作用就是初始化env,主要是加载所有的服务。如orm, title, dialog等。

1、env.js 的加载时机

前文我们讲过前端的启动函数,start.js,其中有这么两句,这里有两个函数makeEnv和startServices,都在同级目录的env.js里

    const env = makeEnv();
    await startServices(env);

2、 makeEnv()

export function makeEnv() {
    return {
        bus: new EventBus(),
        services: {},
        debug: odoo.debug,
        get isSmall() {
            throw new Error("UI service not initialized!");
        },
    };
}

从代码可以看出,env有4个属性:

  1. bus: 全局数据总线
  2. services:全局服务对象
  3. debug: 是否debug模式
  4. isSmall:判断手机端还是移动端

这里抛出一个异常是为了在后面会覆盖这个方法。

从中可以看出,env最重要的其实就两个对象,一个是全局数据总线,负责组件之间通信,另一个就是service。

3、startServices

启动所有在注册表中注册的服务,并且保证所有的依赖都得到满足。

首先获取所有在注册表中注册的服务serviceRegistry

初始化元数据SERVICES_METADATA, 主要是保存service中sync的内容,有什么作用还不清楚

主角开始上场: startServices

启动所有在注册表中注册的服务,并且保证每个服务的依赖都得到满足。

    await Promise.resolve();     // 等待之前的异步操作都完成
    const toStart = new Set();   // 初始化要启动的服务,因为每个服务都不同,所以用了set
 	serviceRegistry.addEventListener   // 这段没看懂,这里添加了一个事件监听,动态增加注册服务的
    await _startServices(env, toStart);  // 将env和toStart对象传入,干正事了
    

4._startServices(env, toStart)

4.1 填充 toStart 集合

    const services = env.services;   //获取了env中的service对象,这时候还是空的,啥也没有
	// 根据注册表中的服务信息,初始化了toStart集合,里面都是创建好的实例对象,并且有一个name属性。
    for (const [name, service] of serviceRegistry.getEntries()) {
        if (!(name in services)) {
            const namedService = Object.assign(Object.create(service), { name });
            toStart.add(namedService);
        }
    }

4.2、findNext()

关键的地方来了, 这里有一个关键的函数findNext(),先看看它的定义:

// 从toStart集合中遍历,有过集合中的服务有依赖并且依赖都已经满足了,则返回这个service,如果没有依赖,那么直接返回,找到第一个就返回,不叨叨,直到找不到,返回null   
function  {
        for (const s of toStart) {
            if (s.dependencies) {
                if (s.dependencies.every((d) => d in services)) {
                    return s;
                }
            } else {
                return s;
            }
        }
        return null;
    }

4.3 、启动服务,并填充到env的services中

在start函数中,findNext函数返回的服务,第一步要最的就是从toStart 删除,这样遍历的次数会越来越少,我看过,odoo17一共有69个服务,while一共还要遍历69次,每次加载一个服务。 通过这种方式,很好的处理了服务之间的依赖关系,并且最大限度的实现了并行。

    // start as many services in parallel as possible 并行启动尽可能多的服务
    async function start() {
        let service = null;
        const proms = [];
        while ((service = findNext())) {
            const name = service.name;
            toStart.delete(service);     // 删除要加载的服务
            const entries = (service.dependencies || []).map((dep) => [dep, services[dep]]);
            const dependencies = Object.fromEntries(entries);
            let value;
            try {
                value = service.start(env, dependencies);     // 调用start函数,并将返回值付给value
            } catch (e) {
                value = e;
                console.error(e);
            }
            if ("async" in service) {
                SERVICES_METADATA[name] = service.async;     // 保存服务的元数据,后面可能会有用
            }
            if (value instanceof Promise) {                  // 如果value是一个Promise
                proms.push(
                    new Promise((resolve) => {
                        value
                            .then((val) => {
                                services[name] = val || null;    // 将promise的返回值保存到services中
                            })
                            .catch((error) => {
                                services[name] = error;
                                console.error("Can't load service '" + name + "' because:", error);
                            })
                            .finally(resolve);
                    })
                );
            } else {
                services[service.name] = value || null;  // 如果不是promise,直接将value保存到services中
            }
        }
        await Promise.all(proms);    // 等待所有的proms完成
        if (proms.length) {
            return start();           
        }
    }

到这里,前端js就完成了所有service的加载,要注意的是, env.services中保存的不是 services对象本身,而是service对象的start函数返回的对象。

这点很重要,每个service都要有start函数,而且要有返回值。

4.4、后面还有一段是异常处理

    if (toStart.size) {
        const names = [...toStart].map((s) => s.name);
        const missingDeps = new Set();
        [...toStart].forEach((s) =>
            s.dependencies.forEach((dep) => {
                if (!(dep in services) && !names.includes(dep)) {
                    missingDeps.add(dep);
                }
            })
        );
        const depNames = [...missingDeps].join(", ");
        throw new Error(
            `Some services could not be started: ${names}. Missing dependencies: ${depNames}`
        );
    }

如果toStart.size >0 ,说明这里面的服务的依赖想没有得到满足,所以无法加载,会抛出一个Error

5、附录:odoo17 env.js

/** @odoo-module **/

import { registry } from "./core/registry";

import { EventBus } from "@odoo/owl";

// -----------------------------------------------------------------------------
// Types
// -----------------------------------------------------------------------------

/**
 * @typedef {Object} OdooEnv
 * @property {import("services").Services} services
 * @property {EventBus} bus
 * @property {string} debug
 * @property {(str: string) => string} _t
 * @property {boolean} [isSmall]
 */

// -----------------------------------------------------------------------------
// makeEnv
// -----------------------------------------------------------------------------

/**
 * Return a value Odoo Env object
 *
 * @returns {OdooEnv}
 */
export function makeEnv() {
    return {
        bus: new EventBus(),
        services: {},
        debug: odoo.debug,
        get isSmall() {
            throw new Error("UI service not initialized!");
        },
    };
}

// -----------------------------------------------------------------------------
// Service Launcher
// -----------------------------------------------------------------------------

const serviceRegistry = registry.category("services");

export const SERVICES_METADATA = {};
let startServicesPromise = null;

/**
 * Start all services registered in the service registry, while making sure
 * each service dependencies are properly fulfilled.
 *
 * @param {OdooEnv} env
 * @returns {Promise<void>}
 */
export async function startServices(env) {
    // Wait for all synchronous code so that if new services that depend on
    // one another are added to the registry, they're all present before we
    // start them regardless of the order they're added to the registry.
    debugger
    await Promise.resolve();

    const toStart = new Set();
    serviceRegistry.addEventListener("UPDATE", async (ev) => {
        // Wait for all synchronous code so that if new services that depend on
        // one another are added to the registry, they're all present before we
        // start them regardless of the order they're added to the registry.
        await Promise.resolve();
        const { operation, key: name, value: service } = ev.detail;
        if (operation === "delete") {
            // We hardly see why it would be usefull to remove a service.
            // Furthermore we could encounter problems with dependencies.
            // Keep it simple!
            return;
        }
        if (toStart.size) {
            const namedService = Object.assign(Object.create(service), { name });
            toStart.add(namedService);
        } else {
            await _startServices(env, toStart);
        }
    });
    await _startServices(env, toStart);
}

async function _startServices(env, toStart) {
    if (startServicesPromise) {
        return startServicesPromise.then(() => _startServices(env, toStart));
    }
    const services = env.services;
    for (const [name, service] of serviceRegistry.getEntries()) {
        if (!(name in services)) {
            const namedService = Object.assign(Object.create(service), { name });
            toStart.add(namedService);
        }
    }

    // start as many services in parallel as possible
    async function start() {
        let service = null;
        const proms = [];
        while ((service = findNext())) {
            const name = service.name;
            toStart.delete(service);
            const entries = (service.dependencies || []).map((dep) => [dep, services[dep]]);
            const dependencies = Object.fromEntries(entries);
            let value;
            try {
                value = service.start(env, dependencies);
            } catch (e) {
                value = e;
                console.error(e);
            }
            if ("async" in service) {
                SERVICES_METADATA[name] = service.async;
            }
            if (value instanceof Promise) {
                proms.push(
                    new Promise((resolve) => {
                        value
                            .then((val) => {
                                services[name] = val || null;
                            })
                            .catch((error) => {
                                services[name] = error;
                                console.error("Can't load service '" + name + "' because:", error);
                            })
                            .finally(resolve);
                    })
                );
            } else {
                services[service.name] = value || null;
            }
        }
        await Promise.all(proms);
        if (proms.length) {
            return start();
        }
    }
    startServicesPromise = start();
    await startServicesPromise;
    startServicesPromise = null;
    if (toStart.size) {
        const names = [...toStart].map((s) => s.name);
        const missingDeps = new Set();
        [...toStart].forEach((s) =>
            s.dependencies.forEach((dep) => {
                if (!(dep in services) && !names.includes(dep)) {
                    missingDeps.add(dep);
                }
            })
        );
        const depNames = [...missingDeps].join(", ");
        throw new Error(
            `Some services could not be started: ${names}. Missing dependencies: ${depNames}`
        );
    }

    function findNext() {
        for (const s of toStart) {
            if (s.dependencies) {
                if (s.dependencies.every((d) => d in services)) {
                    return s;
                }
            } else {
                return s;
            }
        }
        return null;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Odoo中,前端数据的保存通常是通过向后端发送HTTP请求来完成的。具体来说,可以使用Odoo的Web框架来实现前端数据的保存。 在前端页面中,可以使用JavaScript代码来获取表单输入的数据,并将其封装成一个JSON对象。然后,可以使用jQuery等JavaScript库来发送POST请求,将数据发送到后端。具体实现方式如下: 1. 在前端页面中,编写JavaScript代码来将表单数据封装成JSON对象。例如: ``` var data = { 'name': $('#name-input').val(), 'email': $('#email-input').val(), 'phone': $('#phone-input').val() }; ``` 2. 使用jQuery发送POST请求,将数据发送到后端。例如: ``` $.ajax({ url: '/my-controller/save-data', method: 'POST', data: JSON.stringify(data), contentType: 'application/json', success: function(response) { alert('Data saved successfully!'); }, error: function(xhr, status, error) { alert('Error saving data: ' + error); } }); ``` 3. 在后端编写控制器来处理POST请求,并将数据保存到数据库中。例如: ``` class MyController(http.Controller): @http.route('/my-controller/save-data', type='http', auth='user') def save_data(self, **kwargs): data = json.loads(request.httprequest.data) name = data.get('name') email = data.get('email') phone = data.get('phone') # Save data to database my_model = request.env['my.model'].create({ 'name': name, 'email': email, 'phone': phone }) return json.dumps({'success': True}) ``` 通过以上步骤,前端数据就可以成功保存到Odoo后端了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值