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

Posted

技术标签:

【中文标题】如何在另一个 JavaScript 文件中包含一个 JavaScript 文件?【英文标题】:How do I include a JavaScript file in another JavaScript file? 【发布时间】:2010-10-31 07:24:12 【问题描述】:

javascript 中是否有类似于 CSS 中的 @import 的东西允许您将 JavaScript 文件包含在另一个 JavaScript 文件中?

【问题讨论】:

MDN docs about JavaScript modules,与corresponding examples on github 【参考方案1】:

在后端,您可以使用 CommonJS 模块。例如:

//a.js
function func () 

module.exports =  func ;
//b.js
var a = require('./a.js');
console.log(a.func);

【讨论】:

【参考方案2】:

我的通用解决方案来自 EdgeS (我创作的)efekt.js.st 库。

无耻的插件警报 - 我在其他 stackexchange 网络站点上。这是https://codereview.stackexchange.com/questions/263764/dynamic-load-css-or-script 的重新链接。

您将使用什么代码或设计来支持cssscripts 的动态加载?

要求

支持promise-await-async,包括错误处理 支持load-once缓存,包括重新加载 支持在headbody或当前script-element中加载 支持加载cssjsmjs模块或其他脚本类型 支持其他标签attrs,如noncecrossorigin
static loadScriptOrStyle(url, options) 
  // provenance :<# **Smallscript EdgeS efekt** `efekt.js.st` github libraries #>
  // returns    :<Promise#onload;onerror>
  // options    :<# `fIgnoreCache`, `fAppendToHead`, `fUrlIsStyle`, `attrs:` #>
  const head = document.head; let node = options?.fAppendToBody ? document.body : head;
  const url_loader_cache = document.head.url_loader_cache
    ? head.url_loader_cache
    : (head.url_loader_cache = script:,link:)
  const kind = (options?.fUrlIsStyle || /\.css(?:(?:\?|#).*)?$/i.test(url))
    ? 'link' : 'script';
  // check already-loaded cache
  if(url_loader_cache[kind][url]) 
    const el = url_loader_cache[kind][url];
    // support `fIgnoreCache` reload-option; should not use on `head`
    if(options?.fIgnoreCache)
      el.remove();
    else
      return(new CustomEvent('cache',detail:el));
  
  // (re)create and record it
  const self = document.currentScript;
  const el = url_loader_cache[kind][url] = document.createElement(kind);
  const append = (!self || options?.fAppendToHead || options?.fAppendToBody)
    ? el => node.appendChild(el)
    : el => self.parentNode.insertBefore(el, self);
  const load = new Promise((resolve, reject) => 
    el.onload  = e => e.detail = el;resolve(e);
    el.onerror = e => e.detail = el;reject(e);
    // `onload` or `onerror` possibly alter `cache` value
    // throw(new URIError(`The $url didn't load correctly.`))
  );
  // configure `module` attr, as appropriate
  if(/\.mjs(?:(?:\?|#).*)?$/i.test(url))
    el.type = 'module'
  // configure other attrs as appropriate (referrer, nonce, etc)
  for(const key in options?.attrs) el[key] = attrs[key]
  // trigger it
  if(kind === 'link') el.rel = 'stylesheet', el.href = url; else el.src = url;
  append(el);
  return(load);

【讨论】:

【参考方案3】:

发出fetch 请求和eval 结果。

【讨论】:

【参考方案4】:

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

你需要在页面的&lt;head&gt;元素上使用jQuery.append(),即:

$("head").append($("<script></script>").attr("src", url));

/* Note that following line of code is incorrect because it doesn't escape the
 * html attribute src correctly and will fail if `url` contains special characters:
 * $("head").append('<script src="' + url + '"></script>');
 */

但是,这种方法也有一个问题:如果导入的 JavaScript 文件发生错误,Firebug(以及 Firefox 错误控制台和Chrome Developer Tools)会错误地报告它的位置,如果您经常使用 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></script').attr('src', script));
                import_js_imported.push(script);
            
        
    );
    
)(jQuery);

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

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

我也在Example做了一个简单的测试。

它在主 HTML 中包含一个main.js 文件,然后main.js 中的脚本使用$.import_js() 导入一个名为included.js 的附加文件,它定义了这个函数:

function hello()

    alert("Hello world!");

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

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

【讨论】:

【参考方案5】:
Step 1: Declare the function in another class.

    export const myreport = (value) => 
    color = value.color;
    name = value.name;
    
    var mytext = name + " | " + color;
    return mytext;
    

Step 2:- Import that function which is needed to be used.

    import myreport from '../../Test'

Step 3:- Use that function.

let val =  color: "red", name: "error" 
var resultText = myreport(val)
console.log("resultText :- ", resultText)

【讨论】:

【参考方案6】:

@import 语法用于实现类似 CSS 的 JavaScript 导入可以使用 Mixture 等工具通过其特殊的 .mix 文件类型(参见 here)来实现。我假设应用程序通过上述方法之一执行此操作。

来自 .mix 文件的 Mixture 文档:

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

这是一个示例 .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 没有任何关系,只是将它用作前端开发工具。我在看到一个正在运行的.mix JavaScript 文件(在其中一个 Mixture 样板中)并对此感到有点困惑时遇到了这个问题(“你可以这样做吗?”我心想)。然后我意识到这是一种特定于应用程序的文件类型(有点令人失望,同意)。不过,认为这些知识可能对其他人有所帮助。

注意:Mixture 于 2016 年 7 月 26 日停产(在 2015 年 4 月 12 日开源之后)。

【讨论】:

最好避免“更新”(属于本帖修订历史的元信息)。而是将其应用于内容(不是这篇文章),例如“Mixture 于 2015 年 4 月 12 日开源,并于 2016 年 7 月 26 日停产。”【参考方案7】:

保持美观、简短、简单和可维护! :]

// Third-party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
    FULL_PATH + 'plugins/script.js'      // Script example
    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library
    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS
    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS
];

function load(url)

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


// Initialize a single load
load('plugins/script.js');

// Initialize a full load of scripts
if (s.length > 0)

    for (i = 0; i < s.length; i++)
    
        load(s[i]);
    

此代码只是一个简短的功能示例,可能需要额外的特性功能才能在任何(或给定)平台上获得全面支持。

【讨论】:

解释一下。例如。什么是想法(操作原理)以及它是如何工作的?【参考方案8】:

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

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

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

$.getScript("another_script.js");

【讨论】:

【参考方案9】:

此脚本会将 JavaScript 文件添加到任何其他 &lt;script&gt; 标记的顶部:

(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】:

请注意,我们通常使用静态脚本。所以我们希望尽可能多地从缓存中取出。

这样可以节省网络流量并加快登陆速度。

用法

$.cachedScript( "ajax/test.js" ).done(function( script, textStatus ) 
  console.log( textStatus );
);

cache: true 选项已添加到 Ajax 方法中。

【讨论】:

【参考方案11】:

如果你发现有两个或多个脚本被调用时占用了同一个函数,我们不能同时包含它们,我们需要通过用户选择动态来做。

使用 $.getScript 在 jQuery 中包含另一个文件,因为脚本 will not be cached by default 有效。所以我们可以安全地调用其他脚本。调用可以这样安排:

HTML

<select class="choice">
  <option value="script1" selected>Script-1</option>
  <option value="script2">Script-2</option>
</select>

JavaScript

  $(".choice").change(on_change);

    var url = "https://example.com";
    $.url1 = url + "/script1.js";
    $.url2 = url + "/script2.js";

  function on_change() 
    if ($(".choice").val()=="script1") 
        script1();
     else 
         script2();
    

    // script1
    function script1() 
      $.getScript($.url1, function( data, textStatus, jqxhr ) 
          // Execute here
      );
    

    // script2
    function script2() 
       $.getScript($.url2, function( data, textStatus, jqxhr ) 
          // Execute here
      );
    

【讨论】:

【参考方案12】:

是的,有……

继续阅读。在 ES6 中,我们可以将 exportimport 部分或整个 JavaScript 文件放到另一个文件中...

但是等等,ES6 并不是所有浏览器都支持的,所以你需要使用babel.js 进行转译...

所以你创建了一个如下所示的类:

class Person 
  constructor(name) 
    this.name = name;
  

  build() 
    return new Person(this);
  


module.exports = Person;

另一个 JavaScript 文件中,像这样导入:

import  Person  from 'Person';

你也可以像这样要求文件:

const Person = require('./Person');

如果您使用的是较旧的 JavaScript 版本,您可以使用 requirejs

requirejs(["helper/util"], function(util) 
    // This function is called when scripts/helper/util.js is loaded.
    // If util.js calls define(), then this function is not fired until
    // util's dependencies have loaded, and the util argument will hold
    // the module value for "helper/util".
);

如果你想坚持使用旧版本的东西,比如 jQuery,你也可以使用 getScript 之类的东西:

jQuery.getScript('./another-script.js', function() 
    // Call back after another-script loaded
);

最后但并非最不重要的一点是,不要忘记您可以使用 &lt;script&gt; 标记将脚本放在一起的传统方式...

<script src="./first-script.js"></script>
<script src="./second-script.js"></script>
<script src="./third-script.js"></script>

还有 asyncdefer 属性,我应该在这里提到...

注意:可以通过多种方式执行外部脚本:

如果存在异步:脚本异步执行 与页面的其余部分(脚本将在页面 继续解析) 如果 async 不存在并且 defer 存在 present:页面完成后执行脚本 解析 如果async或defer都不存在:脚本是 在浏览器继续之前立即获取并执行 解析页面

【讨论】:

【参考方案13】:

仅适用于 Node.js,这对我来说效果最好!

我在这里尝试了大多数解决方案,但没有一个能帮助我在不改变范围的情况下加载另一个文件。最后我用了这个。这保留了范围和一切。它和你的代码一样好。

const fs = require('fs');
eval(fs.readFileSync('file.js') + '');

【讨论】:

为什么是最后一个学期?它似乎没有做任何事情。 readFileSync 返回缓冲区。 + '' 将其转换为字符串。【参考方案14】:

有几种方法可以在 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(用于 Node.js)

此模块系统用于 Node.js。您基本上将您的导出添加到一个名为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

【讨论】:

【参考方案15】:

您可以使用我的loadScript ES module 来加载 JavaScript 文件。

用法:

在您的 head 标签中,包含以下代码:

<script src="https://raw.githack.com/anhr/loadScriptNodeJS/master/build/loadScript.js"></script>

<script src="https://raw.githack.com/anhr/loadScriptNodeJS/master/build/loadScript.min.js"></script>

现在您可以使用 window.loadScript 来加载您的 JavaScript 文件了。

loadScript.async(src, [options])

异步加载 JavaScript 文件。

src:外部脚本文件的 URL 或脚本文件名数组。

options: 以下选项可用

onload: function () The onload event occurs when a script has been loaded. Default is undefined.

onerror: function ( str, e ) The onerror event occurs when an error has been occurred. The default is undefined.

    str: error details

    e: event

appendTo: The node to which the new script will be append. The default is the head node.

例如

loadScript.async( "JavaScript.js",
        
            onload: function () 

                var str = 'file has been loaded successfully';
                console.log( str );
            ,
            onerror: function ( str, e ) 

                console.error( str );
            ,
         );

Example of usage

【讨论】:

【参考方案16】:

来自Dan Dascalescu's answer 的库的一个小扩展,来自 Facebook 的想法。

(function() 
var __ = ;
this._ = function(name, callback) 
    if(__[name]==undefined) 
        __[name] = true;
        var firstScript = document.getElementsByTagName('script')[0],
          js = document.createElement('script');
          js.src =  name;
          js.onload = callback;
          firstScript.parentNode.insertBefore(js, firstScript);
    

)();

(new _('https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js', function() 
 snowStorm.snowColor = '#99ccff';
));

【讨论】:

【参考方案17】:

ES6 模块

是的,在脚本标签中使用 type="module" (support):

<script type="module" src="script.js"></script>

script.js 文件中包含另一个文件,如下所示:

import  hello  from './module.js';
...
// alert(hello());

在“module.js”中,您必须 export the function/class 才能导入:

export function hello() 
    return "Hello World";

一个工作的example is here。

【讨论】:

【参考方案18】:

我没有看到一个答案,即您在一个文件中创建一个包含所有函数和变量的对象,然后将该对象作为参数以在另一个文件中引用它。

例如,您有名为“jsMod.js”、“jsView”和“jsContr.js”的文件:


    //jsMod.js file
    JSMODOBJ = ;
    JSMODOBJ.valueAddition = function(/* element value 1 */ val1,
                                          /* element value 2 */ val2) 
        return val1 + val2;
    


    //jsView.js file
    JSVIEWOBJ = ;
    JSVIEWOBJ.elementColour = function(/* element id to change colour */ id,
                                          /* css colour classname */ col) 
        document.getElementById(id).className = col;
    


    //jsContr.js file
    JSCONTROBJ = ;
    var jsMod = JSMODOBJ;
    var jsView = JSVIEWOBJ;

    JSCONTROBJ.changeColourByValue = function (val1, val2, id, clss) 
        if (jsMod.valueAddition(val1,val2) !== 0) 
            jsView.elementColour(id, clss);
        
    

然后您可以通过将scripts 回显到您的 .html 或 .php 文件中来动态设置 .js 文件:

<?php
    echo "<script src = './js/dleafView.js'></script>
        <script src = './js/dleafModule.js'></script>
        <script src = './js/dleafContr.js'></script>";
?>

然后只需在&lt;script type="text/javascript"&gt;&lt;/script&gt; 标记内调用控制函数。当然,这在开始时会花费大量时间来设置,但从长远来看,它可以节省您的时间。

我的使用方式略有不同,但这种方式也可以。

【讨论】:

【参考方案19】:

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

function loadJs( url )
  return new Promise(( resolve, reject ) => 
    if (document.querySelector( `head > script[ src = "$url" ]`) !== null )
        console.warn( `script already loaded: $url` );
        resolve();
    
    const script = document.createElement( "script" );
    script.src = url;
    script.onload = resolve;
    script.onerror = function( reason )
        // This can be useful for your error-handling code
        reason.message = `error trying to load script $url`;
        reject( reason );
    ;
    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 =>  );

【讨论】:

如果你想避免参与模块,又不想使用回调函数,但又想使用async / await,这非常好。跨度> url 需要在这里正确转义:`head &gt; script[ src = "$url" ]`【参考方案20】:

这可能是另一种方式!

在 Node.js 中,您可以像下面的代码所示那样做!

sub.js

    module.exports = 
      log: function(string) 
        if(console) console.log(string);
      
      mylog: function()
        console.log('just for log test!');
      
    

ma​​in.js

    const mylog = require('./sub');

    mylog.log('Hurray, it works! :)');
    mylog.mylog();

参考

http://requirejs.org/docs/node.html

【讨论】:

【参考方案21】:

你应该使用这个:

<script src="your_file.js"></script>

简单!

【讨论】:

【参考方案22】:

您还可以使用gulpgulp-concatgulp-typescript/// &lt;reference path= 包括:

packages.json


  "scripts": 
    "gulp": "gulp main"
  ,
  "dependencies": 
    "@types/gulp": "^4.0.6",
    "@types/gulp-concat",
    "@types/gulp-typescript",
    "gulp": "^4.0.2",
    "gulp-concat": "^2.6.1",
    "gulp-resolve-dependencies": "^3.0.1",
    "gulp-typescript": "^6.0.0-alpha.1",
    "typescript": "^3.7.3"
  

src/someimport.ts

class SomeClass 
    delay: number;

src/main.ts

/// <reference path="./someimport.ts" />

someclass = new SomeClass();
someclass.delay = 1;

main gulp 任务(在gulpfile.js 上)仅针对src/main.js 文件,解析其所有/// &lt;reference path=... 包含引用。这些包含被称为Triple-Slash Directives,它们仅用于编译器工具来组合文件。在我们的例子中,.pipe(resolveDependencies( 和 typescript 本身在检查文件是否缺少类型、变量等时明确使用它们。

    https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html When do I need a triple slash reference?

如果您想自定义var tsProject = ts.createProject 调用而不使用tsconfig.json 文件或覆盖其参数,请参阅https://github.com/ivogabe/gulp-typescript#api-overview。

gulpfile.js

var gulp = require("gulp");
var concat = require('gulp-concat');
var resolveDependencies = require('gulp-resolve-dependencies');

var ts = require("gulp-typescript");
var tsProject = ts.createProject("tsconfig.json");

gulp.task("main", function() 
  return gulp
    .src(["src/main.ts"])
    .pipe(resolveDependencies(
      pattern: /^\s*\/\/\/\s*<\s*reference\s*path\s*=\s*(?:"|')([^'"\n]+)/gm
    ))
    .on('error', function(err) 
        console.log(err.message);
    )
    .pipe(tsProject())
    .pipe(concat('main.js'))
    .pipe(gulp.dest("build/"));
);

如果您想定位所有类型脚本项目文件,而不仅仅是src/main.ts,您可以替换它:

  return gulp
    .src(["src/main.ts"])
    .pipe(resolveDependencies(
    ...
// -->
  return tsProject
    .src()
    .pipe(resolveDependencies(
    ...

如果您不想使用typescript,您可以使用这个简化的gulpfile.js 并从package.json 中删除所有typescript 包含:

gulpfile.js

var gulp = require("gulp");
var concat = require('gulp-concat');
var resolveDependencies = require('gulp-resolve-dependencies');

gulp.task("main", function() 
  return gulp
    .src(["src/main.js"])
    .pipe(resolveDependencies(
      pattern: /^\s*\/\/\/\s*<\s*reference\s*path\s*=\s*(?:"|')([^'"\n]+)/gm
    ))
    .on('error', function(err) 
        console.log(err.message);
    )
    .pipe(concat('main.js'))
    .pipe(gulp.dest("build/"));
);

packages.json


  "scripts": 
    "gulp": "gulp main"
  ,
  "dependencies": 
    "gulp": "^4.0.2",
    "gulp-concat": "^2.6.1",
    "gulp-resolve-dependencies": "^3.0.1"
  

然后,运行命令npm run gulp 后,将创建文件build/main.js,其内容如下:

build/main.js

class SomeClass 

/// <reference path="./someimport.ts" />
someclass = new SomeClass();
someclass.delay = 1;

这允许我在提供build 目录文件之后,使用script 标记将其包含在浏览器中:

<html>
    <head>
        <script src="main.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            console.log(someclass.delay);
        </script>
    </body>
</html>

相关问题:

    https://www.typescriptlang.org/docs/handbook/gulp.html Can I use the typescript without requireJS? Gulp simple concatenation of main file that requires another JS file Client on node: Uncaught ReferenceError: require is not defined How can typescript browser node modules be compiled with gulp? Concatenate files using babel How to require CommonJS modules in the browser? Is there an alternative to Browserify?

【讨论】:

【参考方案23】:

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

但自 2015 年(ES6)以来,JavaScript 已经有了 ES6 modules 标准来在 Node.js 中导入模块,most modern browsers 也支持这一点。

为了与旧版浏览器兼容,可以使用Webpack 和Rollup 等构建工具和/或Babel 等转译工具。

ES6 模块

ECMAScript (ES6) 模块从 v8.5 开始是 supported in Node.js,带有 --experimental-modules 标志,至少从 Node.js v13.8.0 开始没有标志。要启用“ESM”(相对于 Node.js 以前的 CommonJS 样式模块系统 [“CJS”]),您可以在 package.json 中使用 "type": "module" 或将文件扩展名为 .mjs。 (类似地,如果您的默认值为 ESM,则使用 Node.js 以前的 CJS 模块编写的模块可以命名为 .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 模块

浏览器已经支持直接加载 ECMAScript 模块(不需要像 Webpack 这样的工具)since Safari 10.1、Chrome 61、Firefox 60 和 Edge 16。在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 需要

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

// 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 调用加载一个或多个脚本,使用承诺来控制使用 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 库提供加载功能in one line:

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

动态脚本加载

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

脚本甚至可以驻留在不同的服务器上。此外,浏览器评估代码。 &lt;script&gt; 标签可以注入到网页&lt;head&gt; 中,也可以插入到关闭&lt;/body&gt; 标签之前。

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

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)

此函数将在页面头部的末尾添加一个新的&lt;script&gt; 标记,其中src 属性设置为作为第一个参数提供给函数的URL。

JavaScript Madness: Dynamic Script Loading 中讨论和说明了这两种解决方案。

检测脚本何时执行

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

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

例如:my_lovely_script.js 包含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 function:

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

然后你运行所有这些:

loadScript("my_lovely_script.js", myPrettyCode);

请注意,脚本可能在 DOM 加载之后或之前执行,具体取决于浏览器以及您是否包含 script.async = false; 行。有一个great article on Javascript loading in general 讨论了这个。

源代码合并/预处理

如本答案顶部所述,许多开发人员在他们的项目中使用构建/转换工具,如 Parcel、Webpack 或 Babel,允许他们使用即将推出的 JavaScript 语法,为旧版浏览器提供向后兼容性,合并文件、缩小、执行代码拆分等。

【讨论】:

我已经通过单击菜单动态加载了 div,而没有使用 URL 哈希加载页面。我的问题是当我单击同一页面 2/3 次 js 加载 2/3 次时。这就是为什么每个事件都会多次发生。我想在附加该代码之前检查已经加载到页脚/头部的 js 文件: var js = document.createElement("script"); js.type = "文本/javascript"; js.src = js文件路径; document.body.appendChild(js); 您还可以使用 Gulp (gulpjs.com) 之类的东西对它们进行预处理,并将输出作为被调用的单个文件。例如:a) 将多个 JavaScript 文件连接成一个,b) 使用 Babel 使其向后兼容,c) minify/uglify 删除 cmets、空格等。然后,您不仅组织了这些文件,还优化了它们以及通过启动可能对其他文件格式(例如 css 和图像)执行相同操作的管道。 这些解决方案中的哪一种可以在 ES3 中使用?【参考方案24】:

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

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

    plugins.js 将有一个名为pluginNames[] 的数组,我们将遍历each(), 然后在每个插件的头部附加一个&lt;script&gt;标签

//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>');
);
    手动调用您脑海中的一个文件:&lt;script src="js/plugins/plugins.js"&gt;&lt;/script&gt;

但是:

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

我发现只在 PHP 包含中编写脚本标签更可靠。您只需编写一次,这与使用 JavaScript 调用插件一样多。

【讨论】:

请注意,如果 pluginNames 包含特殊字符,这将不起作用,甚至可能导致安全漏洞。您需要在这里使用正确的转义:$('head').append('&lt;script src="js/plugins/' + this + '.js"&gt;&lt;/script&gt;');【参考方案25】:

所以这是一个边缘情况。但是,如果您需要从远程源加载 JavaScript,大多数现代浏览器可能会由于 CORS 或类似原因而阻止您的跨站点请求。太正常了

<script src="https://another-domain.com/example.js"></script>

行不通。并且执行document.createElement('script').src = '...' 也不会削减它。相反,您可以通过标准 GET 请求将 java 脚本作为资源加载,然后执行以下操作:

<script type="text/javascript">
    var script = document.createElement('script');
    script.type = 'text/javascript';

    let xhr = new XMLHttpRequest();
    xhr.open("GET", 'https://raw.githubusercontent.com/Torxed/slimWebSocket/master/slimWebSocket.js', true);
    xhr.onreadystatechange = function() 
        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) 
            script.innerHTML = this.responseText; // <-- This one
            document.head.appendChild(script);
        
    
    xhr.send();
