0

我试图通过将功能分解为共享许多通用代码的单独应用程序来模块化我现有的项目。这是一个 Backbone/Marionette 应用程序,在开发模式下一切正常,但我在优化工作时遇到了麻烦。我目前有两个页面,有 2 个主文件和 2 个应用程序文件。这main两个文件都包含几乎相同的 requirejs.config 块,除了第二个使用mapconfig 选项将app模块映射到loginApp. 这样做的原因是大多数其他模块依赖于该app模块来实现某些应用程序范围的功能,包括消息传递和一些全局状态变量。

主.js

requirejs.config({
    shim: { ... },
    paths: { ... }
});

define(['vendor'], function() {
    // This loads app.js
    require(['app'], function(Application) {
        Application.start();
    });
});

主登录.js

requirejs.config({
    shim: { ... },
    paths: { ... },
    map: {
        "*": { "app": "loginApp" }
    }
});

define(['vendor'], function() {
    // This loads loginApp.js because of the mapping above
    require(['app'], function(Application) {
        Application.start();
    });
});

在我优化之前,这很好用。我收到有关丢失文件的错误,但是在使用 requirejs 足够长的时间后,我知道这与问题无关。:)

从文档:

注意:使用地图配置进行构建时,需要将地图配置提供给优化器,并且构建输出仍必须包含设置地图配置的 requirejs 配置调用。优化器在构建期间不会进行 ID 重命名,因为项目中的某些依赖项引用可能依赖于运行时变量状态。因此,优化器不会在构建后使对地图配置的需求无效。

我的build.js文件如下所示:

({
    baseUrl: "js",
    dir: "build",
    mainConfigFile: "js/main.js",
    removeCombined: true,
    findNestedDependencies: true,
    skipDirOptimize: true,
    inlineText: true,
    useStrict: true,
    wrap: true,
    keepBuildDir: false,
    optimize: "uglify2",
    modules: [
        {
            name: "vendor"
        },
        {
            name: "main",
            exclude: ["vendor"]
        },
        {
            name: "main-login",
            exclude: ["vendor"],
            override: {
                mainConfigFile: "js/main-login.js",
                map: {
                    "*": {
                        "app": "loginApp"
                    }
                }
            }
        }
    ]
});

如果可能的话,我想避免使用 2 个单独的构建文件,并且我正在努力将requirejs.config块分解为一个共享文件并让 2 个main文件加载然后加载app文件(这类似于多页示例有效)但我需要该地图配置在优化器中工作才能使其正常工作。有什么想法我在这里想念的吗?

更新

我已将配置拆分为它自己的文件,该文件config.js包含在main-*文件中。在main-login.js文件中,我在上面包含了地图配置,define并且一切都在开发模式下工作。

require.config({
    map: {
        "*": {
            "app": "loginApp"
        }
    }
});

define(['module', 'config'], function(module, config) {
    ...

build.js文件与上述相同,除了第二个mainConfigFile被删除。但是,优化仍然失败。我认为正在发生的是,因为这是一个 Marionette 应用程序,通常的做法是将 Application 对象作为依赖项传递给应用程序的其他部分,包括视图、控制器和模型。当我优化时,我遇到了两个不同的问题。如果我离开removeCombinedas true,优化器将构建第一个应用程序的依赖项,然后删除这些文件,因此当它在第二个应用程序中看到它们时,它将失败,因为它再也找不到源文件。将此设置为false似乎合理,但问题是这会给我以下错误:

Error: RangeError: Maximum call stack size exceeded

我找不到有关此特定错误的任何一致信息。它可能与hbs插件有关(类似于text但用于预编译 Handlebars 模板),但我不肯定是这种情况。由于没有堆栈跟踪,我不确定从哪里开始寻找。不过,我的直觉是它在某个地方是循环依赖。所以,我更新的问题是,应该如何解耦多页 Marionette 应用程序,以使共享代码(不仅仅是 3rd 方代码,还有数据模型和视图等自定义代码)成为可能?我是否需要删除对核心应用程序对象的任何依赖项?(这将需要大量的重构。)由于它在开发模式下工作得很好,r.js我忽略的配置是否有一些技巧?我'appexclude列表以及stubModules但似乎没有任何工作。我正在考虑只创建 2 个构建文件并完成它,但我真的很想知道如何以“正确”的方式解决这个问题。

4

2 回答 2

1

你的构建文件可以是这样的:

({
    ...
    include: [ './config/main.js' ],
    pragmasOnSave: {
        excludeBuildConfig: true
    }
})

您可以使用 pragmasOnSave 告诉优化器在优化结果中排除文件中的部分,因此 Requirejs 配置文件可以像以下代码

requirejs.config({
//>>excludeStart('excludeBuildConfig', pragmas.excludeBuildConfig)
    shim: { ... },
    paths: { ... },
//>>excludeEnd('excludeBuildConfig')
    map: {
          "*": { "app": "loginApp" }
    }
});
于 2015-02-22T21:16:07.637 回答
0

最终使用的解决方案是将 Grunt 合并到构建工作流程中。在 Grunt 中,我正在为任务动态创建任务目标requirejs。我重构了我的多个应用程序以全部使用相同的文件夹结构,因此很容易为每个应用程序重用相同的构建配置。多次编译文件仍然存在一些不便vendor,但这是一个很小的代价。

这是我用来在dev任务中创建配置的函数,以防有人感兴趣:

var buildRequireTargets = function(appList) {
    var requireTargets = {},
        buildConfig = {
            baseUrl: "<%= sourceDir %>/js",
            dir: "<%= buildDir %>/js",
            mainConfigFile: "<%= sourceDir %>/js/config.js",
            removeCombined: true,
            findNestedDependencies: true,
            skipDirOptimize: true,
            inlineText: true,
            useStrict: true,
            wrap: true,
            keepBuildDir: true,
            optimize: "none",
            pragmasOnSave: {
                excludeHbs: true
            }
        };

    _.each(appList, function (app) {
        requireTargets[app] = {
            options: _.extend({
                map: {
                    "*": {
                        "app": app + "/app"
                    }
                },
                modules: [
                    {
                        name: "vendor"
                    },
                    {
                        name: app + "/main",
                        exclude: ["vendor"]
                    }
                ]
            }, buildConfig)
        };
    });

    grunt.config("requirejs", requireTargets);
};
于 2015-04-01T14:07:56.290 回答