Use requirejs to load a file with several classes

2019-05-27 05:43发布

问题:

I have a little problem with grunt concat and requirejs (and typescript). The problem is simple, when I generated one file per class and I load each file seperately, I don't have problem. (But it's slower for the browser) Example:

  • A.js
  • Message.js
  • ValidatorMessage.js (extends Message and has a dependency)

loader.ts:

require.config({
    paths: {
        'A': '../generated/shared/A.min',
        'Message': '../generated/shared/Message.min',
        'ValidatorMessage': '../generated/shared/ValidatorMessage.min', }//more

That's works. I can require() each file in the browser and I get it. But if do:

require.config({
        paths: {
            'shared': '../generated/shared.min',

That doesn't works.

There is the content of shared.min.js

/*! MotorEngine-web - v0.0.1 - 2013-11-30 04:11:18 - development */
///<reference path='./def/lib.d.ts'/>
///<reference path='./def/node.d.ts'/>
define([ "require", "exports" ], function(require, exports) {
    /**
    * @name ValidatorMessage
    * @description  Message object for Validation errors.
    * @author Vadorequest
    */
    var A = function() {
        function A() {}
        /**
        * Get the message.
        * @returns {string}
        */
        return A.prototype.getErrors = function() {
            return this._errors;
        }, A;
    }();
    exports.A = A;
}), ///<reference path='./def/lib.d.ts'/>
///<reference path='./def/node.d.ts'/>
define([ "require", "exports" ], function(require, exports) {
    /**
    * @name Message
    * @description Message object for both server and client side communication.
    * @author Vadorequest
    */
    var Message = function() {
        /**
        * Constructor.
        * @param message   Message to display.
        * @param data      Data useful for callback, can be anything.
        * @param status    Status of the message.
        */
        function Message(message, data, status) {
            "undefined" == typeof data && (data = !1), "undefined" == typeof status && (status = !1), 
            /**
            * Constants.
            */
            this.FIELD_NAME_MESSAGE = "m", this.FIELD_NAME_DATA = "d", this.FIELD_NAME_STATUS = "s", 
            this.EXCEPTION_BAD_JSON_CONTENT = 'Unable to parse JSON. Bad object/string attributes. (Missing message ("' + this.FIELD_NAME_MESSAGE + '" field) or status ("' + this.FIELD_NAME_MESSAGE + '" field)?', 
            this.EXCEPTION_BAD_JSON_TYPE = "Incorrect data type. Object or string expected.", 
            this._message = message, this._data = data, this._status = status;
        }
        /**
        * Get the message.
        * @returns {string}
        */
        return Message.prototype.getMessage = function() {
            return this._message;
        }, /**
        * Get message data.
        * @returns {*}
        */
        Message.prototype.getData = function() {
            return this._data;
        }, /**
        * Get message status.
        * @returns {boolean}
        */
        Message.prototype.getStatus = function() {
            return this._status;
        }, /**
        * Returns the current object as JSON string.
        * @returns {string}
        */
        Message.prototype.toJSON = function() {
            return JSON.stringify(this._toSimpleObject());
        }, /**
        * Returns the current object without methods.
        * @returns {Object}
        */
        Message.prototype.toObject = function() {
            return this._toSimpleObject();
        }, /**
        * Returns a custom object without method using the predefined FIELD_NAME to take less space once converted in JSON string.
        * @returns {{}}
        * @private
        */
        Message.prototype._toSimpleObject = function() {
            var json = {};
            return json[this.FIELD_NAME_MESSAGE] = this._message, this._data !== !1 && (json[this.FIELD_NAME_DATA] = this._data), 
            json[this.FIELD_NAME_STATUS] = this._status, json;
        }, /**
        * Convert JSON to a Message object instance.
        * @param json  Json to convert to object.
        * @returns {Message.Message}
        */
        Message.prototype.fromJSON = function(json) {
            if ("object" == typeof json) return this._fromJSONObject(json);
            if ("string" == typeof json) return this._fromJSONString(json);
            throw "Message.fromJSON " + this.EXCEPTION_BAD_JSON_TYPE;
        }, /**
        * Convert JSON object to a Message object instance.
        * @param json  Json to convert to object.
        * @returns {Message.Message}
        * @private
        */
        Message.prototype._fromJSONObject = function(json) {
            if (json[this.FIELD_NAME_MESSAGE] && json[this.FIELD_NAME_STATUS]) return json[this.FIELD_NAME_DATA] ? new Message(json[this.FIELD_NAME_MESSAGE], json[this.FIELD_NAME_DATA], json[this.FIELD_NAME_STATUS]) : new Message(json[this.FIELD_NAME_MESSAGE], !1, json[this.FIELD_NAME_STATUS]);
            throw "Message._fromJSONObject " + this.EXCEPTION_BAD_JSON_CONTENT;
        }, /**
        * Convert JSON string to a Message object instance.
        * @param json  Json to convert to object.
        * @returns {Message.Message}
        * @private
        */
        Message.prototype._fromJSONString = function(json) {
            try {
                return this._fromJSONObject(JSON.parse(json));
            } catch (e) {
                throw "Message._fromJSONString: JSON.parse error:" + e.message;
            }
        }, Message;
    }();
    exports.Message = Message;
});

///<reference path='./def/lib.d.ts'/>
///<reference path='./def/node.d.ts'/>
var __extends = this.__extends || function(d, b) {
    function __() {
        this.constructor = d;
    }
    for (var p in b) b.hasOwnProperty(p) && (d[p] = b[p]);
    __.prototype = b.prototype, d.prototype = new __();
};

define([ "require", "exports", "Message" ], function(require, exports, __message__) {
    var message = __message__, ValidatorMessage = function(_super) {
        function ValidatorMessage(message) {
            _super.call(this, message);
        }
        return __extends(ValidatorMessage, _super), /**
        * Get the message.
        * @returns {string}
        */
        ValidatorMessage.prototype.getErrors = function() {
            return this._errors;
        }, ValidatorMessage;
    }(message.Message);
    exports.ValidatorMessage = ValidatorMessage;
});

I don't understand if it's because there is several call to define in the same file or something else. Do you?

Edit: I forgot to say that in this example the class A will be available browser side (and work) but not the following. If I remove A to the shared.min.js then the class Message will work but not the next, so I thing that only the first class loaded is well loaded!

Edit2: I also forgot to say that in the case where A doesn't exists, the var Message in the browser exists and works (as I said at Edit1) but the var ValidatorMessage exists too! Except it's undefined, I mean I get the autocompletion from the browser but there is nothing inside. Looks like only the first define() put the object as global.

回答1:

You should be using the r.js optimizer in production, not manually concatenating the files together; otherwise you are losing the benefit of modular code (one class per file). Let the optimizer do the hard work of figuring out the dependency graph, don't try to reinvent the wheel!

There is a grunt task available for this.