【问题标题】:When does a dynamically loaded JavaScript library become available?动态加载的 JavaScript 库何时可用?
【发布时间】:2013-11-14 15:00:44
【问题描述】:

我编写了 JavaScript 库以使用 FileSaver.js 及其相关库。但是,当有人想使用我的库时,我不想总是加载 FileSaver.js。而且我不想强迫他们自己加载带有script标签的所有各种与FileSaver相关的JavaScript库(或者甚至加载我的一个这样做的)。

相反,我更喜欢这样的东西。当他们调用我的createImage 函数时,它首先执行以下操作:

function createImage(image, name) {
  if (typeof(saveAs) !== 'function') {
    var element = document.createElement('script');
    element.async = false;
    element.src = 'FileSaver.js';
    element.type = 'text/javascript';
    (document.getElementsByTagName('head')[0]||document.body).appendChild(element);
  }
  // now do the saveImage code
}

问题是,经过上述操作,saveAs 函数仍未定义。只有我的createImage 完成之后,saveAs 函数才最终定义。

【问题讨论】:

标签: javascript javascript-framework


【解决方案1】:

整体解决方案是使用模块系统。 AMD 可能是最常用的浏览器异步代码加载系统。 AMD 只是一个规范,但像 require.js 这样的东西是使用 AMD 模块的非常流行的工具。

这个想法是你可以定义你的模块之间的依赖关系,如果需要,require.js 会去获取它们。总体思路是模仿其他语言(如 java、C# 或 python)的导入/命名空间功能。我认为是“代码共享”这个词?

只需将所有代码放在一个回调函数中,该函数会在加载依赖项后运行,因此您可以确定所需的对象和方法存在。

2015 年更新

只是一个附录。虽然上面的信息仍然正确,但前端代码管理正在迅速转向像 Webpack 和 Browserify 这样的解决方案,它们捆绑和连接任何模块类型的代码,并且都具有动态代码加载功能(webpack 称之为代码拆分)。再加上用于依赖管理的 npm 呈指数增长,AMD 开始变得不那么重要了。

【讨论】:

    【解决方案2】:

    好的,您需要做的是监听脚本完成加载。不幸的是,对于 ie,此代码存在一些错误。

    这是 Mootools Asset.javascript 加载脚本并在完成时调用回调的方式:

    var loadScript = function (source, properties) {
        properties || (properties = {});
        var script = document.createElement('script');
        script.async = true;
        script.src = source;
        script.type = 'text/javascript';
        var doc = properties.document || document, load = properties.onload || properties.onLoad;
        return delete properties.onload, delete properties.onLoad, delete properties.document, 
        load && (script.addEventListener ? script.addEventListener("load", load) : script.attachEvent("readystatechange", function() {
            [ "loaded", "complete" ].indexOf(this.readyState) >= 0 && load.call(this);
        })), script.set(properties).appendChild(doc.head);
    }
    

    现在在loadImage你可以按如下方式加载文件库:

    function createImage(image, name) {
      function createImg() {
          // now do the saveImage code
      }
      if (typeof(saveAs) !== 'function') {
         loadScript("FileSaver.js", {onLoad: createImg});//load library
      }
      else {
         createImg();
      }
    }
    

    应该适用于大多数浏览器。

    【讨论】:

      【解决方案3】:

      使用 Head.js:http://headjs.com/

      它将按需加载脚本。

      【讨论】:

        【解决方案4】:

        所以我同意 AMD 的评论(can't put code blocking into cmets meh...)

        这就是我为 FileSaver.js 做的事情

        首先在我的 requirejs 配置/main.js 中:

        (function() {
            // REMEMBER TO DUPLICATE CHANGES IN GRUNTFILE.JS
            requirejs.config({
                paths: {
                    "jquery": "PATH/jquery.min", // NO .js
                    "lib.filesaver" : "PATH/FileSaver", // NO .js
                    "shim.blob" : "PATH/Blob" // NO .js
                },
                shim: {
                    "lib.filesaver": {deps: ["shim.blob"]}
                }
            });
        
            define([
                "jquery"
            ], function(
                $
                ) {
                    $(document).ready(function() {
                        // start up code...
                    });
                return {};
                });
        })();
        

        然后我将 Blob.js/jquery 和 Filersaver 放在正确的位置

        我还为 pre IE10 创建了一个 IEShim

        define([], function () {
            /**
             * @class IEshims
             * container for static IE shim functions
             */
            var IEShims = {
                /**
                 * saveFile, pops up a built in javascript file as a download
                 * @param {String} filename, eg doc.csv
                 * @param {String} filecontent eg "this","is","csv"
                 */
                saveAs: function (filename, filecontent, mimetype ) {
                    var w = window.open();
                    var doc = w.document;
                    doc.open( mimetype,'replace');
                    doc.charset = "utf-8";
                    doc.write(filecontent);
                    doc.close();
                    doc.execCommand("SaveAs", null, filename);
                }
            };
            return IEShims;
        });
        

        最后,当我想使用 Filesaver 时,将其设为必需(与 IEShim 一起用于不良浏览器)

        define([
        "lib.filesaver",
        "IEShims"
        ],
        function (
            FileSaver, // it's empty, see saveAs global var
            IEShims
            ) {
        ...
            var fileName = "helloworld.txt";
            var fileContents = "Me haz file contents, K Thx Bye";
            var mimeType = "text/plain";
            if(saveAs) {
                var blob = new Blob(
                    [fileContents],
                    {type: mimeType + ";charset=" + document.characterSet}
                );
                saveAs(blob, fileName);
            } else {
                IEShims.saveAs(fileName, fileContents,mimeType );
            }
            ...
        };
        

        【讨论】:

          【解决方案5】:

          最简单的答案是将您的代码放在您创建的script 标记的onload 处理程序中:

          <script>
            var firstScript = document.getElementsByTagName('script')[0],
                js = document.createElement('script');
            js.src = 'https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js';
            js.onload = function () {
              // do stuff with your dynamically loaded script
              snowStorm.snowColor = '#99ccff';
            };
            firstScript.parentNode.insertBefore(js, firstScript);
          </script>

          以这种方式动态加载脚本是done by Facebook

          【讨论】:

            猜你喜欢
            • 2018-04-02
            • 1970-01-01
            • 1970-01-01
            • 2010-11-23
            • 2011-08-15
            • 2011-12-18
            • 1970-01-01
            • 2011-10-10
            • 2012-12-01
            相关资源
            最近更新 更多