为什么在指定YUI模块时需要YUI.add,如果需要,非YUI模块如何工作?

时间:2013-08-07 17:25:09

标签: javascript yui yui3

我们使用YUI3加载程序来管理加载我们的javascript和css文件。作为每页上bootstrap js代码的一部分,我们有如下内容:

YUI({
  ...
  groups: {
     ... 
     myGroup: {
         modules: {
             "my-module": {
                 ...
                 path: "MyModule.js",
                 requires: [ "yui-base" ]
             },
         }
         ...
     }
  }
}).use("my-module", function (Y) {
    Y.MyModule.doStuff();
});

MyModule.js具有以下内容:

YUI.add('my-module', function (Y) {
    Y.MyModule = function () {
        ...
        _validator: Y.Lang.isString
    };
}, '3.4.0', {
    requires: [ "yui-base" ]
});

YUI还声称here加载程序可以与非YUI3“模块一起使用,因为它们在配置中指定了它们的依赖项。他们为yui2组提供以下示例模块配置:

       yui2: {
           combine: true,
           base: 'http://yui.yahooapis.com/2.8.0r4/build/',
           comboBase: 'http://yui.yahooapis.com/combo?',
           root: '2.8.0r4/build/',
           modules:  { // one or more external modules that can be loaded along side of YUI
               yui2_yde: {
                   path: "yahoo-dom-event/yahoo-dom-event.js"
               },
               yui2_anim: {
                   path: "animation/animation.js",
                   requires: ['yui2_yde']
               }
           }
       }

这表明YUI足够智能,只有在yahoo-dom-event.js加载并运行后才能加载和运行YUI2的animation.js。

我不明白的是,如果这适用于非YUI模块,为什么我必须用YUI.add和冗余需求列表包装我自己的模块(因为需求也在配置中指定)?< / p>

我尝试删除添加包装器(我将其替换为(function (Y) { /* module content */ })(YUI);),但这导致页面加载时出现js错误:Y.Lang未定义。因此,似乎在没有包装add()调用的情况下,脚本将在定义Y.Lang的基本yui脚本之前执行。但是,如果是这种情况,那么对于非YUI模块(不调用YUI.add())这不会是一个问题吗?

1 个答案:

答案 0 :(得分:5)

区分使用YUI3功能的自定义模块(沙盒Y.Lang等)和完全外部代码非常重要。

在第一种情况下,YUI.add()包装器始终是必需的,因为沙箱Y变量在模块回调之外不可用(YUI.add()的第二个参数)。不幸的是,由于Y.Loader(组合加载魔法发生的地方)内的约束,手写模块中重复模块配置是必要的。使用YUI的build tools的模块具有自动添加的包装器和元数据。

使用完全外部代码,您只需要提供fullpath配置属性,YUI就会做正确的事情。在内部,YUI知道给定的<script>请求何时完成,并将该成功与配置的模块名称相关联。

为简化起见,我将使用YUI.applyConfig来演示配置位。使用它,您可以在混合配置的情况下创建任意数量的YUI沙箱(通过YUI().use(...)),而不是在整个地方重复使用。

YUI.applyConfig({
    "modules": {
        "leaflet": {
            "fullpath": "http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js"
        },
        "my-leaflet-thing": {
            "path": "path/to/my-leaflet-thing.js",
            "requires": [
                "base-build",
                "node-base",
                "leaflet"
            ]
        }
    }
});

my-leaflet-thing.js看起来像这样:

YUI.add("my-leaflet-thing", function (Y) {
    // a safe reference to the global "L" provided by leaflet.js
    var L = Y.config.global.L;

    Y.MyLeafletThing = Y.Base.create("myLeaflet", Y.Base, {
        initializer: function () {
            var id = this.get('node').get('id');
            var map = L.map(id);
            // etc
        }
    }, {
        ATTRS: {
            node: {
                getter: Y.one
            }
        }
    });

// third argument is a version number,
// but it doesn't affect anything right now
}, "1.0.0", {
    "requires": [
        "base-build",
        "node-base",
        "leaflet"
    ]
});

鉴于此设置,由于这需要非异步库,您可以安全地执行此操作:

YUI().use("my-leaflet-thing", function (Y) {
    var instance = new Y.MyLeafletThing({
        "node": "#foo"
    });
});

注意:如果外部文件自动动态加载(例如,async Google Maps API),YUI只会知道初始请求成功,而不是整个文件链加载。要解决这个问题,你需要在fullpath配置中使用querystring callback参数,该参数与需要它的模块中一些全局公开的回调相关联。

在这些情况下,最好做一个内部Y.use()(注意沙盒变量)以更好地封装所需的全局变量。

配置:

YUI.applyConfig({
    "modules": {
        "google-maps-api": {
            "fullpath": "http://maps.googleapis.com/maps/api/js" +
                            "?v=3&sensor=false&callback=initGMapsAPI"
        },
        "my-google-map-thing": {
            "path": "path/to/my-google-map-thing.js",
            "requires": [
                "base-build",
                "node-base"
            ]
        }
    }
});

MY-谷歌地图-thing.js:

YUI.add("my-google-map-thing", function (Y) {
    // publish a custom event that will be fired from the global callback
    Y.publish('gmaps:ready', {
        emitFacade: true,
        fireOnce: true
    });

    // private sentinel to determine if Y.use() has been called
    var isUsed = false;

    // expose global function that matches "callback" parameter value
    Y.config.global.initGMapsAPI = function () {
        // Y.config.global.google is now available
        Y.fire('gmaps:ready');
    };

    Y.MyGoogleMapThing = Y.Base.create("myGoogleMap", Y.Base, {
        initializer: function () {
            Y.on('gmaps:ready', this.render, this);
            if (!isUsed) {
                isUsed = true;
                Y.use("google-maps-api");
            }
        },
        render: function () {
            // safe reference to global "google"
            var google = Y.config.global.google;
            var id = this.get('node').get('id');
            var map = new google.maps.Map(id, {
                // ...
            });
            // etc
        }
    }, {
        ATTRS: {
            node: {
                getter: Y.one
            }
        }
    });

}, "1.0.0", {
    "requires": [
        "base-build",
        "node-base"
    ]
});

总结:只有在编写依赖于YUI3沙盒资源的模块时才需要YUI.add()。加载外部代码,只要它是全部同步的,就像使用fullpath config属性一样简单。异步外部加载有点毛茸茸,但仍有可能。