</script>

通过自己抓取内容,浏览器不会注意到恶意意图并允许您执行请求。然后将其添加到&lt;script&gt;innerHTML 中。这仍然会导致浏览器(至少在 Chrome 中测试过) 解析/执行脚本。

同样,这是一个边缘案例用例。而且您可能没有向后兼容性或浏览器合规性。但有趣/有用的事情要知道。

【讨论】:

【参考方案26】:

这是一个浏览器(不是 Node.js)使用 HTML 导入的解决方法。

首先,所有的JavaScript类和脚本都不在.js文件中,而是在.js.html文件中(.js.html只是为了识别HTML页面和完整的JavaScript脚本/classes),在&lt;script&gt; 标签内,像这样:

MyClass.js.html:

<script>
   class MyClass 

      // Your code here..

   

</script>

如果你想导入你的类,你只需要使用 HTML 导入:

<link rel="import" href="relative/path/to/MyClass.js.html"/>

<script>
   var myClass = new MyClass();
   // Your code here..
</script>

编辑:HTML 导入将被删除

HTML 导入被删除,有利于 ES6 模块。 你应该使用 ES6 模块。

【讨论】:

【参考方案27】:

按顺序动态加载多个脚本

如果您只加载一个脚本或者您不关心多个脚本的加载顺序,上述功能可以正常工作。如果你有一些脚本依赖于其他的,你需要使用Promise来指定加载的顺序。这背后的原因是 Javascript 异步加载脚本和图像等资源。加载顺序不依赖于异步调用的顺序,这意味着即使在调用dynamicallyLoadScript("scrip2")之前调用dynamicallyLoadScript("scrip1"),也不能保证script1在script2之前加载

