如何在另一个JavaScript文件中包含一个JavaScript文件?


5192

JavaScript中是否有类似@importCSS的内容,可让您在另一个JavaScript文件中包含一个JavaScript文件?



82
@Daniel,我不想使用AJAX调用。
Alec Smart 2009年

13
为什么不使用简单的有序script标签就先声明导入的文件呢?
falsarella 2015年

6
@Claudiu这将无助于导入任何东西,但它也应该可以正常工作。如果您有一个依赖于另一个JS文件的JS文件,则只需先声明依赖文件的脚本标签,这样以后就已经加载了其依赖文件。如果遇到无法解决的情况,则此处的答案应该会有所帮助。
falsarella '16

1
这样做的实际好处是什么?无论哪种方式,依赖javascript文件的代码库都不会加载,并且无论如何都不会加载!
Ciasto piekarz

Answers:


4469

JavaScript的旧版本没有导入,包含或要求,因此已经开发了许多解决此问题的方法。

但是自2015年(ES6)起,JavaScript就拥有了ES6模块标准,可以在Node.js中导入模块,大多数现代浏览器也支持该模块。

为了与旧浏览器的兼容性,这样的构建工具的WebPack汇总和/或transpilation工具,如巴贝尔都可以使用。

ES6模块

ECMAScript(ES6)模块已经 从v8.5开始,带有--experimental-modules标志的Node.js支持,并且至少从不带有标志的Node.js v13.8.0支持。要启用“ ESM”(相对于Node.js以前的CommonJS样式的模块系统[“ CJS”]),您可以"type": "module"在中使用package.json或为其赋予扩展名.mjs。(类似地,.cjs如果您的默认值为ESM,则可以命名用Node.js的先前CJS模块编写的模块。)

使用package.json

{
    "type": "module"
}

然后module.js

export function hello() {
  return "Hello";
}

然后main.js

import { hello } from './module.js';
let val = hello();  // val is "Hello";

使用 .mjs,您将拥有module.mjs

export function hello() {
  return "Hello";
}

然后 main.mjs

import { hello } from './module.mjs';
let val = hello();  // val is "Hello";

浏览器中的ECMAScript模块

Safari 10.1,Chrome 61,Firefox 60和Edge 16开始,浏览器已经支持直接加载ECMAScript模块(不需要像Webpack这样的工具)。请在caniuse上查看当前支持。无需使用Node.js .mjs扩展名;浏览器完全忽略模块/脚本上的文件扩展名。

<script type="module">
  import { hello } from './hello.mjs'; // Or it could be simply `hello.js`
  hello('world');
</script>
// hello.mjs -- or it could be simply `hello.js`
export function hello(text) {
  const div = document.createElement('div');
  div.textContent = `Hello ${text}`;
  document.body.appendChild(div);
}

阅读更多 https://jakearchibald.com/2017/es-modules-in-browsers/

浏览器中的动态导入

动态导入使脚本可以根据需要加载其他脚本:

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });
</script>

阅读更多 https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js要求

module.exports/require系统是仍在Node.js中广泛使用的较早的CJS模块样式。

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"   

JavaScript还有其他方法可以在不需要预处理的浏览器中包含外部JavaScript内容。

AJAX加载

您可以使用AJAX调用加载其他脚本,然后用于eval运行它。这是最直接的方法,但是由于JavaScript沙箱安全模型,它仅限于您的域。使用eval还打开了漏洞,黑客和安全问题的大门。

获取加载

像动态导入一样,您可以fetch使用Promise通过Fetch Inject库使用诺言来控制脚本依赖项的执行顺序,从而通过调用加载一个或多个脚本:

fetchInject([
  'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'
]).then(() => {
  console.log(`Finish in less than ${moment().endOf('year').fromNow(true)}`)
})

jQuery加载

jQuery的库提供加载功能在同一行

$.getScript("my_lovely_script.js", function() {
   alert("Script loaded but not necessarily executed.");
});

动态脚本加载

您可以将带有脚本URL的脚本标签添加到HTML中。为了避免jQuery的开销,这是一个理想的解决方案。

该脚本甚至可以驻留在其他服务器上。此外,浏览器会评估代码。该<script>标签可以被注入或者网页<head>,或者只是收盘前插入</body>标签。

这是一个如何工作的示例:

function dynamicallyLoadScript(url) {
    var script = document.createElement("script");  // create a script DOM node
    script.src = url;  // set its src to the provided URL

    document.head.appendChild(script);  // add it to the end of the head section of the page (could change 'head' to 'body' to add it to the end of the body section instead)
}

此功能会将新<script>标签添加到页面标题的末尾,src属性设置为URL,该URL作为第一个参数提供给该函数。

JavaScript Madness:动态脚本加载》中讨论和说明了这两种解决方案。

检测何时执行脚本

现在,您必须知道一个大问题。这样做意味着您远程加载代码。现代的Web浏览器将加载文件并继续执行当前脚本,因为它们异步加载所有内容以提高性能。(这适用于jQuery方法和手动动态脚本加载方法。)

这意味着,如果您直接使用这些技巧,则在您要求加载新代码之后将无法在下一行使用新加载的代码,因为它将继续加载。

例如:my_lovely_script.jscontains MySuperObject

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

然后,您重新加载命中的页面F5。而且有效!令人困惑...

那么该怎么办呢?

好吧,您可以使用作者在我给您的链接中建议的技巧。总而言之,对于有急事的人,他在加载脚本时使用事件来运行回调函数。因此,您可以将所有使用远程库的代码放入回调函数中。例如:

