JavaScript 文件的动态加载是你必须拥有的非常有用的工具之一。它允许你通过将阻塞脚本从加载过程中移出(通常称为“延迟加载”)来优化网页性能,并仅在用户需要时加载脚本(通常称为“按需加载”)。如果您明智地使用此工具,它将大大提高你的页面性能。

配置

让我们定义我们的示例模型,我们将从定义需要动态加载的远程文件开始。这是“ ​remote.js​ ”文件的定义:

// this is going to be executed when script is loaded
(function () {
  console.log("Remote script loaded");
}());

var sayHello = function (name) {
  alert("Hello", name);
}

在上面的代码中,我们定义了一个立即函数来跟踪文件加载。我们还定义了一个从主页面调用的自定义函数。

现在,这是我们的主要“ ​index.htm​ ”页面。它只包含一个用于加载和测试文件的按钮。

<html>
<head>
</head>
<body>
  <button id="loadButton">Load script file</button>
  <script type="text/javascript">
    document.getElementById('loadButton').onclick = function () {
      // your code goes here
    };
  </script>
</body>
</html>

无脑方法

加载 JavaScript 文件最直接的方法是在​<script>​元素中引用它。动态加载这个文件最简单的方法就是动态加载这个元素!(你没有看到即将到来吗?是吗?)

让我们更新代码并查看实际结果:

document.getElementById("loadButton").onclick = function () {
  var script = document.createElement("script");
  script.src = "remote.js";

  script.onload = function () {
    sayHello("Mohammad");
  };

  // append and execute script
  document.documentElement.firstChild.appendChild(script);
};

上面的代码简单地创建了一个​<script>​元素并将​src​这个元素的字段设置为我们文件的路径。然后它使用该​appendChild()​函数将其附加到我们代码的第一个子​<head>​元素元素。以下代码产生以下结果:

现在,让我们重构上面的代码并稍微修改一下,以便能够在其他任何地方使用它:

/**
 * Used to load and execute javascript file. an be used cross-domain seamlessly.
 * @param file JS file name
 * @param callback Subscribe to get notified when script file is loaded
 **/
function require(file, callback) {
  // create script element

  var script = document.createElement("script");
  script.src = file;

  // monitor script loading
  // IE < 7, does not support onload
  if (callback) {
    script.onreadystatechange = function () {
      if (script.readyState === "loaded" || script.readyState === "complete") {
        // no need to be notified again
        script.onreadystatechange = null;
        // notify user
        callback();
      }
    };

    // other browsers
    script.onload = function () {
      callback();
    };
  }

  // append and execute script
  document.documentElement.firstChild.appendChild(script);
}

document.getElementById("loadButton").onclick = function () {
  require("remote.js", function () {
    sayHello("Mohammad");
  });
};

现在,你可以轻松调用require()JavaScript 路径和回调函数,以便在加载脚本时收到通知。

费力方法

动态加载 JavaScript 文件的另一种方法是使用经典 HTTP 请求检索它。这是一个纯 JavaScript 调用,但它有很多缺点。让我们看看它的实际效果:

/**
 * Used to load and execute javascript file. Suffers from same-domain restriction.
 * @param file JS file name
 * @param callback Subscribe to get notified when script file is loaded
 **/
function requireXhr(file, callback) {
  // object initialization
  const xhr = new XMLHttpRequest();

  // subscribe to request events
  xhr.onreadystatechange = function () {
    // readyState:
    // 0 UNSENT Client has been created. open() not called yet.
    // 1 OPENED open() has been called.
    // 2 HEADERS_RECEIVED send() has been called, and headers and status are available.
    // 3 LOADING Downloading; responseText holds partial data.
    // 4 DONE The operation is complete.

    // when not done, return
    if (xhr.readyState !== 4) {
      return;
    }

    // done, check status code
    if (xhr.status !== 200) // 200 = OK
    {
      return;
    }

    // now the file is loaded,
    // go and execute the script
    eval(xhr.responseText);

    // notify caller
    if (callback) {
      callback();
    }
  };

  // open connection to file
  xhr.open("GET", file, true);

  // send the request
  xhr.send();
}

document.getElementById("loadButton").onclick = function () {
  requireXhr("remote.js", function () {
    sayHello("Mohammad");
  });
};

该代码非常简单且具有自我描述性。我们使用​XMLHttpRequestobject​设置一个 HTML 请求,然后触发它。然后,我们监控其状态变化并相应地执行。

当我们执行上面的代码时,我们得到以下结果。我们可以在 DevTools 扩展中看到我们成功的 XHR 请求:

另一方面,结果令人失望。我们能够成功运行脚本,并且 HTML 内容中没有脚本文件的线索,但是,由于​eval()​的私有范围,我们无法调用加载的函数。

这种方式的其他缺点是它在跨域工作时会受到很大影响。而且你也不能从本地路径加载!

简单方法

现在是最直接的延迟加载 JavaScript 文件的方式,jQuery 方式。jQuery 提供了一个名为​getScript()​(它是​ajax()​的简写)的函数来检索和加载 JavaScript 文件。这是代码:

/**
 * Used to load and execute JavaScript file. 
 * @param file JS file name
 * @param callback Subscribe to get notified when script file is loaded
 **/
 function requireAjax(file, callback) {
  jQuery.getScript(file, callback);
}

document.getElementById("loadButton").onclick = function () {
  requireAjax("remote.js", function () {
    sayHello("Mohammad");
  });
};

虽然在内部​jQuery.getScript() ​使用​XMLHttpRequest​,并且您可以在 DevTools 中的 XHR 列表中看到请求,但它不会遭受与​XMLHttpRequest​. 它比 ​raw ​更容易使用和执行​XMLHttpRequest​。

结果也很棒:

结论

总而言之,如果你在页面中启用了 jQuery,你就可以轻松地​​jQuery.getScript(​)​按需异步加载 JavaScript。如果你正在寻找一种纯 JavaScript 方法,请选择这种​<script>​方法,它产生的开销很小。