所以这是另一个版本的dynamicLoadScript,它保证了加载顺序:

// Based on: https://javascript.info/promise-basics#example-loadscript
function dynamicallyLoadScript(url) 
        return new Promise(function(resolve, reject) 
        var script = document.createElement("script");
        script.src = url;
        script.onload = resolve;
        script.onerror = () => reject(new Error(`Error when loading $url!`));
        document.body.appendChild(script);
    );

有关 Promise 的更多信息,请参阅this excellent page。

这个新的dynamicLoadScript的用法很简单:

dynamicallyLoadScript("script1.js")
.then(() => dynamicallyLoadScript("script2.js"))
.then(() => dynamicallyLoadScript("script3.js"))
.then(() => dynamicallyLoadScript("script4.js"))
.then(() => dynamicallyLoadScript("script5.js"))
//...

现在脚本按script1.js、script2.js、script3.js等顺序加载。

脚本加载后运行依赖代码

此外,您可以在加载脚本后立即运行使用脚本的代码。只需在加载脚本后添加另一个.then

dynamicallyLoadScript("script1.js")
.then(() => dynamicallyLoadScript("script2.js"))
.then(() => foo()) // foo can be a function defined in either script1, script2
.then(() => dynamicallyLoadScript("script3.js"))
.then(() => 
     if (var1) // var1 can be a global variable defined in either script1, script2, or script3
          bar(var1); // bar can be a function defined in either script1, script2, or script3
      else 
          foo(var1);
     
)
//more .then chains...

