AMD/Require JS - 如何仅按需加载文件

7

我有一些关于AMD/Require.js的问题。我对模块化js和AMD还比较新,所以如果有人能帮我解决这个问题就太好了。

假设我的系统上有两个不同的区域,被定义为模块(使用"define()"函数 - 顺便说一下,我正在使用Backbone)

  • 图书
    • BookItemView.js
    • BookModel.js
    • BookCollection.js
  • DVDs
    • DvdItemView.js
    • DvdModel.js
    • DvdCollection.js

我注意到,如果用户当前正在访问网站的“图书”部分,则Require.js也会包含DVD部分的文件。我有以下问题:

  • 是否有一种方法只包括与活动站点部分相关的文件?有些情况下,用户不想看到DVD列表,甚至没有权限这么做。我想避免服务器进行不必要的HTTP请求。有没有办法使用Require.js实现这一点?

任何想法都可以。

4个回答

4
请查看require-lazy,这是我编写的一个小型库,可以在运行时和编译时(r.js)都使用。它可以实现您所需的功能。基本用法如下:
(从main.js脚本中:)
define(["lazy!modules/books", "lazy!modules/dvds"], function(books, dvds) {
    ...
});

modules/booksmodules/dvds 脚本将像往常一样。上面的定义函数中的 booksdvds 变量实际上是实际模块的 promises。这些模块本身不会被预先加载。随后,在 main.js 的某个地方,代码会决定显示哪个视图:

if( view === "books" ) {
    books.get().then(function(realBooks) {
       ...
    });
}
else if( view === "dvds" ) {
    dvds.get().then(function(realDvds) {
       ...
    });
}

它还支持 gruntbower


3
这是可能的,但取决于您如何构建JS应用程序。 我假设您正在使用Backbone应用程序。 对于不同的框架,有不同的方法。
例如,您可能会有一个类似于以下内容的app.js
define([
    'DvdItemView', 'DvdModel', 'DvdCollection', 
    'BookItemView', 'BookModel', 'BookCollection'
], function(DvdView, DvdModel, DvdCol, BookView, BookModel, BookCol) {

   // router declarations go here

});

因为你将这个模块“定义”为依赖于上面的模块,所以在运行路由器代码之前,它们将始终被加载。正如 @alexndm 指出的那样,如果你在此运行 r.js,则可以将所有模块合并为一个。
但是,正如你提到的那样,一旦应用程序达到一定大小,你希望根据用户的交互来惰性地加载不同的部分。这更多是一个应用程序问题,在这方面 RequireJS 只能帮你解决一些问题。我认为最好的方法是在路由器中实现这一点。
routes: {
    '/books': 'booksList',
    '/books/:id': 'booksDetail',
    '/dvds': 'dvdList',
    '/dvds/:id': 'dvdDetail'
},

booksList: function() {
    if (!booksDepsLoaded) {
        // Here we go and load the appropriate files
        require(['BooksArea'], function(BooksArea) {
            // BooksArea.Model, BooksArea.Collection, etc...
            // do stuff to render a book list..
        })
    }
},

...

这是非常朴素的伪代码。你可能需要编写一些通用代码来处理仍需要加载依赖项和错误处理等路由,但重要的概念是你需要在应用程序/路由器开始之前定义路由 - 然后你需要异步代码来处理获取模块。


2
如果你正在寻找一些有用的RequireJS模式,请看看Durandal.js。他们使用基于Sammy.JS的路由器,但概念是相似的。 - Jon Jaques
1
此外,还可以在这里查看一些更高级的Marionette模式:https://gist.github.com/yethee/3729470 - Jon Jaques

3
实际上,可以通过稍微不同的项目结构来实现这一点。
James Burke有一个存储库详细介绍了如何在此处执行:https://github.com/requirejs/example-multipage-shim 基本思路是您有多个“main.js”文件,用于加载站点不同区域的依赖项。
因此,在您的情况下,您可能会有main-booksmain-dvd文件。
在不同区域的HTML中,仅需加载您需要用于两个区域的公共模块,然后加载特定的主文件...
require(['./js/common'], function (common) {
    require(['app/main-books']);
});

希望这能帮到您!

1

在生产环境中使用require.js时,建议运行r.js http://requirejs.org/docs/optimization.html

它的作用是: - 将相关脚本合并到构建层中,并通过UglifyJS(默认值)或Closure Compiler(使用Java时的选项)进行缩小。 - 通过内联@import引用的CSS文件和删除注释来优化CSS。

这意味着所有的脚本都将被连接到一个文件中,没有额外的http请求。


谢谢,我知道r.js,但是我试图避免将所有内容合并到一个文件中,因为我正在处理的这个项目非常庞大,有数百个模板文件、视图、模型和集合。你认为在初始化时加载它们是否是最好的方法? - darksoulsong
1
如果你有数百个文件,我建议使用串联。那么多的单独请求并不理想。 - kinakuta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接