要求JS为什么以及何时使用shim配置

101

我从这里阅读了 requirejs 文档 API

requirejs.config({
    shim: {
        'backbone': {
            //These script dependencies should be loaded before loading
            //backbone.js
            deps: ['underscore', 'jquery'],
            //Once loaded, use the global 'Backbone' as the
            //module value.
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                //Using a function allows you to call noConflict for
                //libraries that support it, and do other cleanup.
                //However, plugins for those libraries may still want
                //a global. "this" for the function will be the global
                //object. The dependencies will be passed in as
                //function arguments. If this function returns a value,
                //then that value is used as the module export value
                //instead of the object found via the 'exports' string.
                return this.Foo.noConflict();
            }
        }
    }
});

但我没有理解shim的部分。 为什么我需要使用shim?如何配置?能否提供更多解释?

请问有人可以举例解释为什么和何时应该使用shim吗? 谢谢。

3个回答

113

Shim 的主要用途是处理不支持 AMD 的库,但你需要管理它们的依赖关系。例如,在上面的 Backbone 和 Underscore 示例中:你知道 Backbone 需要 Underscore,所以假设你像这样编写了代码:

require(['underscore', 'backbone']
, function( Underscore, Backbone ) {

    // do something with Backbone

}

RequireJS将为Underscore和Backbone启动异步请求,但您不知道哪一个会先返回,因此在Underscore加载之前尝试使用它的Backbone可能会发生。

注意:这个underscore/backbone示例是在这些库都支持AMD之前编写的。但是这个原则对于今天不支持AMD的任何库仍然适用。

"init"钩子让您可以做其他高级操作,例如如果一个库通常会将两个不同的事物导出到全局命名空间中,但您想要在单个命名空间下重新定义它们。或者,也许您想对正在加载的库中的某些方法进行一些猴子补丁。

更多背景信息:


像你的示例代码一样,这里使用的 UnderscoreBackbone 像正常情况下一样使用,那么在这种情况下 shim 是什么作用?我可以使用 require( function() { _.extend({}); }) 吗?它能理解 _ 吗? - Stiger
"RequireJS将为Underscore和Backbone启动异步请求。" -> 如果库已经加载,是否有可能防止这种情况发生? - Codii
1
如果已经加载了库,它将不会启动另一个服务器请求,但 RequireJS 的重点在于您的代码不必关心发生了什么。也许为您特定的用例提出一个新问题? - explunit

66
根据RequireJS API文档,shim 可以让您:
配置依赖项、导出和自定义初始化,为旧式的 "浏览器全局" 脚本提供支持。这些脚本不使用 define() 来声明依赖项并设置模块值。
- 配置依赖项
假设您有两个javascript模块(moduleA和moduleB),其中一个(moduleA)依赖于另一个(moduleB)。这两个都对您的模块是必需的,因此您可以在require()或define()中指定依赖项。
require(['moduleA','moduleB'],function(A,B ) {
    ...
}

但是由于require本身遵循AMD规范,您无法确定哪一个会被提前获取。这就是shim发挥作用的地方。

require.config({
    shim:{
       moduleA:{
         deps:['moduleB']
        } 
    }

})

这将确保在加载 moduleA 之前始终先获取 moduleB。

- 配置输出值

Shim 输出值告诉 RequireJS 全局对象(假设你正在浏览器中)上的哪个成员是实际的模块值。假设 moduleA 将自己添加到 window 上作为“modA”(就像 jQuery 和 underscore 分别作为 $ 和 _),然后我们使我们的输出值为 'modA'。

require.config({
    shim:{
       moduleA:{
         exports:'modA'
        } 
    }

它将为RequireJS提供对此模块的本地引用。全局modA也将继续存在于页面上。

-旧版本"浏览器全局"脚本的自定义初始化

这可能是shim配置最重要的功能,它允许我们将“浏览器全局”,“非AMD”脚本(也不遵循模块化模式)作为依赖项添加到我们自己的模块中。

假设moduleB只是一个普通的JavaScript,有两个函数funcA()和funcB()。

function funcA(){
    console.log("this is function A")
}
function funcB(){
    console.log("this is function B")
}

虽然这两个函数都在窗口作用域中可用,但是RequireJS建议我们通过它们的全局标识符/句柄来使用它们,以避免混淆。所以将shim配置为

shim: {
    moduleB: {
        deps: ["jquery"],
        exports: "funcB",
        init: function () {
            return {
                funcA: funcA,
                funcB: funcB
            };
        }
    }
}

初始化函数的返回值被用作模块导出值,而不是通过“exports”字符串找到的对象。这将使我们能够在我们自己的模块中使用funcB。

require(["moduleA","moduleB"], function(A, B){
    B.funcB()
})
希望这有所帮助。

3
容易理解!一个问题:在最后一个例子中,“exports”属性是否被简单地忽略了? - Niko Bellic
不,它并没有被忽略。在上一个例子中,如果“exports”属性被忽略了,那么您传递的对象参数(在本例中为'B')将是未定义的,因为moduleB不符合AMD规范,不会返回RequireJS可用的对象(因此'B.funcB'将无法工作)。 - nalinc
嗯,我以为导出的值会被 init 函数返回的对象覆盖。所以参数 B 应该是对象 {funcA: funcA, funcB: funcB},而不仅仅是 funcB 本身。这不是真的吗? - Niko Bellic
4
尼克·贝利奇是正确的,出口确实被忽略了(我刚刚测试过)。对象B是“init”部分指定的函数返回的对象。如果您删除了“init”部分,对象B将变成函数funcB,因此您只需执行B(),而不是B.funcB()。显然,在这种情况下,funcA将变得无法访问。 - user4205580

-2

你必须在 requirejs.config 中添加路径来声明,例如:

requirejs.config({
    paths: {
          'underscore' : '.../example/XX.js' // your JavaScript file
          'jquery' : '.../example/jquery.js' // your JavaScript file
    }
    shim: {
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                return this.Foo.noConflict();
            }
        }
    }
});

1
这个回答只是一个代码转储,对于“为什么和何时使用shim config”的解释毫无帮助。如果您编辑您的答案并提供解释,请确保添加一些新的内容,而不是已经被之前的答案覆盖的部分。 - Louis
复制粘贴,不包含任何积极反馈。 - william.eyidi
在“shim”之前应该有一个逗号: - Scott

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