处理加载错误

要显示未处理的 Promise 拒绝(加载脚本时出错等),请将此 unhandledrejection 事件侦听器放在代码顶部:

// Based on: https://javascript.info/promise-error-handling#unhandled-rejections
window.addEventListener('unhandledrejection', function(event) 
     // the event object has two special properties:
     console.error(event.promise);// the promise that generated the error
     console.error(event.reason); // the unhandled error object
);

现在您将收到任何脚本加载错误的通知。


快捷键功能

如果你在加载大量脚本后没有立即执行代码,这个速记函数可能会派上用场:

function dynamicallyLoadScripts(urls)
        if (urls.length === 0)
            return;
        
        let promise = dynamicallyLoadScript(urls[0]);
        urls.slice(1).forEach(url => 
            promise = promise.then(() => dynamicallyLoadScript(url));
        );
    

要使用它,只需像这样传入一个脚本 url 数组:

const scriptURLs = ["dist/script1.js", "dist/script2.js", "dist/script3.js"];
dynamicallyLoadScripts(scriptURLs);

脚本将按照它们在数组中出现的顺序加载。

【讨论】:

这是一个很好的解决方案,并且解释得很好。太棒了,谢谢。【参考方案28】:

我用另一种方法尝试了这个问题,

脚本导入的顺序,这里没有影响。

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Trials</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="main.js"></script>
    <script src="scriptA.js"></script>
