Javascript 当宿主站点使用RequireJS时,将KnockoutJS作为第三方包的一部分加载时出错

Javascript 当宿主站点使用RequireJS时,将KnockoutJS作为第三方包的一部分加载时出错,javascript,jquery,knockout.js,requirejs,Javascript,Jquery,Knockout.js,Requirejs,我已经开发了一个JavaScript插件,将包含在我们客户的网站上。我创建的插件依赖于一些外部库,这些库作为一个大包捆绑并交付给客户机:jQuery 1.8.2和knockoutjsv3.0.0 该插件在大多数站点上运行良好,但如果宿主站点使用RequireJS,我的包将无法加载,因为KnockoutJS会自动检测到RequireJS存在并尝试使用它。下面是引发的错误: Mismatched anonymous define() module 显然,我在RequireJS站点上找到了错误消息的

我已经开发了一个JavaScript插件,将包含在我们客户的网站上。我创建的插件依赖于一些外部库,这些库作为一个大包捆绑并交付给客户机:jQuery 1.8.2和knockoutjsv3.0.0

该插件在大多数站点上运行良好,但如果宿主站点使用RequireJS,我的包将无法加载,因为KnockoutJS会自动检测到RequireJS存在并尝试使用它。下面是引发的错误:

Mismatched anonymous define() module
显然,我在RequireJS站点上找到了错误消息的“解释”。不幸的是,我不知道如何避免它。在我本地的KnockoutJS库副本中,我发现了令人不快的一行:

(function(factory) {
    // Support three module loading scenarios
    if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
        // [1] CommonJS/Node.js
        var target = module['exports'] || exports; // module.exports is for Node.js
        factory(target);
    } else if (typeof define === 'function' && define['amd']) {
        // [2] AMD anonymous module
        define(['exports'], factory);
    } else {
        // [3] No module loader (plain <script> tag) - put directly in global namespace
        factory(window['ko'] = {});
    }
}
(功能(工厂){
//支持三种模块加载场景
if(typeof require==='function'&&typeof exports=='object'&&typeof module=='object'){
//[1]CommonJS/Node.js
var target=module['exports']| | exports;//module.exports用于Node.js
工厂(目标);
}else if(typeof define==='function'&&define['amd'])){
//[2]AMD匿名模块
定义(['exports'],工厂);
}否则{
//[3]无模块加载器(普通标记)-直接放入全局命名空间
工厂(窗口['ko']={});
}
}
如果我手动编辑此文件,使条件[2]永远不会执行,只有条件[3]每次都会执行,则一切正常。当然,我不想这样做,因为它需要我编辑一个外部库,我更愿意将其保持在原始状态,以便稍后升级

我有一种感觉,也许有一种方法可以让它发挥作用,我只是不明白RequireJS是如何工作的。显然,KnockoutJS试图与RequireJS配合得很好,但在我看来,这是失败的。对我来说,即使RequireJS存在,我也不需要KnockoutJS来使用它

如何让这两个库同时工作

编辑

我无法控制何时加载我的库以及宿主网站已经加载的所有其他库。事实上,大多数时候我的插件都会被包含在内,它将由没有web开发经验的人使用WordPress、Webs.com或Weebly等糟糕的WYSIWYG平台,所以有时我的脚本标记可能会出现在head元素的顶部,其他时候它可能包含在body元素的某个地方

另外,我的库没有使用RequireJS。碰巧我们的一个试图使用我的库的客户使用了RequireJS,当我的库被包括进来时,KnockoutJS(与我的库捆绑在一起,但还没有在宿主网站上)抛出异常,因为它认为它需要向RequireJS注册(或者至少这是我对例外情况的推测)


虽然原则上,我并不反对加载我的代码依赖于需求的库,但事实是,这会给我的用户带来缓慢而糟糕的体验,因为加载它们需要额外的请求/响应周期。

好吧,最简单的方法可能是在requirejs.ko不需要之前加载knockout如果检测到存在require,则将使用选项[3]。如果无法执行此操作,则另一个选项是在require层次结构中添加插件和ko文件

假设你的插件是这样的:

(function(ko){

//stuff
ko.applyBindings({});

})(ko) 
您需要将其更改为:

require([
    "knockout-3.0.0.js" // this should be the url you use for knockout
    ], function(ko){
       //stuff
       ko.applyBindings({});
})
不要将knockout.js文件作为单独的标记加载。Require将处理加载。服务器当然必须仍然能够提供“knockout-3.0.0.js”url。Require就是这样工作的。它加载您作为Require的数组参数中的元素传递的url,并将它们作为参数返回的内容传递给函数

如果需要将插件文件和ko文件缩小/捆绑到单个文件中,可以使用reuquirejs缩小器/优化器()。它将浏览依赖关系树并只输出一个包含所有模块的js文件。这里有一个怪癖:你需要删除.js扩展名,迷你们才能工作,阅读更多关于它的文档,我刚才提到它是为了省去一些麻烦

此外,有关如何将ko与require一起使用的更多文档可在此处找到:

编辑,操作编辑后:

好的,在这种情况下,你应该创建一个单独的作用域,在这个作用域中你可以做你想做的事情。你需要在你的文件中复制ko代码,但是像这样你至少会得到一个文件

因此,首先创建一个范围:

(function(){

})()
然后将ko代码复制到:

(function(){

//ko code here, should be a single, minified line

})()
然后,您需要诱使ko使用选项3,因此请执行以下操作:

(function(){

var define = null; //so define will no longer be a function, don't forget the var
var require = null;
//ko code here, should be a single, minified line

})()
或者,如果不希望ko对整个页面可用,您可能还希望在上述步骤中重新分配窗口

现在添加您的插件代码:

(function(){

var define = null; //so define will no longer be a function, don't forget the var
var require = null;
//ko code here, should be a single, minified line

//plugin code here;

})()

如果客户端已经在使用jQuery和Knockout,那么您的方法会迫使他们加载第二个副本,这难道不是另一个问题吗?另一个方法是重新考虑您的打包结构,在检测到时检测并使用RequireJS加载这些模块。您如何加载插件?jQuery上的插件依赖性如何d ko指定了?@explunit--是的,很遗憾,当前的打包机制会导致加载第二个副本。显然,这不是首选,但我到目前为止所做的测试,这不是一个问题。不是最佳的,是吗?但现在可以接受。@pax162--插件只是通过一个单独的脚本标记加载,该标记下载javascript file将所有依赖项和我的自定义代码捆绑在一起。进行依赖项分析并根据需要从客户端加载软件包将创建一个非常缓慢的体验。客户端页面必须呈现,然后从我的服务器下载第一批javascript,然后分析需要加载的依赖项,并进行修改请求获取这些代码,然后等待它们加载,然后我的任何代码才能执行