When does a dynamically loaded JavaScript library become available?
我编写了javascript库来使用filesaver.js及其相关库。但是,我不希望每次有人想使用我的库时都加载filesaver.js。我不想强迫他们用
相反,我更喜欢这样的东西。当他们调用我的
1 2 3 4 5 6 7 8 9 10 | 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 } |
问题是,在上述之后,仍然没有定义
整体解决方案是使用模块系统。AMD可能是最常用的浏览器异步代码加载系统。AMD只是一个规范,但是像require.js之类的东西是使用AMD模块的非常流行的工具。
您可以定义模块之间的依赖关系,如果需要的话,require.js将获取它们。一般的想法是模仿其他语言的导入/命名空间功能(如Java、C语言或Python)。代码共享"我想是这个词吗?
只需将所有代码放入一个回调函数中,该函数在加载依赖项后运行,这样就可以确保存在所需的对象和方法。
更新2015
只是一个附录。虽然上面的信息仍然正确,但是前端代码管理正在迅速向Webpack和Browserify等解决方案发展,它们捆绑并连接任何模块类型的代码,并且都具有动态代码加载功能(Webpack称之为代码拆分)。再加上依赖管理的NPM指数增长,AMD的相关性开始降低。
好的,您需要做的是监听脚本以完成加载。不幸的是,对于IE<7的代码有一些错误。
这是moootools
1 2 3 4 5 6 7 8 9 10 11 12 | 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); } |
现在在
1 2 3 4 5 6 7 8 9 10 11 | function createImage(image, name) { function createImg() { // now do the saveImage code } if (typeof(saveAs) !== 'function') { loadScript("FileSaver.js", {onLoad: createImg});//load library } else { createImg(); } } |
应该适用于大多数浏览器。
最简单的答案是将代码放在您创建的
1 2 3 4 5 6 7 8 | 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); |
以这种方式动态加载脚本是由Facebook完成的。
所以我同意AMD的评论(不能将代码阻塞放入评论MEH…)
以下是我为filesaver.js所做的工作
首先在我的RequireJS config/main.js中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | (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放在正确的位置
我还为IE10之前的版本创建了一个IEShim
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 ); } ... }; |
使用head.js:http://headjs.com/
它将按需加载脚本。