【问题标题】:RequireJS optimize multi-page app using map configRequireJS 使用地图配置优化多页应用
【发布时间】:2015-02-20 20:42:52
【问题描述】:

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

ma​​in.js

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

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

ma​​in-login.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 文件(这类似于 multipage example 的工作方式)但我需要该映射配置在优化器中工作才能使其工作。任何想法我在这里缺少什么?

更新

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

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

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

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

Error: RangeError: Maximum call stack size exceeded

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

【问题讨论】:

  • + 仅使用 一个配置文件(建议只使用一个 require.config({}) + 您可以使用多个构建文件来构建不同的 js 文件 + 依赖项是区分大小写(对同一文件的所有引用必须完全相同)
  • 我最终使用了两个构建文件(和一个配置文件),现在一切正常。发生了一些不必要的重复(“供应商”文件被编译两次,以防文件乱序),但这是一个很小的代价。

标签: javascript requirejs marionette requirejs-optimizer


【解决方案1】:

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

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

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

requirejs.config({
//>>excludeStart('excludeBuildConfig', pragmas.excludeBuildConfig)
    shim: { ... },
    paths: { ... },
//>>excludeEnd('excludeBuildConfig')
    map: {
          "*": { "app": "loginApp" }
    }
});

【讨论】:

  • 这是一个非常酷的想法,感谢您提供的信息。我能够在构建脚本中使用override 并在其中包含地图(我创建了一个共享配置文件,类似于多页站点示例),但还有其他问题阻止我编译。我将花更多时间重构和搜索,并将用我的结果更新这个问题。
【解决方案2】:

使用的最终解决方案是将 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);
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-02
    • 2012-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多