function loadScript(url, callback)
{
    // Adding the script tag to the head as suggested before
    var head = document.head;
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    // Then bind the event to the callback function.
    // There are several events for cross browser compatibility.
    script.onreadystatechange = callback;
    script.onload = callback;

    // Fire the loading
    head.appendChild(script);
}

然后,在脚本加载到lambda函数中之后,编写要使用的代码:

var myPrettyCode = function() {
   // Here, do whatever you want
};

然后运行所有这些:

loadScript("my_lovely_script.js", myPrettyCode);

请注意,脚本可能在加载DOM之后或之前执行,具体取决于浏览器以及是否包含line script.async = false;总的来说,有一篇很棒的关于Javascript加载的文章对此进行了讨论。

源代码合并/预处理

如该答案的顶部所述,许多开发人员在其项目中使用了诸如Parcel,Webpack或Babel之类的构建/编译工具,从而允许他们使用即将到来的JavaScript语法,为较旧的浏览器提供向后兼容性,合并文件,最小化,执行代码拆分等


130
不,但是有人使用像Rhino一样先进的东西,否则不会问这个问题。
e-satis 2010年

192
只是为了完整起见,还有第三种方法:在某些解决方案中,当您控制两个javascript文件时,您只能制作一个结合了两个文件内容的巨型javascript文件。
Toad 2012年

20
不应该“ document.createElement(“ my_lovely_script.js”);“ 在示例中为“ document.createElement(” script“)”?
拉塞尔·席尔瓦

14
如果您正在执行的是代码,那么eval如何为黑客打开大门?
文斯Panuccio

6
您的答案需要对IE进行一些修改(onreadystatechange事件和readyState属性)。而且,动态脚本加载不会从浏览器的预加载扫描程序中受益。推荐此HTML5Rocks文章:html5rocks.com/en/tutorials/speed/script-loading
ruhong 2015年

570

如果有人在寻找更高级的东西,请尝试RequireJS。您将获得更多好处,例如依赖管理,更好的并发性,并避免重复(也就是说,多次检索脚本)。

您可以在“模块”中编写JavaScript文件,然后在其他脚本中将它们作为依赖项引用。或者,您可以将RequireJS用作简单的“获取此脚本”解决方案。

例:

将依赖项定义为模块:

some-dependency.js

define(['lib/dependency1', 'lib/dependency2'], function (d1, d2) {

     //Your actual script goes here.   
     //The dependent scripts will be fetched if necessary.

     return libraryObject;  //For example, jQuery object
});

Implementation.js是您的“主要” JavaScript文件,该文件取决于some-dependency.js

require(['some-dependency'], function(dependency) {

    //Your script goes here
    //some-dependency.js is fetched.   
    //Then your script is executed
});

摘自GitHub自述文件:

RequireJS加载普通的JavaScript文件以及更多定义的模块。它针对浏览器内使用进行了优化,包括在Web Worker中使用,但可以在其他JavaScript环境(例如Rhino和Node)中使用。它实现了异步模块API。

RequireJS使用纯脚本标记来加载模块/文件,因此应便于调试。它可以简单地用于加载现有的JavaScript文件,因此您可以将其添加到现有项目中,而无需重新编写JavaScript文件。

...


1
@aaaidan:MattDmo的原因加上它依赖于外部库,而外部库则依赖于可接受的答案。
David Mulder 2014年

1
为了克服require.js,最新的版本是angular js,它更稳定,更易于与其他绑定和丰富的HTML功能一起使用。
zeeshan 2014年

5
-1:那些抽象-“ some_dependency”-确实很差,索引更加混乱。我很难理解工作代码示例的外观。如果作者提供了工作示例,几乎任何人都可以根据自己的需求进行定制和概括。
Tegiri Nenashi 2014年

1
不适用于带有自动缩小功能的MVC和脚本捆绑
Triynko 2015年

1
“添加..无需重新编写JavaScript文件”;很酷,但是究竟如何呢?姜黄素。答案建议在主脚本中添加这样的“ require”(不是“ requirejs”?),并在每个相关文件中添加“ define”,是吗?但是,这是重写,那么什么不重写?代码的其余部分,因为RequireJS可以让ea文件像正常一样进行全局定义?-可以?我只是尝试过,+(忘记了?)<script src =“ requirejs.org/docs/release/2.2.0/comments/require.js ”> </ script>,然后是'requirejs([' GoogleDrive.com/host /..',..],function(a,b) {} mycode的)':萤火虫说,每依赖加载,但他们的DEFS不mycode的与后确定。
Destiny Architect

195

实际上,一种方法可以异步加载JavaScript文件,因此您可以在加载后立即使用新加载的文件中包含的功能,并且我认为它可以在所有浏览器中使用。

您需要jQuery.append()<head>页面的元素上使用,即:

$("head").append('<script type="text/javascript" src="' + script + '"></script>');

但是,此方法也有一个问题:如果在导入的JavaScript文件中发生错误,则Firebug(以及Firefox错误控制台和Chrome开发者工具)也会错误地报告其位置,如果您使用Firebug进行跟踪,这将是一个大问题JavaScript错误很多(我愿意)。Firebug出于某种原因根本不了解新加载的文件,因此,如果该文件中发生错误,它将报告该错误是在您的主HTML文件中发生的,您将很难找到导致该错误的真正原因。

但是,如果这对您来说不是问题,则此方法应该有效。

我实际上已经编写了一个使用此方法的名为$ .import_js()的jQuery插件:

(function($)
{
    /*
     * $.import_js() helper (for JavaScript importing within JavaScript code).
     */
    var import_js_imported = [];

    $.extend(true,
    {
        import_js : function(script)
        {
            var found = false;
            for (var i = 0; i < import_js_imported.length; i++)
                if (import_js_imported[i] == script) {
                    found = true;
                    break;
                }

            if (found == false) {
                $("head").append('<script type="text/javascript" src="' + script + '"></script>');
                import_js_imported.push(script);
            }
        }
    });

})(jQuery);

因此,导入JavaScript所需要做的就是:

$.import_js('/path_to_project/scripts/somefunctions.js');

我还在示例中对此进行了简单测试

main.js在主HTML中包含一个文件,然后在脚本中main.js用于$.import_js()导入一个名为的附加文件included.js,该文件定义了此功能:

function hello()
{
    alert("Hello world!");
}

在include之后included.js,立即hello()调用该函数,您会收到警报。

(此答案是对e-satis的评论的回应)。


我正在尝试此方法,但对我不起作用,该元素只是未出现在head标签中。
网站

16
@juanpastas-use jQuery.getScript,这样您就不必担心编写插件了……
MattDMo 2013年

6
嗯,根据本文的介绍,除非将script元素专门设置为head,否则将其添加到会导致该元素异步运行。asyncfalse
Flimm 2015年

1
脚本变量不应该编码html实体吗?如果链接包含",则代码将中断
RedClover

1
@Flimm亲爱的先生,我和我的团队感谢您的评论,如果我们亲自见面,我个人欠您的钱甚至不算啤酒
Alex

155

我认为更简洁的另一种方法是发出同步Ajax请求,而不使用<script>标签。Node.js处理的内容也包括在内。

这是使用jQuery的示例:

function require(script) {
    $.ajax({
        url: script,
        dataType: "script",
        async: false,           // <-- This is the key
        success: function () {
            // all good...
        },
        error: function () {
            throw new Error("Could not load script " + script);
        }
    });
}

然后,您可以像通常使用include那样在代码中使用它:

require("/scripts/subscript.js");

并能够在下一行中从所需脚本中调用函数:

subscript.doSomethingCool(); 

1
好的解决方案,不幸的是头包含异步,ajax解决方案有效。
Matteo Conta,

17
就像其他人提到的那样,requirejs.org可以做到这一点,并且还具有一个预编译器,可以将js文件放在一起,以便更快地加载它们。您可能需要检查一下。
Ariel 2012年

2
发现我可以通过在Chrome文件的底部添加此指令来调试它:// @ sourceURL = view_index.js
Todd Vance

11
不幸的是,jQuery现在不推荐使用async:false。将来可能会中断,所以我会避免。
sqram

3
@katsh我们在这里不使用jqXHR对象。您的报价似乎没有备份您先前的评论,认为该评论async: false已被弃用。它不是!如您的报价所述,只有与jqXHR相关的东西才是。
Zero3

101

有个好消息对你来说。很快,您将能够轻松加载JavaScript代码。这将成为导入JavaScript代码模块的标准方法,并将成为核心JavaScript本身的一部分。

您只需要编写import cond from 'cond.js';以加载cond文件中命名的宏即可cond.js

因此,您不必依赖任何JavaScript框架,也不必显式进行Ajax调用。

参考:


12
jsfile上的require / import太长了。(IMO)。
rwheadon

6
@rwheadon是的,这似乎不是语言的一部分,真是令人震惊!js人们如何完成任何事情超出了我!林新的给它,这似乎疯狂的最差的(许多)位
JonnyRaa

@ jonny-leeds即使没有内置模块加载,浏览器中的JavaScript也足够灵活,我们可以为模块管理实现像RequireJS这样的库。
敏锐2014年

8
2015年中-尚未在任何浏览器中实现,developer.mozilla.org
zh

是的,没错。现在,我对此感到不爽:(。Thouh,一旦在所有浏览器中实现,这将是javascript中内置的一项强大功能
。– Imdad

96

可以动态生成JavaScript标记并将其从其他JavaScript代码内部附加到HTML文档。这将加载目标JavaScript文件。

function includeJs(jsFilePath) {
    var js = document.createElement("script");

    js.type = "text/javascript";
    js.src = jsFilePath;

    document.body.appendChild(js);
}

includeJs("/path/to/some/file.js");

8
@ e-satis-实际上,这是一个优势,同步脚本将被阻止。赛马,但每10场中有9场您希望使用非阻塞选项。
annakata 2009年

@Svitlana-这样创建的脚本元素是异步的。目前,这可能被视为利用漏洞,因此它可能不会成为未来的证明,在任何标准中我都没有看到任何可以澄清这一点的东西。
annakata 2009年

实际上,我把它收回了,我已经注意到您在附加身体而不是头部。
annakata 2009年

@annakata:有什么区别?顺便说一句,雅虎建议在正文末尾添加脚本标签,因此在动态附加时应该没有问题。
Svitlana Maksymchuk,2009年

7
@ e-satis异步是好的,因为它不会冻结您的页面。使用回调功能在完成时收到通知js.onload = callback;
Vitim.us 2013年

74

语句import在ECMAScript 6中。

句法

import name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import name , { member [ , [...] ] } from "module-name";
import "module-name" as name;

...,但根据您链接到的页面上的兼容性表,迄今为止,任何浏览器均不支持。
Zero3

6
您现在可以编写ES6代码,并使用Babel.js(babeljs.io)将其编译为您当前首选的模块系统(CommonJS / AMD / UMD):babeljs.io/docs/usage/modules
Jeremy Harris

@ Zero3显然,新的IE(Edge)是唯一的IE
朱利安·阿瓦尔

在2019年,IE仍不支持任何形式的此功能。为什么Microsoft必须如此反对可用性?
GreySage

61

也许您可以使用在此页面上找到的此功能,如何在JavaScript文件中包含JavaScript文件?

function include(filename)
{
    var head = document.getElementsByTagName('head')[0];

    var script = document.createElement('script');
    script.src = filename;
    script.type = 'text/javascript';

    head.appendChild(script)
}

5
应该有用添加script.onload = callback;
Vitim.us 2013年

@SvitlanaMaksymchuk所以,如果我不使用var,该变量将是全局变量?
弗朗西斯科·科拉莱斯·莫拉莱斯2014年

@FranciscoCorrales是的。
Christopher Chiche 2014年

无论是否使用var,它最终都将在全局范围内出现:)
Vedran Maricevic。


54

这是没有jQuery同步版本:

function myRequire( url ) {
    var ajax = new XMLHttpRequest();
    ajax.open( 'GET', url, false ); // <-- the 'false' makes it synchronous
    ajax.onreadystatechange = function () {
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4) {
            switch( ajax.status) {
                case 200:
                    eval.apply( window, [script] );
                    console.log("script loaded: ", url);
                    break;
                default:
                    console.log("ERROR: script not loaded: ", url);
            }
        }
    };
    ajax.send(null);
}

请注意,要获得此有效的跨域服务,服务器将需要allow-origin在响应中设置标头。


功能出色!在正文之后编写任何其他JS之前先加载JavaScript。加载多个脚本时非常重要。
tfont

3
@heinob:如何使它适用于跨域?(从加载脚本http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
2014年

@ user2284570:如果您是外部域的所有者,请在服务器答案中设置“ allow-origin”标头。如果您不是所有者:什么都不是。抱歉! 那就是跨地区政策。
heinob 2014年

2
@ user2284570:我以这种方式理解您的评论,因为您不是要从中加载脚本的域的所有者。在这种情况下,您只能通过插入的<script>标签而不是通过加载脚本XMLHttpRequest
heinob 2014年

2
对于计划在Firefox中使用此代码的用户(例如imacros脚本编写),请将此行添加到文件顶部:const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
Kwestion

49

我刚刚编写了以下JavaScript代码(使用Prototype进行DOM操作):

var require = (function() {
    var _required = {};
    return (function(url, callback) {
        if (typeof url == 'object') {
            // We've (hopefully) got an array: time to chain!
            if (url.length > 1) {
                // Load the nth file as soon as everything up to the
                // n-1th one is done.
                require(url.slice(0, url.length - 1), function() {
                    require(url[url.length - 1], callback);
                });
            } else if (url.length == 1) {
                require(url[0], callback);
            }
            return;
        }
        if (typeof _required[url] == 'undefined') {
            // Haven't loaded this URL yet; gogogo!
            _required[url] = [];

            var script = new Element('script', {
                src: url,
                type: 'text/javascript'
            });
            script.observe('load', function() {
                console.log("script " + url + " loaded.");
                _required[url].each(function(cb) {
                    cb.call(); // TODO: does this execute in the right context?
                });
                _required[url] = true;
            });

            $$('head')[0].insert(script);
        } else if (typeof _required[url] == 'boolean') {
            // We already loaded the thing, so go ahead.
            if (callback) {
                callback.call();
            }
            return;
        }

        if (callback) {
            _required[url].push(callback);
        }
    });
})();

用法:

<script src="prototype.js"></script>
<script src="require.js"></script>
<script>
    require(['foo.js','bar.js'], function () {
        /* Use foo.js and bar.js here */
    });
</script>

要点:http : //gist.github.com/284442


7
jrburke将其写为RequireJS。GitHub:requirejs.org/docs/requirements.html
Mike Caron

这不是将加载的脚本放在调用require()的作用域之外吗?似乎eval()是在范围内执行此操作的唯一方法。还是有另一种方法?
trusktr

44

这是Facebook如何针对其无处不在的“赞”按钮执行此操作的通用版本:

<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>

如果它适用于Facebook,它将为您服务。

我们之所以选择第一个script元素而不是headbody的原因,是因为某些浏览器在缺少时不会创建一个script元素,但是我们保证有一个元素-这个元素。请访问http://www.jspatterns.com/the-ridiculous-case-of-adding-a-script-element/了解更多信息


3
真好!这里的某些方法也可以使用,但是在动态设置下效果最佳。
tfont

我伸出你的脚本,以消除dublicate
卡米尔·东布罗夫斯基

39

如果要使用纯JavaScript,可以使用document.write

document.write('<script src="myscript.js" type="text/javascript"></script>');

如果使用jQuery库,则可以使用$.getScript方法。

$.getScript("another_script.js");

8
document.write不会删除其他所有内容吗?
伊萨·阿迪尔

30

您也可以使用PHP汇编脚本:

档案main.js.php

<?php
    header('Content-type:text/javascript; charset=utf-8');
    include_once("foo.js.php");
    include_once("bar.js.php");
?>

// Main JavaScript code goes here

16
听起来关键是将这一切保留在前端的javascript中
Ariel

1
感谢您的提醒。您还可以在HTML标头中用PHP编写<script>标记,以便将加载所需的js文件(只有那些)。
罗尔夫(Rolf)2013年

29

此处显示的大多数解决方案都暗含动态载荷。我搜索的是一个将所有依赖文件组合成单个输出文件的编译器。与Less / Sass预处理器处理CSS @import规则相同。由于找不到类似的东西,因此我编写了一个简单的工具来解决此问题。

这就是编译器https://github.com/dsheiko/jsic,它可以$import("file-path")安全地替换为请求的文件内容。这是相应的Grunt插件:https : //github.com/dsheiko/grunt-jsic

在jQuery master分支上,它们仅将原子源文件连接成一个以开头intro.js和以结束的单个源文件outtro.js。那不适合我,因为它在源代码设计上没有灵活性。看看jsic的工作原理:

src / main.js

var foo = $import("./Form/Input/Tel");

src /表格/输入/Tel.js

function() {
    return {
          prop: "",
          method: function(){}
    }
}

现在我们可以运行编译器:

node jsic.js src/main.js build/mail.js

并获得合并的文件

build / main.js

var foo = function() {
    return {
          prop: "",
          method: function(){}
    }
};

2
自从这篇文章以来,我想出了一个更好的解决方案-CommonJS模块编译器-github.com/dsheiko/cjsc因此,您可以简单地编写CommonJs或NodeJs模块并互相访问,而又将它们保持在孤立的范围内。好处:不需要多个影响性能的HTTP请求您不需要手动包装模块代码-这是编译器的责任(因此,源代码具有更好的可读性)您不需要任何外部库它与UMD-兼容和NodeJs模块(例如,您可以将jQuery,Backbone作为模块寻址而不用触摸其代码)
Dmitry Sheiko 2014年

26

如果要加载JavaScript文件的目的是使用导入/包含的文件中的函数,则还可以定义一个全局对象并将这些函数设置为对象项。例如:

global.js

A = {};

file1.js

A.func1 = function() {
  console.log("func1");
}

file2.js

A.func2 = function() {
  console.log("func2");
}

main.js

A.func1();
A.func2();

在HTML文件中包含脚本时,只需要小心即可。顺序应如下所示:

<head>
  <script type="text/javascript" src="global.js"></script>
  <script type="text/javascript" src="file1.js"></script>
  <script type="text/javascript" src="file2.js"></script>
  <script type="text/javascript" src="main.js"></script>
</head>

22

应该这样做:

xhr = new XMLHttpRequest();
xhr.open("GET", "/soap/ajax/11.0/connection.js", false);
xhr.send();
eval(xhr.responseText);

5
eval是怎么回事。在Crockford中,“ eval是邪恶的。该eval函数是JavaScript最易滥用的功能。请避免使用它。它eval具有别名。不要使用Function构造函数。不要将字符串传递给setTimeoutor setInterval。” 如果您还没有阅读过他的“ JavaScript:优秀零件”,请立即进行操作。你不会后悔。
MattDMo

13
@MattDMo“有人说这很糟糕”实际上不是一个论点。
Casey 2014年

4
@emodendroket我认为您不知道谁是道格拉斯·克罗克福德。
MattDMo 2014年

13
@MattDMo我完全知道他是谁,但他是人,不是神。
Casey 2014年

2
@tggagne:如何使它适用于跨域?(从加载脚本http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
2014年


18

我有一个简单的问题,但对这个问题的回答感到困惑。

我必须使用在另一个JavaScript文件(main.js)中的一个JavaScript文件(myvariables.js)中定义的变量(myVar1)。

为此,我这样做如下:

以正确的顺序将JavaScript代码加载到HTML文件中,首先是myvariables.js,然后是main.js:

<html>
    <body onload="bodyReady();" >

        <script src="myvariables.js" > </script>
        <script src="main.js" > </script>

        <!-- Some other code -->
    </body>
</html>

文件:myvariables.js

var myVar1 = "I am variable from myvariables.js";

档案:main.js

// ...
function bodyReady() {
    // ...
    alert (myVar1);    // This shows "I am variable from myvariables.js", which I needed
    // ...
}
// ...

如您所见,我曾在另一个JavaScript文件的一个JavaScript文件中使用过一个变量,但是我不需要在另一个JavaScript文件中包含一个变量。我只需要确保可以在第二个JavaScript文件中自动访问在第二个JavaScript文件之前加载的第一个JavaScript文件以及第一个JavaScript文件的变量。

这挽救了我的一天。我希望这有帮助。


这个答案的问题在于它不是类似的东西import。您需要一个HTML文件才能将内容从一个js文件转移到另一个。
杀人剂

非常真实 那就是其他一些答案将其作为解决方案的原因。但是我有两个JS文件,一个应该使用其他JS的变量。没有node.js,next.js,express.js等,因此import OR require将不起作用,仅具有简单的.js文件。
Manohar Reddy Poreddy

但是,可以理解的是,问题实际上是要求在另一个文件本身中导入一个javascript文件,而不仅仅是使用HTML文件访问其变量。例如,假设您正在为按钮创建功能,并且使用了三个不同的js文件,一个用于单击处理,一个用于悬停处理,一个用于其他两个回调。在第三个js本身中导入另外两个js,并且只有一个<script>标签会很有帮助。这可以帮助组织。这个答案根本不是问题所要的,在这种情况下也不理想。
Cannicide

当我寻找问题的解决方案时,我以正确的方式进行搜索,但是Google认为这是正确的页面,因此我将其放在了这里。这就是为什么我在这里写下答案的原因。由于上述15项投票,看来我做对了。为了不浪费其他人的时间,我已经清楚地说明了我的情况-首先放置HTML代码-看起来很有帮助,因为您回答并说html不是您的情况。希望能弄清楚。
Manohar Reddy Poreddy

是的-确实看起来有些人在寻找一个稍微不同的问题,这是一个解决方案,然后找到了。尽管这不一定是对这个问题的答案,但绝对是对另一个类似问题的答案。因此,可以在此处获得此答案,因为它可以帮助那些正在寻找该答案的人。感谢您的答复,澄清了这一点。
Cannicide

16

如果您使用的是Web Workers,并且希望在worker的范围内包括其他脚本,则提供了有关将脚本添加到Web的其他答案。head标记将对您不起作用。

幸运的是,Web Workers具有自己的importScripts功能,这是Web Workers范围内的全局功能,它浏览器本身的固有功能,因为它是规范的一部分

另外,作为对您问题的第二投票,突出显示 RequireJS也可以在Web Worker中处理脚本(可能会importScripts自称,但还具有其他一些有用的功能)。


16

用现代语言检查脚本是否已经加载,它将是:

function loadJs(url){
  return new Promise( (resolve, reject) => {
    if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
    const script = document.createElement("script")
    script.src = url
    script.onload = resolve
    script.onerror = reject
    document.head.appendChild(script)
  });
}

用法(异步/等待):

try { await loadJs("https://.../script.js") } 
catch(error) {console.log(error)}

要么

await loadJs("https://.../script.js").catch(err => {})

用法(承诺):

loadJs("https://.../script.js").then(res => {}).catch(err => {})

真好 好的解决方案。
Naftali aka Neal'7

简洁而仅需ES5,并且优雅地避免了正式的回叫。谢谢德米特里!
尤里卡

哇,可以在没有服务器的浏览器中使用。pi.js =简单地var pi = 3.14。通过loadJs("pi.js").then(function(){ console.log(pi); });
zipzit

14

@import可以使用Mixture之类的工具通过其特殊.mix文件类型来实现类似CSS的JavaScript导入的语法(请参见此处))。我想该应用程序只是内部使用一种上述方法,尽管我不知道。

从文件的Mixture文档中.mix

混合文件只是带.mix的.js或.css文件。在文件名中。混合文件只是扩展了普通样式或脚本文件的功能,并允许您导入和合并。

这是一个.mix将多个.js文件合并为一个的示例文件:

// scripts-global.mix.js
// Plugins - Global

@import "global-plugins/headroom.js";
@import "global-plugins/retina-1.1.0.js";
@import "global-plugins/isotope.js";
@import "global-plugins/jquery.fitvids.js";

Mixture将其输出为scripts-global.js,也将其作为缩小版本输出(scripts-global.min.js)。

注意:除了使用它作为前端开发工具之外,我与Mixture无关。在看到一个运行中的.mixJavaScript文件(在Mixture样板之一中)时,我遇到了这个问题,对此有点困惑(“我能做到吗?”我心想)。然后我意识到这是一个特定于应用程序的文件类型(有些令人失望,同意)。但是,认为这些知识可能对其他人有所帮助。

更新:混合物现在免费(脱机)。

更新:混合物现已停产。旧的混合物版本仍然可用


如果它是节点模块,那就太棒了。
2015年

@ b01听起来像是一个挑战;)如果我有时间的话……也许其他人呢?
艾萨克·格雷格森


13

我通常的方法是:

var require = function (src, cb) {
    cb = cb || function () {};

    var newScriptTag = document.createElement('script'),
        firstScriptTag = document.getElementsByTagName('script')[0];
    newScriptTag.src = src;
    newScriptTag.async = true;
    newScriptTag.onload = newScriptTag.onreadystatechange = function () {
        (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') && (cb());
    };
    firstScriptTag.parentNode.insertBefore(newScriptTag, firstScriptTag);
}

它工作得很好,对我来说不需要任何页面重载。我已经尝试过AJAX方法(其他答案之一),但它似乎对我来说效果不佳。

这是代码对好奇的人如何工作的解释:从本质上讲,它会创建URL的新脚本标签(在第一个脚本标签之后)。它将其设置为异步模式,因此它不会阻止其余代码,但是会在readyState(要加载的内容的状态)更改为“ loaded”时调用回调。


13

尽管这些答案很不错,但是自从脚本加载就存在一个简单的“解决方案”,它将覆盖大多数人的99.999%用例。只需在需要脚本之前添加所需脚本即可。对于大多数项目,不需要很长时间就能确定需要哪些脚本以及顺序如何。

<!DOCTYPE HTML>
<html>
    <head>
        <script src="script1.js"></script>
        <script src="script2.js"></script>
    </head>
    <body></body>
</html>

如果script2需要script1,那么这实际上是绝对简单的方法。我很惊讶没有人提出这个建议,因为这是几乎在每种情况下都适用的最明显,最简单的答案。


1
我猜对于包括我自己在内的许多人,我们需要动态文件名。
斯图尔特·麦金太尔

1
在这种情况下,@ StuartMcIntyre在运行时设置脚本标签的src属性,然后等待onload事件(当然,在这种情况下,2019年会有更好的解决方案)。
KthProg

12

我编写了一个简单的模块,该模块可以自动执行导入/包含JavaScript中的模块脚本的工作。有关代码的详细说明,请参阅博客文章JavaScript require / import / include modules

// ----- USAGE -----

require('ivar.util.string');
require('ivar.net.*');
require('ivar/util/array.js');
require('http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js');

ready(function(){
    //Do something when required scripts are loaded
});

    //--------------------

var _rmod = _rmod || {}; //Require module namespace
_rmod.LOADED = false;
_rmod.on_ready_fn_stack = [];
_rmod.libpath = '';
_rmod.imported = {};
_rmod.loading = {
    scripts: {},
    length: 0
};

_rmod.findScriptPath = function(script_name) {
    var script_elems = document.getElementsByTagName('script');
    for (var i = 0; i < script_elems.length; i++) {
        if (script_elems[i].src.endsWith(script_name)) {
            var href = window.location.href;
            href = href.substring(0, href.lastIndexOf('/'));
            var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length);
            return url.substring(href.length+1, url.length);
        }
    }
    return '';
};

_rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark
                                                   //the root directory of your library, any library.


_rmod.injectScript = function(script_name, uri, callback, prepare) {

    if(!prepare)
        prepare(script_name, uri);

    var script_elem = document.createElement('script');
    script_elem.type = 'text/javascript';
    script_elem.title = script_name;
    script_elem.src = uri;
    script_elem.async = true;
    script_elem.defer = false;

    if(!callback)
        script_elem.onload = function() {
            callback(script_name, uri);
        };
    document.getElementsByTagName('head')[0].appendChild(script_elem);
};

_rmod.requirePrepare = function(script_name, uri) {
    _rmod.loading.scripts[script_name] = uri;
    _rmod.loading.length++;
};

_rmod.requireCallback = function(script_name, uri) {
    _rmod.loading.length--;
    delete _rmod.loading.scripts[script_name];
    _rmod.imported[script_name] = uri;

    if(_rmod.loading.length == 0)
        _rmod.onReady();
};

_rmod.onReady = function() {
    if (!_rmod.LOADED) {
        for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++){
            _rmod.on_ready_fn_stack[i]();
        });
        _rmod.LOADED = true;
    }
};

_.rmod = namespaceToUri = function(script_name, url) {
    var np = script_name.split('.');
    if (np.getLast() === '*') {
        np.pop();
        np.push('_all');
    }

    if(!url)
        url = '';

    script_name = np.join('.');
    return  url + np.join('/')+'.js';
};

//You can rename based on your liking. I chose require, but it
//can be called include or anything else that is easy for you
//to remember or write, except "import", because it is reserved
//for future use.
var require = function(script_name) {
    var uri = '';
    if (script_name.indexOf('/') > -1) {
        uri = script_name;
        var lastSlash = uri.lastIndexOf('/');
        script_name = uri.substring(lastSlash+1, uri.length);
    } 
    else {
        uri = _rmod.namespaceToUri(script_name, ivar._private.libpath);
    }

    if (!_rmod.loading.scripts.hasOwnProperty(script_name)
     && !_rmod.imported.hasOwnProperty(script_name)) {
        _rmod.injectScript(script_name, uri,
            _rmod.requireCallback,
                _rmod.requirePrepare);
    }
};

var ready = function(fn) {
    _rmod.on_ready_fn_stack.push(fn);
};

10

该脚本会将JavaScript文件添加到其他任何<script>标签的顶部:

(function () {
    var li = document.createElement('script'); 
    li.type = 'text/javascript'; 
    li.src= "http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"; 
    li.async=true; 
    var s = document.getElementsByTagName('script')[0]; 
    s.parentNode.insertBefore(li, s);
})();

10

还有Head.js。这很容易处理:

head.load("js/jquery.min.js",
          "js/jquery.someplugin.js",
          "js/jquery.someplugin.css", function() {
  alert("Everything is ok!");
});

如您所见,它比Require.js更容易,并且与jQuery的$.getScript方法一样方便。它也有一些先进的功能,如条件装载,特征检测和更多


10

这个问题有很多潜在的答案。我的答案显然基于其中的一些。这是我看完所有答案后得出的结论。

$.getScript加载完成后实际上需要回调的其他解决方案的问题是,如果您有多个使用它的文件并且彼此依赖,则您将无法再知道何时加载所有脚本(一旦嵌套)在多个文件中)。

例:

file3.js

var f3obj = "file3";

// Define other stuff

file2.js:

var f2obj = "file2";
$.getScript("file3.js", function(){

    alert(f3obj);

    // Use anything defined in file3.
});

file1.js:

$.getScript("file2.js", function(){
    alert(f3obj); //This will probably fail because file3 is only guaranteed to have loaded inside the callback in file2.
    alert(f2obj);

    // Use anything defined in the loaded script...
});

说对了,您可以指定Ajax同步运行或使用XMLHttpRequest是对的,这是对的,但是当前的趋势似乎是弃用同步请求,因此您现在或将来可能无法获得完整的浏览器支持。

您可以尝试使用$.when它检查延迟对象的数组,但是现在您正在每个文件中执行此操作,并且将在文件File2 $.when被执行时立即考虑加载,而不是在执行回调时执行,因此file1在文件3被加载之前仍会继续执行。这确实仍然存在相同的问题。

我决定后退而不是前进。谢谢document.writeln。我知道这是忌讳的,但是只要正确使用它就可以了。您最终得到的代码可以轻松调试,可以在DOM中正确显示,并且可以确保正确加载依赖项的顺序。

您当然可以使用$(“ body”)。append(),但随后您将无法再正确调试。

注意:仅在页面加载时才必须使用此选项,否则将出现空白屏幕。换句话说,请始终将其放在document.ready之前/之外。在将页面加载到click事件或类似事件之后,我还没有使用此工具进行过测试,但是我敢肯定它会失败。

我喜欢扩展jQuery的想法,但显然您不需要这样做。

在调用之前document.writeln,它将通过评估所有脚本元素来检查以确保尚未加载脚本。

我假定脚本的document.ready事件执行完毕后才能完全执行。(我知道使用document.ready不是必需的,但是很多人都使用它,并且处理这是一种保障。)

加载其他文件后,document.ready回调将以错误的顺序执行。为了在实际加载脚本时解决此问题,将重新导入导入脚本的脚本本身并暂停执行。这使原始文件现在可以从其document.ready导入的任何脚本中执行任何回调。

