iT邦幫忙

2023 iThome 鐵人賽

DAY 6
1
Odoo

Odoo 14 Javascript 開發心路歷程系列 第 6

Day 6 require 物件介紹 - 底層基本面講述

  • 分享至 

  • xImage
  •  

在昨天的 JavaScript 程式中,有個東西需要介紹給各位,

理解 require 物件,對於有些模組的程式只有執行,但沒有賦值的情況會有更多的了解

首先,在昨天的 widget.js 中,執行 require 物件後取得 FieldText 物件

odoo.define('ironman_js.widget', function (require) {
    'use strict';

    const { FieldText } = require('web.basic_fields');

    FieldText.include({
        start: function () {
            console.log('Hello world');
        },
    })
});

簡單解釋 require 的功用就是 “等待目標 JS 執行完成”

因此也能理解成確保目前的程式,至少會在 require 目標執行後,自己才會執行

還是很亂嗎?用 python 的角度來解釋,可以想像成下面程式

from module_a import Text

但跟 python import 不一樣的是,若該模組的 JS 沒有 return,那取得就會是 undefined

因此在 require 時,要查詢目標是否有回傳,沒有的話就有很大的機率會發生錯誤

主要論述講完了,接著帶各位看一下 odoo.define 的程式

// addons/web/static/src/js/boot.js
odoo.define = function () {
    var args = Array.prototype.slice.call(arguments);
    var name = typeof args[0] === 'string' ? args.shift() : ('__odoo_job' + (jobUID++));
    var factory = args[args.length - 1];
    var deps;
    if (args[0] instanceof Array) {
        deps = args[0];
    } else {
        deps = [];
        factory.toString()
            .replace(commentRegExp, '')
            .replace(cjsRequireRegExp, function (match, dep) {
                deps.push(dep);
            });
    }

    if (odoo.debug) {
        if (!(deps instanceof Array)) {
            throw new Error('Dependencies should be defined by an array', deps);
        }
        if (typeof factory !== 'function') {
            throw new Error('Factory should be defined by a function', factory);
        }
        if (typeof name !== 'string') {
            throw new Error("Invalid name definition (should be a string", name);
        }
        if (name in factories) {
            throw new Error("Service " + name + " already defined");
        }
    }

    factory.deps = deps;
    factories[name] = factory;

    jobs.push({
        name: name,
        factory: factory,
        deps: deps,
    });

    deps.forEach(function (dep) {
        jobDeps.push({from: dep, to: name});
    });

    this.processJobs(jobs, services);
};

重點在 factory 的處理,factory 就是一開始 widget.js 傳入的 function

而過了處理後,會執行工作 (processJobs),所以再看到對應的程式

// addons/web/static/src/js/boot.js
odoo.processJobs = function (jobs, services) {
        var job;

        function processJob(job) {
            var require = makeRequire(job);

            var jobExec;
            var def = new Promise(function (resolve) {
                try {
                    jobExec = job.factory.call(null, require);
                    jobs.splice(jobs.indexOf(job), 1);
                } catch (e) {
                    job.error = e;
                    console.error('Error while loading ' + job.name + ': '+ e.stack);
                }
                if (!job.error) {
                    Promise.resolve(jobExec).then(
                        function (data) {
                            services[job.name] = data;
                            resolve();
                            odoo.processJobs(jobs, services);
                        }).guardedCatch(function (e) {
                            job.rejected = e || true;
                            jobs.push(job);
                            resolve();
                        }
                    );
                }
            });
            jobPromises.push(def);
        }

        function isReady(job) {
            return !job.error && !job.rejected && job.factory.deps.every(function (name) {
                return name in services;
            });
        }

        function makeRequire(job) {
            var deps = {};
            Object.keys(services).filter(function (item) {
                return job.deps.indexOf(item) >= 0;
            }).forEach(function (key) {
                deps[key] = services[key];
            });

            return function require(name) {
                if (!(name in deps)) {
                    console.error('Undefined dependency: ', name);
                }
                return deps[name];
            };
        }

        while (jobs.length) {
            job = undefined;
            for (var i = 0; i < jobs.length; i++) {
                if (isReady(jobs[i])) {
                    job = jobs[i];
                    break;
                }
            }
            if (!job) {
                break;
            }
            processJob(job);
        }

        return services;
    };

會發現迴圈處理所有工作,而在內部函式 processJob,會執行 factory

jobExec = job.factory.call(null, require);

這樣就能理解為什麼 odoo.define 的傳入函式第一個參數一定是 require 物件了

當然要定義其他名稱也是可以,不過依照整個 odoo 開源社群的理解,

大家都共同定義都是 require,後續維護者也知道說這是做什麼的,就不會混淆


上一篇
Day 5 實作 1: Say Hi to field widget!
下一篇
Day 7 include() 介紹
系列文
Odoo 14 Javascript 開發心路歷程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言