</head>

<body>
<h3>testing js in js (check console logs)</h3>
<button onclick="fnClick()">TEST</button>
</body>

</html>

ma​​in.js

function fnClick() 
  console.log('From\tAAAAA');
  var pro = myExpo.hello();
  console.log(pro);

scriptA.js

myExpo = 
    hello: function () 
        console.log('From\tBBBBB');
        return "Hello";
    

结果

From    AAAAA
From    BBBBB
Hello

【讨论】:

【参考方案29】:

使用适用于 Node.js 的 ES6 导入和导出模块

使用.mjs 扩展名而不是.js 命名文件

创建文件

touch main.mjs lib.mjs

main.js

import  add  from './lib.mjs';
console.log(add(40, 2));

lib.mjs

export let add = (x,y) => 
  return x + y

运行

node --experimental-modules main.js

【讨论】:

【参考方案30】:

尽管这些答案很棒,但自脚本加载存在以来就有一个简单的“解决方案”,它将涵盖大多数人 99.999% 的用例。只需在需要它的脚本之前包含您需要的脚本。对于大多数项目来说,确定需要哪些脚本以及以什么顺序需要很长时间。

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

如果 script2 需要 script1,这确实是执行此类操作的最简单方法。我很惊讶没有人提出这个问题,因为这是最明显和最简单的答案,几乎适用于每一个案例。

【讨论】:

这是一个很好的答案。它可能被遗漏了,因为它没有直接回答问题,但理解“你通常不需要这样做”也很重要。特别是因为其他解决方案非常混乱。 但这仅适用于网络浏览器?离线单元测试(比如在 Node.js 下)呢? 这个答案类似于 2015 年的详细答案 - ***.com/a/31552759/984471

以上是关于如何在另一个 JavaScript 文件中包含一个 JavaScript 文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用javascript在另一个jsp中包含一个jsp

如何在另一个 XAML 文件中包含 XAML 图标

如何在另一个 php.ini 文件中包含一个 php.ini 文件?

使用 QML 和 QtQuick2 时如何在另一个 .js 文件中包含 .js 文件?不涉及浏览器

GitHub Pages 上的 Jekyll:在另一个 markdown 文件中包含 markdown

是否可以在另一个中包含一个 CSS 文件?