除了这种方法,您可以尝试修改jQuery readyList,但这似乎是一个较差的解决方案。

解:

$.extend(true,
{
    import_js : function(scriptpath, reAddLast)
    {
        if (typeof reAddLast === "undefined" || reAddLast === null)
        {
            reAddLast = true; // Default this value to true. It is not used by the end user, only to facilitate recursion correctly.
        }

        var found = false;
        if (reAddLast == true) // If we are re-adding the originating script we do not care if it has already been added.
        {
            found = $('script').filter(function () {
                return ($(this).attr('src') == scriptpath);
            }).length != 0; // jQuery to check if the script already exists. (replace it with straight JavaScript if you don't like jQuery.
        }

        if (found == false) {

            var callingScriptPath = $('script').last().attr("src"); // Get the script that is currently loading. Again this creates a limitation where this should not be used in a button, and only before document.ready.

            document.writeln("<script type='text/javascript' src='" + scriptpath + "'></script>"); // Add the script to the document using writeln

            if (reAddLast)
            {
                $.import_js(callingScriptPath, false); // Call itself with the originating script to fix the order.
                throw 'Readding script to correct order: ' + scriptpath + ' < ' + callingScriptPath; // This halts execution of the originating script since it is getting reloaded. If you put a try / catch around the call to $.import_js you results will vary.
            }
            return true;
        }
        return false;
    }
});

用法:

文件3:

var f3obj = "file3";

// Define other stuff
$(function(){
    f3obj = "file3docready";
});

文件2:

$.import_js('js/file3.js');
var f2obj = "file2";
$(function(){
    f2obj = "file2docready";
});

文件1:

$.import_js('js/file2.js');

// Use objects from file2 or file3
alert(f3obj); // "file3"
alert(f2obj); // "file2"

$(function(){
    // Use objects from file2 or file3 some more.
    alert(f3obj); //"file3docready"
    alert(f2obj); //"file2docready"
});

这正是当前接受的答案所陈述的:仅仅是不够的。
杀人剂'17

主要区别在于,如果缺少依赖项,它将为该依赖关系添加脚本标记,然后还为调用文件添加脚本标记,并引发一个错误,该错误将停止执行。这将导致脚本被处理并以正确的顺序运行,并且消除了对回调的需要。因此,在支持嵌套的同时,相关脚本将在调用脚本之前加载并执行。
—curcurhairedgenius

至于“当前趋势似乎是弃用同步请求”,这是正确的,这仅是因为许多开发人员滥用了它们,并使用户不必要地等待。但是,如果请求是自然同步的,例如Include语句,那么简单的同步Ajax就可以了。我创建了一个通用的JavaScript扩展功能,该功能可以通过运行PHP“服务器”文件来同步执行诸如读取不属于JavaScript的文件之类的功能,并且在使用JavaScript编写应用程序时非常有用。这样的Ajax不需要本地Web服务器。
David Spector '18

10

有几种方法可以用Javascript实现模块,以下是两种最流行的方法:

ES6模块

浏览器尚不支持此调制系统,因此,要使用此语法,必须使用捆绑程序(例如webpack)。无论如何,使用捆绑器会更好,因为这可以将所有不同的文件合并为一个(或几个相关的)文件。这将更快地将文件从服务器发送到客户端,因为每个HTTP请求都带有一些相关的开销。因此,通过减少总体HTTP请求,我们可以提高性能。这是ES6模块的示例:

// main.js file

export function add (a, b) {
  return a + b;
}

export default function multiply (a, b) {
  return a * b;
}


// test.js file

import {add}, multiply from './main';   // for named exports between curly braces {export1, export2}
                                        // for default exports without {}

console.log(multiply(2, 2));  // logs 4

console.log(add(1, 2));  // logs 3

CommonJS(在NodeJS中使用)

此调制系统用于NodeJS。基本上,您将导出添加到名为的对象中module.exports。然后,您可以通过访问该对象require('modulePath')。在这里重要的是要意识到这些模块正在被缓存,因此,如果require()某个模块两次,它将返回已经创建的模块。

// main.js file

function add (a, b) {
  return a + b;
}

module.exports = add;  // here we add our add function to the exports object


// test.js file

const add = require('./main'); 

console.log(add(1,2));  // logs 3

9

我之所以提出这个问题,是因为我在寻找一种简单的方法来维护一组有用的JavaScript插件。在这里看到一些解决方案后,我想到了:

  1. 设置一个名为“ plugins.js”的文件(或extensions.js或您想要的任何文件)。将您的插件文件与该一个主文件保持在一起。

  2. plugins.js将有一个名为的数组pluginNames[],我们将对其进行迭代each(),然后<script>为每个插件的头部添加一个标签

//set array to be updated when we add or remove plugin files
var pluginNames = ["lettering", "fittext", "butterjam", etc.];

//one script tag for each plugin
$.each(pluginNames, function(){
    $('head').append('<script src="js/plugins/' + this + '.js"></script>');
});
  1. 手动仅调用您脑中的一个文件:
    <script src="js/plugins/plugins.js"></script>

但:

即使所有插件都按应有的方式放入了head标签,但当您单击页面或刷新时,它们并不总是被浏览器运行。

我发现仅在PHP include中编写脚本标签会更可靠。您只需编写一次,就和使用JavaScript调用插件一样多。


@will,您的解决方案看起来比我的解决方案干净得多,并且我担心如果我使用我的解决方案,因为它使用了.append(),可能会出现一些错误。因此,要使用此功能,您只需为要包含的每个插件文件调用一次该函数?
rgb_life 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.