繁体   English   中英

RequireJS影响嵌入式jquery-ui小部件

[英]RequireJS effecting an embedded jquery-ui widget

我有一个jQuery小部件,合作伙伴正试图嵌入。 我们遇到的问题是合作伙伴正在使用requireJS及其影响小部件。

小部件是匿名函数,需要jquery-ui。 在调试之后,我们发现在noConflict调用之后将删除jQuery UI。 这是小部件的代码。

(function () {

    // Localize jQuery variable
    var jQueryWidget;

    /******** Load jQuery if not present *********/
    if (window.jQuery === undefined || window.jQuery.fn.jquery !== '3.2.1') {
        var script_tag = document.createElement('script');
        script_tag.setAttribute("type", "text/javascript");
        script_tag.setAttribute("src", "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js");
        script_tag.onload = scriptLoadHandler;
        script_tag.onreadystatechange = function () { // Same thing but for IE
            if (this.readyState == 'complete' || this.readyState == 'loaded') {
                scriptLoadHandler();
            }
        };
        (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
    } else {
        loadJQueryUi();
    }

    function scriptLoadHandler() {
        loadJQueryUi();    
    }

    function loadJQueryUi() {
    /******* Load UI *******/
        jQuery.getScript('https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js', function () {
          jQueryWidget = jQuery.noConflict(true);
          setHandlers(jQueryWidget);
        });


        /******* Load UI CSS *******/
        var css_link = jQuery("<link>", {
            rel: "stylesheet",
            type: "text/css",
            href: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css"
        });
        css_link.appendTo('head');
    }

    function setHandlers($) {
        $(document).on('focus', '#start-date, #end-date', function(){

      $('#start-date').datepicker({
        dateFormat: "M dd, yy",
        minDate: 'D',
        numberOfMonths: 1,
      });

            $('#end-date').datepicker({
                dateFormat: "M dd, yy",
                minDate:'+1D',
                numberOfMonths:1,
            });
    }
})();

使用chrome调试器,我们可以看到,当调用getScript时,它正确地将jquery-ui添加到加载的版本中。 在我们调用noConflict它恢复之前的jQuery但版本不再具有jQueryUI之后。

在没有requireJS的情况下测试其他站点上的小部件是否正常工

以前有人遇到过这个吗? 不幸的是我们之前没有使用过RequireJS,但是无法理解为什么它会影响匿名函数。

任何帮助将非常感激。

问题是你要做的是不安全的。 结合起来有两个因素对你不利:

  1. 脚本以异步方式加载。 您唯一控制的是窗口小部件加载jQuery和jQueryUI的相对顺序。 但是,窗口小部件运行的页面也会加载自己的jQuery版本。 您的代码无法强制加载合作伙伴代码加载的脚本的顺序。

  2. jQuery不是一个表现良好的AMD模块。 一个表现良好的AMD模块调用define来获取它的依赖关系,它不会泄漏到全局空间。 不幸的是,jQuery确实将$jQuery泄漏到全球空间。

有了这两个因素结合在一起,你是根据其面临的竞争条件订购的jQuery的两个版本加载:这是一般不可能知道哪些jQuery的全局符号的版本$jQuery被引用。 考虑你的代码:

jQuery.getScript('https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js', function () {
  jQueryWidget = jQuery.noConflict(true);
  setHandlers(jQueryWidget);
});

您无法知道jQuery是指您要求加载的版本还是合作伙伴代码要加载的版本。 唯一.getScript保证是脚本加载回调将被调用,但它并不妨碍其他脚本加载脚本之间的.getScript负载和调用回调的时间。 浏览器完全可以自由加载您为.getScript提供的脚本,加载通过RequireJS请求的其他脚本,然后调用您的回调。

如果您希望您的窗口小部件可以放入页面而不必更改任何现有代码,那么就没有简单的修复方法。 您不仅可以更改问题中显示的逻辑,还可以将RequireJS添加到窗口小部件中。 RequireJS 本身无法修复此问题。 与其他答案所暗示的相反,RequireJS的context配置选项不是修复。 如果没有脚本尝试通过全局$jQuery访问jQuery,那将是一个修复,但是有几十个jQuery插件就是这样做的。 您无法确保合作伙伴代码不使用它们。

并提防似乎解决问题的建议修复程序。 你可以尝试一个修复,它似乎工作,你认为问题已经解决但实际上问题并没有表现出来,因为,这是一个竞争条件 一切都很好,直到一个月后,另一个合作伙伴加载你的小部件和繁荣:他们的页面创建了正确的条件,导致事情加载到一个搞砸你的代码的顺序。

还有一个额外的复杂功能,你可能还没有遇到,但必然会不时发生。 (再次,你正在处理竞争条件,所以......)你的代码是通过script元素加载jQuery和jQuery UI。 但是,它们都检查define是否可用,如果可用,它们将调用define 这可能会导致各种问题,具体取决于所有事情发生的顺序,但一个可能的问题是,如果在加载窗口小部件之前存在RequireJS,jQuery UI将从script元素调用define,这将导致匿名define不匹配错误 (jQuery有一个不同的问题,它更复杂,不值得进入。)

我可以看到让你的小部件加载而不受合作伙伴代码干扰而且不需要合作伙伴更改代码的唯一方法是使用类似Webpack的东西将你的代码构建到一个捆绑包中,其中应该强制define在你的包中是假的,这样就不会触发任何测试是否存在define代码。 (请参阅import-loader ,它可以用于此。)如果您将代码作为单个包加载,那么它可以以同步方式初始化自己,并且您可以确定$jQuery引用您的jQuery包含在你的包中。


如果您要遵循我的建议,这是一个很好的例子,它充分利用了Webpack,包括正确的缩小,并消除了代码中的一些工件,这些工具不再需要这种方法(例如IIFE,以及一些你有的功能)。 它可以通过保存文件在本地运行,运行:

  1. npm install webpack jquery jquery-ui imports-loader lite-server
  2. ./node_modules/.bin/webpack
  3. ./node_modules/.bin/lite-server

当我第一次写下我的解释时,我没有意识到这一点,但我现在注意到了。 当您使用Webpack包装代码时,没有必要调用noConflict ,因为当它被Webpack包装时,jQuery会检测带有DOM的CommonJS环境并在内部打开noGlobal标志,以防止泄漏到全局空间。

webpack.conf.js

const webpack = require('webpack');
module.exports = {
    entry: {
        main: "./widget.js",
        "main.min": "./widget.js",
    },
    module: {
        rules: [{
            test: /widget\.js$/,
            loader: "imports-loader?define=>false",
        }],
    },
    // Check the options for this and use what suits you best.
    devtool: "source-map",
    output: {
        path: __dirname + "/build",
        filename: "[name].js",
        sourceMapFilename: "[name].map.js",
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            include: /\.min\.js$/,
        }),
    ],
};

您的小部件为widget.js

var $ = require("jquery");
require("jquery-ui/ui/widgets/datepicker");

var css_link = $("<link>", {
    rel: "stylesheet",
    type: "text/css",
    href: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css"
});
css_link.appendTo("head");

$(document).ready(function() {
    console.log("jQuery compare (we want this false)", $ === window.$);
    console.log("jQuery version in widget",  $.fn.jquery);
    console.log("jQuery UI version in widget", $.ui.version);
    $("#end-date").datepicker();
});

index.html

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script>
    <script>
      require.config({
        paths: {
          jquery: "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min",
          "jquery-ui": "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui"
        }
      });
      require(["jquery", "jquery-ui"], function(myJQuery) {
        console.log("jQuery compare (we want this true)", myJQuery === $); 
        console.log("jQuery version main", $.fn.jquery);
        console.log("jQuery ui version main", $.ui.version);
      })
    </script>
  </head>
  <body>
    <input id="end-date">
    <script src="build/main.min.js"></script>

    <!-- The following also works: -->
    <!--
    <script>
      require(["build/main.min.js"]);
    </script>
    -->
  </body>
</html>

我认为问题在于jQueryWidget = jQuery.noConflict(true);

true表示从全局范围中删除所有jQuery变量。

jQuery.noConflict([removeAll])removeAll类型:Boolean一个布尔值,指示是否从全局范围中删除所有jQuery变量(包括jQuery本身)。

[noconflict] [1]

有一件事我们可以尝试删除真正的布尔参数,让我们知道它是否有帮助。

更新2: 以下方法不应要求任何合作伙伴代码更改

 <!DOCTYPE html> <html> <head> <title></title> <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script> <script type="text/javascript"> (function() { // Localize jQuery variable var jQueryWidget; /* * * * This is plugin's require config. Only used by plugin and will not affect partner config. * * */ var requireForPlugin = require.config({ context: "pluginversion", paths: { "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min", "jquery.ui.widget": "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min" } }); requireForPlugin(["require", "jquery", "jquery.ui.widget"], function() { /******** Load jQuery if not present *********/ if (window.jQuery === undefined || window.jQuery.fn.jquery !== '3.2.1') { scriptLoadHandler(); } else { loadJQueryUi(); } function scriptLoadHandler() { loadJQueryUi(); } function loadJQueryUi() { jQueryWidget = jQuery.noConflict(true); setHandlers(jQueryWidget); var css_link = jQueryWidget("<link>", { rel: "stylesheet", type: "text/css", href: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css" }); css_link.appendTo('head'); } function setHandlers($) { $('#end-date').on('click', function() { alert('JQUERY--' + $().jquery); alert('JQUERY UI--' + $.ui.version); $('#end-date').datepicker({ dateFormat: "M dd, yy", minDate: '+1D', numberOfMonths: 1, }); }); } }); })(); </script> <script> //SAMPLE PARTNER APPLICATION CODE: /* * * * This is partner's require config which uses diffrent version of jquery ui and jquery * * */ require.config({ paths: { "jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min", "jquery.ui.widget": "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui" } }); require(["jquery", "jquery.ui.widget"], function() { $('#btn').on('click', function() { alert('JQUERY--' + $().jquery); alert('JQUERY UI--' + $.ui.version); }); }) </script> </head> <body> <div><span>FOCUS</span> <input type="text" name="" id="end-date" /> </div> <button id="btn" style="width: 100px; height:50px; margin:10px">click me</button> </body> </html> 

我修改了插件代码,因此它使用自己的jquery和jquery ui版本(我在这里使用requirejs)。

另外,对于演示,我添加了一个示例合作伙伴脚本,它在按钮点击时发出警报 你可以看到,无需修改合作伙伴代码和requirejs配置,插件现在可以工作。

插件和合作伙伴代码都有独立的jquery和jquery ui版本。

希望这可以帮助。

参考资料使用带有Require.js的多个版本的jQueryhttp://requirejs.org/docs/api.html#multiversion

UPDATE3:使用webpack和import loader我们可以使用webpack捆绑插件js代码,在这种情况下插件将拥有自己的jquery版本,我们必须改变插件的构建方式。

使用npm安装webpack,jquery,import loader和jquery-ui并构建它,下面是示例代码:

main.js使用import loader来定义为false

 (function() {

     var $ = require('jquery');
     require('jquery-ui');
     require('jquery-ui/ui/widgets/datepicker.js');

     function scriptLoadHandler() {
         loadJQueryUi();
     }

     $(document).ready(function() {
         scriptLoadHandler();
     });

     function loadJQueryUi() {
         setHandlers();
         var css_link = $("<link>", {
             rel: "stylesheet",
             type: "text/css",
             href: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css"
         });
         css_link.appendTo('head');
     }

     function setHandlers() {
         $('#end-date').on('click', function() {
             alert('JQUERY--' + $().jquery);
             alert('JQUERY UI--' + $.ui.version);
             $('#end-date').datepicker({
                 dateFormat: "M dd, yy",
                 minDate: '+1D',
                 numberOfMonths: 1,
             });
         });
     }
 })();

app.js包含插件代码,并添加了所需的依赖项

  var webpack = require('webpack');

  module.exports = {
  entry: "./main.js",
  output: {
  filename: "main.min.js"
     }
  };

webpack.config.js

<!DOCTYPE html>
<html>

<head>
    <title></title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script> 
    <script src="main.min.js"></script> 
    <script>
    //SAMPLE PARTNER APPLICATION CODE:

    /*
    *
    *
    *
        This is partner's require config
        which uses diffrent version of jquery ui and
        jquery
    *
    *
    */
    require.config({
        paths: {
            "jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min",

            "jquery.ui.widget": "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui"

        }
    });
    require(["jquery", "jquery.ui.widget"], function() {
        $('#btn').on('click', function() {
            alert('JQUERY--' + $().jquery);
            alert('JQUERY UI--' + $.ui.version);
        });
    })
    </script>
</head>

<body>
    <div><span>FOCUS</span>
        <input type="text" name="" id="end-date" />
    </div>
    <button id="btn" style="width: 100px;"></button>
</body>

</html>

sample.html

 <!DOCTYPE html> <html> <head> <title></title> <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script> <script src="main.min.js"></script> <script> //SAMPLE PARTNER APPLICATION CODE: /* * * * This is partner's require config which uses diffrent version of jquery ui and jquery * * */ require.config({ paths: { "jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min", "jquery.ui.widget": "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui" } }); require(["jquery", "jquery.ui.widget"], function() { $('#btn').on('click', function() { alert('JQUERY--' + $().jquery); alert('JQUERY UI--' + $.ui.version); }); }) </script> </head> <body> <div><span>FOCUS</span> <input type="text" name="" id="end-date" /> </div> <button id="btn" style="width: 100px;"></button> </body> </html> 

在做一个webpack之后,它将生成一个main.min.js文件,该文件包含在sample.html文件中

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM