我正在尝试减轻应用服务器上的负载。我希望它只提供一个小页面shell,并从我的CDN提供优化的捆绑js。我可以这样做吗?
我问,因为我没有运气,在这种情况下应该问的第一个问题是“我应该这样做吗?”也许这不是该工具的预期工作流程。
该项目可能包含100个左右的js文件(大多数是我的,有些是第三方)。我通过require使用AMD来使其易于管理。我运行了优化器并生成了一个优化文件。但是当我将该文件放在我的CDN上并从我项目中的脚本标签引用它时,应用程序会抱怨如下:
GET https://myserver.com/modules/util/utilloader.js net::ERR_ABORTED 12:50:16.452 require.js:168 Uncaught Error: Script error for "modules/util/utilloader" http://requirejs.org/docs/errors.html#scripterror at makeError (require.js:168) at HTMLScriptElement.onScriptError (require.js:1735)
似乎它可能会抱怨,因为它希望组件/文件位于我服务器上相对于我的应用程序的路径上。但我希望它在捆绑文件中查找该代码。
我正在努力做到可行吗?如果是这样,我如何让应用程序在优化文件中查找代码,而不是在与我服务器上的app root相关的其他文件中?
附加细节(根据要求): 配置文件(application.js)本质上首先需要一堆第三方的东西。然后它需要我制作的模块。这似乎是麻烦开始的地方。如果你查看下面的application.js文件,有两个require语句在依赖项列表中有路径(utilloader,mod1,mod2,mod3等)。它永远不会找到那些因为它们不存在。我已将这些文件捆绑到一个名为application.js的文件中。这就是现在所有,只有一个文件。这种困境告诉我,我对这个工具和流程一般都不了解。如何在没有像“modules / util / utilloader”这样的路径的情况下需要像utilloader这样的东西?我原以为优化器会理解这个问题并在捆绑时解释它。但同样,我认为我不理解这个工具或过程。
以下是相关文件:
建立文件
({
appDir: ".",
baseUrl: "myapp",
dir: "../scripts-build",
mainConfigFile: 'myapp/application.js',
modules: [
{
name: "application"
}
]
})
代码结构(优化程序目录)
-optimize -r -scripts -backbone.js -backbone.marionette.js ...many more 3rd party libs -underscore.js -myapp -application.js -controller.js -router.js -modules (complex modules each with many dependencies. Sub-structures omitted here for brevity) -mod1 -mod2 -mod3 -util
CONFIG(application.js)
require.config({
paths: {
jquery: '../jquery-2.2.3.min',
bootstrap: '../bootstrap.min',
jqueryui: '../jquery-ui-1.11.4.min',
jqcloud: '../jqcloud.min',
jqmarquee: '../jquery.marquee.min',
bootstrapslider: '../bootstrap-slider.min',
underscore: '../underscore.min',
backbone: '../backbone.min',
marionette: '../backbone.marionette.min',
handlebars: '../handlebars.min',
hls: '../hls.min'
},
shim: {
'jqueryui': {
deps: ['jquery']
},
"bootstrap": {
"deps": ['jquery']
},
'jqcloud': {
deps: ['jquery']
},
'jqmarquee': {
deps: ['jquery']
},
'bootstrapslider': {
deps: ['jquery', 'bootstrap']
},
underscore: {
exports: '_'
},
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
marionette: {
deps: ["backbone"],
exports: "Marionette"
}
}
});
//Outer require - Need this 3rd party stuff to make our Marionette app.
//There will be 2 more requires inside this outer require.
require(["marionette", "hls", "jqueryui", "jqcloud", "jqmarquee", "handlebars", "bootstrap", "bootstrapslider"], function (Marionette, Hls) {
//Create a Marionette js application
window.App = new Marionette.Application();
//Do a bunch of stuff in the app's global space that uses some of the 3rd party packages required above
...code ommitted here for brevity
//#1
//Require some other modules (mine, not 3rd party) before officially "starting" the marionette js application.
//Each of these modules include many dependencies (many dozens of files) of thier own in their definitions
require([
"modules/util/utilloader",
"modules/mod1/mod1loader",
"modules/mod2/mod2loader",
"modules/mod3/mod3loader"
], function () {
App.start();
});
//#2
//Require two more files (mine) to make a backbone router.
require(["controller", "router"], function (Controller, AppRouter) {
var router = new AppRouter({ controller: new Controller() });
});
//When the marionette js application is officially "started", start the backbone history.
App.on("start", function () {
//History
if (Backbone.history) {
Backbone.history.start();
}//End history
//Some more app start functions
...code ommitted here for brevity
});//end App on start handler
})//end outer require
我的主要配置文件(application.js)所需的示例模块(utilloader.js)
// loader for Util module (lets you spread this module across multiple files)
// the 'loader' includes initial module definition and manages the loading
// of all module files including javascript and templates.
// define base module elements; other module files may depend
// on this, but it must not depend on any other module files.
//NOTE: I think this first piece here is what the optimizer is complaining about
//It's not inside a define?
App.module("UtilModule", function (UtilModule) {
UtilModule.views = {}; //put your views and models in structures so you
UtilModule.models = {};//can get at them from anywhere in the module
});
// Recommended: define all dependencies for this module
// while you could spread dependency requirements
// over all your module files on purely "as needed" basis,
// this adds to complication of code in your module files
// defining them all, here, has the advantage of limiting use of RequireJS
// to this loader file only
var dependencies = [
"modules/util/views/view1",
"modules/util/views/view2",
"modules/util/views/view3",
"modules/util/models/model1",
"modules/util/models/model2",
"modules/util/models/model3"
];
// define the loader last. generally, it should depend on all
// module files, otherwise they may not get loaded
define(dependencies,
function () {
App.module("UtilModule", function (UtilModule, App, Backbone, Marionette, $, _) {
//Some functions here to make views, bind them to the models listed above and show them.
//...ommitted here for space and clarity
});//Close the module definition
});//Close the define function
更新12/13/2017
也许我的优化内容不起作用,因为并非所有文件都符合AMD标准(未包含在定义函数中的内容)。所以我完成了整个项目并解决了这个问题。我在整个项目中所做的修订类型的示例如下,并概述了新的问题。
我的主要配置文件(application.js)要求修改的示例模块(utilloader.js)上面的版本没有被优化器(不符合AMD标准)所喜欢。它有一些超出定义功能(模块定义,var依赖)。在这里,我已经修改过,要把它塞进去。现在应该好了。但是这样做我得到了新的错误。现在有人抱怨外部文件中定义的内容并未在此模块中受到限制。因此,我说,我定义了一个外部文件内的一个backBone模型,模块/工具/模型/模型1,尝试在此文件中创建该模型的实例,从而产生未定义的错误。这个模块只是不知道这些外部文件中的内容,即使它们是在这个模块的依赖性阵列中列出的。所以要求外部文件没有任何错误,但是在这个模块中创建的东西并没有在这里受到限制。但是所有这些运行都很精细(如果我不使用优化器)。为什么不同?如果代码以某种方式进行表现,那么我希望它能够以两种方式失败,或者以两种方式成功。
// loader for Util module (lets you spread this module across multiple files)
// the 'loader' includes initial module definition and manages the loading
// of all module files including javascript and templates.
// Recommended: define all dependencies for this module.
// while you could spread dependency requirements
// over all your module files on purely "as needed" basis,
// this adds to complication of code in your module files.
// defining them all, here, has the advantage of limiting use of RequireJS
// to this loader file only
// define the loader last. generally, it should depend on all
// module files, otherwise they may not get loaded
define(
[
"modules/util/views/view1",
"modules/util/views/view2",
"modules/util/views/view3",
"modules/util/models/model1",
"modules/util/models/model2",
"modules/util/models/model3"
],
function () {
App.module("UtilModule", function (UtilModule, App, Backbone, Marionette, $, _) {
UtilModule.views = {}; //put your views and models in structures so you
UtilModule.models = {};//can get at them from anywhere in the module
//Try to create an instance of a model defined in an external file.
var model1 = new Model1();
//No errors are reported in the parsing of the external file. But the line above
//produces 'Model1' is undefined. I have dozens of external definitions like this
//in the app. All of them resolve fine unoptimized. When I run the requirejs optimizer,
//none of them resolve.
});//Close the module definition
});//Close the define function
答案 0 :(得分:0)
是的,它应该是可能的,是的,你应该(通常是*)这样做。
如果您没有开始这样做,您可能需要调整代码中的操作方式以使其发挥得很好。如果该捆绑包不能为您工作,那么还有其他人可以尝试(Webpack和汇总是两个流行的)。它应该是可能的。
要实际提供有关如何使其工作的帮助,我们需要了解更多信息,例如配置和代码结构。但一般情况下,请确保在代码中使用相对路径。
一般来说,捆绑是一个好主意。但是有一些例外。
如果您有不同人员访问的网站的谨慎区域(例如,普通用户与管理员),您可能希望将其捆绑为两个(或三个,如果它们共享可以是公共文件的公共代码)文件而不是两个。
另一个例外是如果文件超级疯狂,打破它可能会更好(但这是一个极端的情况)。
最后,您不想捆绑的另一个原因是您使用的是HTTP / 2。通常,使用HTTP / 1进行捆绑,因为由于HTTP / 1的握手,发送一个大文件然后发送一些较小的文件会更快。使用HTTP / 2,该公式会发生变化,因为您基本上可以通过1次握手发送多个文件,因此发送大量较小的文件实际上会变得更快。
对于HTTP / 2,它还取决于用户使用此浏览器支持此功能。对它的支持即将到来,并且在明年内,绝对应该完成。现在,这是一个灰色的区域,你理想的是希望有个性化的分析来帮助指导你的决定。
答案 1 :(得分:0)
是的,你可以做你想做的事。没有什么是禁止的,或者相对于RequireJS支持的界限。
您要求r.js
跟踪嵌套在require
个调用中的require
个调用的依赖关系。为此,您需要在构建配置中使用findNestedDependencies: true
。否则,r.js
只会查看顶级define
和require
来电并仅处理这些调用。处理嵌套依赖项需要付出代价,因此默认情况下它是关闭的。
要么是这样,要么压扁require
来电的层次结构。 是某些外部约束迫使您拥有层次结构的情况。但是,很多时候根本不需要它。
ETA:我看到了你的编辑。您的模块不是合适的AMD模块。它会调用define
,但是您传递给define
的工厂函数之外的代码,这是禁止的。
使用r.js
创建捆绑包时,默认情况下,define
之外的代码按原样填充到捆绑包中。这对您的代码来说是个问题。仅在App
获取第一个application.js
所需的模块后才会创建require
全局,但application.js
是包含utilloader.js
的同一个包的一部分,这意味着utilloader.js
'define
调用之外的代码已尝试运行并失败,因为尚未定义App
。 (有一个wrapShim
选项可以包装在define
调用中在全局空间中执行的代码,但它不会帮助您,因为您已经使用define
。它可能会导致其他的问题。)
如果您设置r.js
配置以生成除utilloader.js
之外的所有内容的捆绑包,并将utilloader.js
放入第二个捆绑包中,您应该能够获得所需的行为。这样,utilloader.js
的代码在请求之前不会执行。 (当您不使用优化程序时也会发生这种情况。)您必须对依赖于App
定义的所有模块执行此操作。您可能需要处理其他限制,这些限制在您在问题中显示的代码部分中并不明显。如下所示:
({
appDir: ".",
baseUrl: "myapp",
dir: "../scripts-build",
mainConfigFile: 'myapp/application.js',
modules: [
{
name: "application",
exclude: ["modules/util/utilloader"],
},
{
name: "modules/util/utilloader",
}
],
})
您也可以尝试使用excludeShallow
。同样,具体工作取决于问题中没有的细节。你必须试验。
但最终,拥有表现良好的AMD模块并停止依赖于通过全局变量传递信息会更好。
另请注意,除非您使用的是旧版本的Underscore,Backbone和Marionette,否则它们不需要shim
配置,因为它们都会调用define
。并且您不得将shim
用于调用define
的脚本。充其量,你的shim
对他们没有影响。最糟糕的是它可能会产生意想不到的影响。 RequireJS没有指定将shim
与调用define
的文件一起使用的语义。当我在旧版本中尝试它时,我遇到了崩溃。在这种情况下,较新版本似乎优雅地忽略shim
,但您永远不知道问题何时会再次发生。