我想使用requireJS,我正在使用jQuery。我不想使用 requireJS 和 jQuery 的组合版本,因为我没有使用最新的 jQuery 版本。我使用 requireJS 的最佳方式是什么?
5 回答
这也是我的确切问题!我还必须使用较旧的 jQuery,但也必须使用更“传统”的 javascript 库。做到这一点的最佳技术是什么?(如果您不介意,我可以编辑您的问题以使其更广泛。)这是我学到的。
RequireJS 作者 James Burke 解释了RequireJS + jQuery 文件组合的优点。你得到两件事。
一个模块 ,
jquery
是可用的,它是 jQuery 对象。这是安全的:// My module depends on jQuery but what if $ was overwritten? define(["jquery"], function($) { // $ is guaranteed to be jQuery now */ })
jQuery 已经在任何东西之前
require()
加载define()
。所有模块都保证 jQuery 已准备就绪。你甚至不需要这个require/order.js
插件,因为 jQuery 基本上是硬编码的,可以先加载。
对我来说,#2 不是很有帮助。大多数实际应用程序都有许多 .js
文件必须以正确的顺序加载——可悲但真实。只要您需要 Sammy 或 Underscore.js,组合的 RequireJS+jQuery 文件就无济于事。
我的解决方案是编写简单的 RequireJS 包装器,使用“order”插件加载我的传统脚本。
例子
假设我的应用程序具有这些组件(通过依赖项)。
- 我的应用程序,很棒的应用程序
- greatapp 依赖于自定义jquery(我必须使用旧版本)
- greatapp 依赖于my_sammy(SammyJS 加上我必须使用的所有插件)。这些必须有序
- my_sammy 依赖于jquery(SammyJS 是一个 jQuery 插件)
- my_sammy 依赖于sammy.js
- my_sammy 依赖于sammy.json.js
- my_sammy 依赖于sammy.storage.js
- my_sammy 依赖于sammy.mustache.js
在我看来,以上结尾的所有内容.js
都是“传统”脚本。没有的一切.js
都是 RequireJS 插件。关键是:高层次的东西(greatapp、my_sammy)是模块,在更深层次上,它回退到传统.js
文件。
引导
这一切都始于一个引导程序告诉 RequireJS 如何开始。
<html>
<head>
<script data-main="js/boot.js" src="js/require.js"></script>
</head>
</html>
我js/boot.js
只放了配置以及如何启动应用程序。
require( // The "paths" maps module names to actual places to fetch the file.
// I made modules with simple names (jquery, sammy) that will do the hard work.
{ paths: { jquery: "require_jquery"
, sammy : "require_sammy"
}
}
// Next is the root module to run, which depends on everything else.
, [ "greatapp" ]
// Finally, start my app in whatever way it uses.
, function(greatapp) { greatapp.start(); }
);
主要应用
在greatapp.js
我有一个正常的模块。
define(["jquery", "sammy"], function($, Sammy) {
// At this point, jQuery and SammyJS are loaded successfully.
// By depending on "jquery", the "require_jquery.js" file will run; same for sammy.
// Those require_* files also pass jQuery and Sammy to here, so no more globals!
var start = function() {
$(document).ready(function() {
$("body").html("Hello world!");
})
}
return {"start":start};
}
RequireJS 围绕传统文件的模块包装器
require_jquery.js
:
define(["/custom/path/to/my/jquery.js?1.4.2"], function() {
// Raw jQuery does not return anything, so return it explicitly here.
return jQuery;
})
require_sammy.js
:
// These must be in order, so use the "order!" plugin.
define([ "order!jquery"
, "order!/path/to/custom/sammy/sammy-0.6.2-min.js"
, "order!/path/to/custom/sammy/plugins/sammy.json-0.6.2-min.js"
, "order!/path/to/custom/sammy/plugins/sammy.storage-0.6.2-min.js"
, "order!/path/to/custom/sammy/plugins/sammy.mustache-0.6.2-min.js"
]
, function($) {
// Raw sammy does not return anything, so return it explicitly here.
return $.sammy;
}
);
这个问题现在至少有两年了,但我注意到这仍然是 RequireJS 2.0 的问题(require-jquery.js 使用 jQuery 1.8.0,但最新版本是 1.8.2)。
如果您碰巧看到这个问题,请注意require-jquery.js 现在只是 require.js 和 jquery.js,混合在一起。 您可以只编辑 require-jquery.js 并将 jQuery 部分替换为更新的版本。
更新(2013 年 5 月 30 日):现在 RequireJS 有路径和 shim,有一种新的方法来导入 jQuery 和 jQuery 插件,旧方法不再需要也不推荐。这是当前方法的精简版:
requirejs.config({
"paths": {
"jquery": "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min"
}
});
define(["jquery"], function($) {
$(function() {
});
});
有关详细信息,请参阅http://requirejs.org/docs/jquery.html。
我发现最好的方法是将 jQuery 保留在我的 RequireJS 构建之外。
只需在您的 HTML 中包含 jquery.min.js。然后,用这样的东西制作一个 jquery.js 文件......
define([], function() {
return window.$;
});
发现 JasonSmith 的回答非常有帮助,可能比 RequireJS 的文档更有帮助。
但是,有办法对其进行优化以避免对(微小的)定义声明模块(“require_jquery”“require_sammy”)产生单独的 AJAX 请求。我怀疑 r.js 会在优化阶段执行此操作,但您可以提前执行此操作,以免与 Path、BaseURI 系统发生冲突。
索引.html:
<html>
<head>
<script data-main="js/loader.js" src="js/require.js"></script>
</head>
</html>
加载器.js:
// We are going to define( dependencies by hand, inline.
// There is one problem with that through (inferred from testing):
// Dependencies are starting to load (and execute) at the point of declaring the inline
// define, not at the point of require(
// So you may want to nest the inline-defines inside require(
// this is, in a way, short replacement for Order plug in, but allows you to use
// hand-rolled defines, which the Order plug in, apparently does not allow.
var jQueryAndShims = ['jquery']
if(window.JSON == null){
jQueryAndShims.push('json2')
define(
'json2'
, ['js/libs/json2.min.js']
, function() {
return window.JSON
}
)
}
// will start loading the second we define it.
define(
'jquery'
, ['js/libs/jquery_custom.min.js']
, function() {
// we just pick up global jQuery here.
// If you want more than one version of jQuery in dom, read a more complicated solution discussed in
// "Registering jQuery As An Async-compatible Module" chapter of
// http://addyosmani.com/writing-modular-js/
return window.jQuery
}
)
// all inline defines for resources that don't rely on other resources can go here.
// First level require(
// regardless of depends nesting in 'myapp' they will all start downloading
// at the point of define( and exec whenever they want,
// async in many browsers. Actually requiring it before the nested require makes
// sure jquery had *executed and added jQuery to window object* before
// all resolved depends (jquery plugins) start firing.
require(jQueryAndShims, function($) {
// will start loading the second we define it.
define(
'sammy_and_friends'
, ['jquery','js/libs/jquery_pluginone.min.js','js/libs/jquery_plugintwo.min.js','js/libs/sammy.min.js']
, function($) {
// note, all plugins are unaltered, as they are shipped by developers.
// in other words, they don't have define(.. inside.
// since they augment global $ (window.jQuery) anyway, and 'jquery' define above picks it up
// , we just keep on returning it.
// Sammy is attached to $ as $.sammy, so returning just Sammy makes little sense
return $
}
)
// second level require - insures that Sammy (and other jQuery plugins) - 'sammy_and_friends' - is
// loaded before we load Sammy plugins. I normally i would inline all sammy plugins i need
// (none, since i use none of them preferring jQuery's direct templating API
// and no other Sammy plug in is really of value. ) right into sammy.js file.
// But if you want to keep them separate:
require(['sammy_and_friends'], function() {
// will start loading the second we define it.
define(
'sammy_extended'
, ['sammy_and_friends','js/libs/sammy_pluginone.min.js','js/libs/sammy_plugintwo.min.js']
, function($) {
// as defined above, 'sammy_and_friends' actually returns (globall) jQuery obj to which
// Sammy is attached. So we continue to return $
return $
}
)
// will start loading the second we define it.
define(
'myapp'
, ['sammy_extended', 'js/myapplication_v20111231.js']
, function($, myapp_instantiator) {
// note, myapplication may, but does not have to contain RequireJS-compatible define
// that returns something. However, if it contains something like
// "$(document).ready(function() { ... " already it MAY fire before
// it's depends - 'sammy_extended' is fully loaded.
// Insdead i recommend that myapplication.js returns a generator
// (app-object-generating function pointer)
// that takes jQuery (with all loaded , applied plugins)
// The expectation is that before the below return is executed,
// all depends are loaded (in order of depends tree)
// You would init your app here like so:
return myapp_instantiator($)
// then "Run" the instance in require( as shown below
}
)
// Third level require - the one that actually starts our application and relies on
// dependency pyramid stat starts with jQuery + Shims, followed by jQuery plugins, Sammy,
// followed by Sammy's plugins all coming in under 'sammy_extended'
require(['jquery', 'myapp'], function($, myappinstance) {
$(document).ready(function() {myappinstance.Run()})
})
}) // end of Second-level require
}) // end of First-level require
最后,myapplication.js:
// this define is a double-wrap.
// it returns application object instantiator that takes in jQuery (when it's available) and , then, that
// instance can be "ran" by pulling .Run() method on it.
define(function() {
// this function does only two things:
// 1. defines our application class
// 2. inits the class and returns it.
return function($) {
// 1. defining the class
var MyAppClass = function($) {
var me = this
this._sammy_application = $.sammy(function() {
this.raise_errors = true
this.debug = true
this.run_interval_every = 300
this.template_engine = null
this.element_selector = 'body'
// ..
})
this._sammy_application.route(...) // define your routes ets...
this.MyAppMethodA = function(blah){log(blah)} // extend your app with methods if you want
// ...
// this one is the one we will .Run from require( in loader.js
this.Run = function() {
me._sammy_application.run('#/')
}
}
// 2. returning class's instance
return new MyAppClass($) // notice that this is INITED app, but not started (by .Run)
// .Run will be pulled by calling code when appropriate
}
})
这种结构(松散地替换(重复?)RequireJS 的 Order 插件,但是)允许您修剪 AJAX 所需的文件数量,为依赖和依赖树的定义添加更多控制。
单独加载 jQuery(通常为 100k)还有一个很大的好处——您可以控制服务器上的缓存,或将 jQuery 缓存到浏览器的 localStorage 中。看看这里的 AMD-Cache 项目https://github.com/jensarps/AMD-cache 然后更改定义(语句以包括“缓存!”:它将(永远:))卡在用户的浏览器中。
define(
'jquery'
, ['cache!js/libs/jquery_old.min.js']
, function() {
// we just pick up global jQuery here.
// If you want more than one version of jQuery in dom, read a more complicated solution discussed in
// "Registering jQuery As An Async-compatible Module" chapter of
// http://addyosmani.com/writing-modular-js/
return window.jQuery
}
)
关于 jQuery 1.7.x+ 的注意事项 它不再将自身附加到窗口对象,因此上述内容不适用于未修改的 jQuery 1.7.x+ 文件。在那里,您必须自定义您的 jquery**.js 以在关闭 "})( window );" 之前包含它:
;window.jQuery=window.$=jQuery
如果您在控制台中出现“jQuery 未定义”错误,则表明您使用的 jQuery 版本未将自身附加到窗口。
代码许可证:公共领域。
披露:JavaScript 上面有“伪代码”的味道,因为它是更详细的生产代码的释义(手工修剪)。上述代码不保证可以正常工作,也没有经过测试可以按所示工作。审核,测试。故意省略分号,因为它们不是 JS 规范所必需的,并且没有它们代码看起来会更好。
除了 jhs 的答案,请参阅README.md 文件中require-jquery github 页面上的最新说明。它涵盖了使用组合 jquery/require.js 文件的最简单方法以及如何使用单独的 jquery.js。